a640c27cf8287684498b31b5bbd5a11f791a6783
[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         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
127
128         /* Get the RIP of the io access. */
129         if (rippa(vm_thread, (uint64_t *)&ip))
130                 return VM_PAGE_FAULT;
131         edx = vm_tf->tf_rdx;
132         ip8 = (void *)ip;
133         ip16 = (void *)ip;
134         //printf("io: ip16 %p\n", *ip16, edx);
135
136         if (*ip8 == 0xef) {
137                 vm_tf->tf_rip += 1;
138                 /* out at %edx */
139                 if (edx == 0xcf8) {
140                         //printf("Set cf8 ");
141                         configaddr(vm_tf->tf_rax);
142                         return 0;
143                 }
144                 if (edx == 0xcfc) {
145                         //printf("Set cfc ");
146                         configwrite32(edx, vm_tf->tf_rax);
147                         return 0;
148                 }
149                 /* While it is perfectly legal to do IO operations to
150                  * nonexistant places, we print a warning here as it
151                  * might also indicate a problem.  In practice these
152                  * types of IOs happens less frequently, and whether
153                  * they are bad or not is not always easy to decide.
154                  * Simple example: for about the first 10 years Linux
155                  * used to outb 0x98 to port 0x80 while idle. We
156                  * wouldn't want to call that an error, but that kind
157                  * of thing is a bad practice we ought to know about,
158                  * because it can cause chipset errors and result in
159                  * other non-obvious failures (in one case, breaking
160                  * BIOS reflash operations).  Plus, true story, it
161                  * confused people into thinking we were running
162                  * Windows 98, not Linux.
163                  */
164                 printf("(out rax, edx): unhandled IO address dx @%p is 0x%x\n", ip8, edx);
165                 return 0;
166         }
167         // out %al, %dx
168         if (*ip8 == 0xee) {
169                 vm_tf->tf_rip += 1;
170                 /* out al %edx */
171                 if (edx == 0xcfb) { // special!
172                         printf("Just ignore the damned cfb write\n");
173                         return 0;
174                 }
175                 if ((edx&~3) == 0xcfc) {
176                         //printf("ignoring write to cfc ");
177                         return 0;
178                 }
179                 /* Another case where we print a message but it's not an error. */
180                 printf("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8, edx);
181                 return 0;
182         }
183         if (*ip8 == 0xec) {
184                 vm_tf->tf_rip += 1;
185                 //printf("configread8 ");
186                 configread8(edx, &vm_tf->tf_rax);
187                 return 0;
188         }
189         if (*ip8 == 0xed) {
190                 vm_tf->tf_rip += 1;
191                 if (edx == 0xcf8) {
192                         //printf("read cf8 0x%lx\n", v->regs.tf_rax);
193                         vm_tf->tf_rax = cf8;
194                         return 0;
195                 }
196                 //printf("configread32 ");
197                 configread32(edx, &vm_tf->tf_rax);
198                 return 0;
199         }
200         /* Detects when something is written to the PIC. */
201         if (*ip8 == 0xe6) {
202                 vm_tf->tf_rip += 2;
203                 return 0;
204         }
205         /* Detects when something is read from the PIC, so
206          * a value signifying there is no PIC is given.
207          */
208         if (*ip16 == 0x21e4) {
209                 vm_tf->tf_rip += 2;
210                 vm_tf->tf_rax |= 0x00000ff;
211                 return 0;
212         }
213         if (*ip16 == 0xed66) {
214                 vm_tf->tf_rip += 2;
215                 //printf("configread16 ");
216                 configread16(edx, &vm_tf->tf_rax);
217                 return 0;
218         }
219
220         /* This is, so far, the only case in which we indicate
221          * failure: we can't even decode the instruction. We've
222          * implemented the common cases above, and recently this
223          * failure has been seen only when the RIP is set to some
224          * bizarre value and we start fetching instructions from
225          * (e.g.) the middle of a page table. PTEs look like IO
226          * instructions to the CPU.
227          */
228         printf("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
229         return -1;
230 }
231