net: Make select() not spurious
[akaros.git] / user / vmm / io.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <parlib/arch/arch.h>
6 #include <parlib/ros_debug.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <dirent.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ros/syscall.h>
13 #include <sys/mman.h>
14 #include <vmm/coreboot_tables.h>
15 #include <ros/common.h>
16 #include <vmm/vmm.h>
17 #include <vmm/virtio.h>
18 #include <vmm/virtio_mmio.h>
19 #include <vmm/virtio_ids.h>
20 #include <vmm/sched.h>
21 #include <ros/arch/trapframe.h>
22
23 /* crude PCI bus. Just enough to get virtio working. I would rather not add to this. */
24 struct pciconfig {
25         uint32_t registers[256];
26 };
27
28 /* just index by devfn, i.e. 8 bits */
29 struct pciconfig pcibus[] = {
30         /* linux requires that devfn 0 be a bridge.
31          * 00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
32          */
33         {
34                 {0x71908086, 0x02000006, 0x06000001},
35         },
36 };
37 /* cf8 is a single-threaded resource. */
38 static uint32_t cf8;
39 static uint32_t allones = (uint32_t)-1;
40
41 /* Return a pointer to the 32-bit "register" in the "pcibus" give an address. Use cf8.
42  * only for readonly access.
43  * this will fail if we ever want to do writes, but we don't.
44  */
45 void regp(uint32_t **reg)
46 {
47         *reg = &allones;
48         int devfn = (cf8>>8) & 0xff;
49         //printf("devfn %d\n", devfn);
50         if (devfn < COUNT_OF(pcibus))
51                 *reg = &pcibus[devfn].registers[(cf8>>2)&0x3f];
52         //printf("-->regp *reg 0x%lx\n", **reg);
53 }
54
55 static void configaddr(uint32_t val)
56 {
57         printd("%s 0x%lx\n", __func__, val);
58         cf8 = val;
59 }
60
61 static void configread32(uint32_t edx, uint64_t *reg)
62 {
63         uint32_t *r = &cf8;
64         regp(&r);
65         *reg = *r;
66         printd("%s: 0x%lx 0x%lx, 0x%lx 0x%lx\n", __func__, cf8, edx, r, *reg);
67 }
68
69 static void configread16(uint32_t edx, uint64_t *reg)
70 {
71         uint64_t val;
72         int which = ((edx&2)>>1) * 16;
73         configread32(edx, &val);
74         val >>= which;
75         *reg = val;
76         printd("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
77 }
78
79 static void configread8(uint32_t edx, uint64_t *reg)
80 {
81         uint64_t val;
82         int which = (edx&3) * 8;
83         configread32(edx, &val);
84         val >>= which;
85         *reg = val;
86         printd("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
87 }
88
89 static void configwrite32(uint32_t addr, uint32_t val)
90 {
91         uint32_t *r = &cf8;
92         regp(&r);
93         *r = val;
94         printd("%s 0x%lx 0x%lx\n", __func__, addr, val);
95 }
96
97 static void configwrite16(uint32_t addr, uint16_t val)
98 {
99         printd("%s 0x%lx 0x%lx\n", __func__, addr, val);
100 }
101
102 static void configwrite8(uint32_t addr, uint8_t val)
103 {
104         printd("%s 0x%lx 0x%lx\n", __func__, addr, val);
105 }
106
107 /* this is very minimal. It needs to move to vmm/io.c but we don't
108  * know if this minimal approach will even be workable. It only (for
109  * now) handles pci config space. We'd like to hope that's all we will
110  * need.
111  * It would have been nice had intel encoded the IO exit info as nicely as they
112  * encoded, some of the other exits.
113  */
114 int io(struct guest_thread *vm_thread)
115 {
116
117         /* Get a pointer to the memory at %rip. This is quite messy and part of the
118          * reason we don't want to do this at all. It sucks. Would have been nice
119          * had linux had an option to ONLY do mmio config space access, but no such
120          * luck.
121          */
122         uint8_t *ip8 = NULL;
123         uint16_t *ip16;
124         uintptr_t ip;
125         uint32_t edx;
126         uint32_t eax;
127         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
128
129         /* Get the RIP of the io access. */
130         if (rippa(vm_thread, (uint64_t *)&ip))
131                 return VM_PAGE_FAULT;
132         edx = vm_tf->tf_rdx;
133         eax = vm_tf->tf_rax;
134         ip8 = (void *)ip;
135         ip16 = (void *)ip;
136         //printf("io: ip16 %p\n", *ip16, edx);
137
138         if (*ip8 == 0xef) {
139                 vm_tf->tf_rip += 1;
140                 /* out at %edx */
141                 if (edx == 0xcf8) {
142                         //printf("Set cf8 ");
143                         configaddr(vm_tf->tf_rax);
144                         return 0;
145                 }
146                 if (edx == 0xcfc) {
147                         //printf("Set cfc ");
148                         configwrite32(edx, vm_tf->tf_rax);
149                         return 0;
150                 }
151                 /* While it is perfectly legal to do IO operations to
152                  * nonexistant places, we print a warning here as it
153                  * might also indicate a problem.  In practice these
154                  * types of IOs happens less frequently, and whether
155                  * they are bad or not is not always easy to decide.
156                  * Simple example: for about the first 10 years Linux
157                  * used to outb 0x98 to port 0x80 while idle. We
158                  * wouldn't want to call that an error, but that kind
159                  * of thing is a bad practice we ought to know about,
160                  * because it can cause chipset errors and result in
161                  * other non-obvious failures (in one case, breaking
162                  * BIOS reflash operations).  Plus, true story, it
163                  * confused people into thinking we were running
164                  * Windows 98, not Linux.
165                  */
166                 printf("(out rax, edx): unhandled IO address dx @%p is 0x%x\n", ip8, edx);
167                 return 0;
168         }
169         /* TODO: sort out these various OUT operations */
170         // out %al, %dx
171         if (*ip8 == 0xee) {
172                 vm_tf->tf_rip += 1;
173                 /* out al %edx */
174                 if (edx == 0xcfb) { // special!
175                         printf("Just ignore the damned cfb write\n");
176                         return 0;
177                 }
178                 if ((edx&~3) == 0xcfc) {
179                         //printf("ignoring write to cfc ");
180                         return 0;
181                 }
182                 if (edx == 0xcf9) {
183                         // on real hardware, an outb to 0xcf9 with bit 2 set is
184                         // about as hard a reset as you can get. It yanks the
185                         // reset on everything, including all the cores.  It
186                         // usually happens after the kernel has done lots of work
187                         // to carefully quiesce the machine but, once it happens,
188                         // game is over. Hence, an exit(0) is most appropriate,
189                         // since it's not an error.
190                         if (eax & (1 << 2)) {
191                                 printf("outb to PCI reset port with bit 2 set: time to die\n");
192                                 exit(0);
193                         }
194                         return 0;
195                 }
196
197                 /* Another case where we print a message but it's not an error. */
198                 printf("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8, edx);
199                 return 0;
200         }
201         /* Silently accept OUT imm8, al */
202         if (*ip8 == 0xe6) {
203                 vm_tf->tf_rip += 2;
204                 return 0;
205         }
206         /* Silently accept OUT dx, ax with opcode size modifier */
207         if (*ip16 ==  0xef66) {
208                 vm_tf->tf_rip += 2;
209                 return 0;
210         }
211         if (*ip8 == 0xec) {
212                 vm_tf->tf_rip += 1;
213                 //printf("configread8 ");
214                 configread8(edx, &vm_tf->tf_rax);
215                 return 0;
216         }
217         if (*ip8 == 0xed) {
218                 vm_tf->tf_rip += 1;
219                 if (edx == 0xcf8) {
220                         //printf("read cf8 0x%lx\n", v->regs.tf_rax);
221                         vm_tf->tf_rax = cf8;
222                         return 0;
223                 }
224                 //printf("configread32 ");
225                 configread32(edx, &vm_tf->tf_rax);
226                 return 0;
227         }
228         /* Detects when something is read from the PIC, so
229          * a value signifying there is no PIC is given.
230          */
231         if (*ip16 == 0x21e4) {
232                 vm_tf->tf_rip += 2;
233                 vm_tf->tf_rax |= 0x00000ff;
234                 return 0;
235         }
236         if (*ip16 == 0xed66) {
237                 vm_tf->tf_rip += 2;
238                 //printf("configread16 ");
239                 configread16(edx, &vm_tf->tf_rax);
240                 return 0;
241         }
242
243         /* This is, so far, the only case in which we indicate
244          * failure: we can't even decode the instruction. We've
245          * implemented the common cases above, and recently this
246          * failure has been seen only when the RIP is set to some
247          * bizarre value and we start fetching instructions from
248          * (e.g.) the middle of a page table. PTEs look like IO
249          * instructions to the CPU.
250          */
251         printf("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
252         return -1;
253 }
254