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