VMM: Add our virtio_mmio_dev struct and specify its interface functions
[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 /* nowhere on my linux system. */
25 #define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
26
27 /* crude PCI bus. Just enough to get virtio working. I would rather not add to this. */
28 struct pciconfig {
29         uint32_t registers[256];
30 };
31
32 /* just index by devfn, i.e. 8 bits */
33 struct pciconfig pcibus[] = {
34         /* linux requires that devfn 0 be a bridge. 
35          * 00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
36          */
37         {
38                 {0x71908086, 0x02000006, 0x06000001},
39         },
40 };
41 /* cf8 is a single-threaded resource. */
42 static uint32_t cf8;
43 static uint32_t allones = (uint32_t)-1;
44
45 /* Return a pointer to the 32-bit "register" in the "pcibus" give an address. Use cf8.
46  * only for readonly access.
47  * this will fail if we ever want to do writes, but we don't.
48  */
49 void regp(uint32_t **reg)
50 {
51         *reg = &allones;
52         int devfn = (cf8>>8) & 0xff;
53         //printf("devfn %d\n", devfn);
54         if (devfn < ARRAY_SIZE(pcibus))
55                 *reg = &pcibus[devfn].registers[(cf8>>2)&0x3f];
56         //printf("-->regp *reg 0x%lx\n", **reg);
57 }
58
59 static uint32_t configaddr(uint32_t val)
60 {
61         printf("%s 0x%lx\n", __func__, val);
62         cf8 = val;
63         return 0;
64 }
65
66 static uint32_t configread32(uint32_t edx, uint64_t *reg)
67 {
68         uint32_t *r = &cf8;
69         regp(&r);
70         *reg = *r;
71         printf("%s: 0x%lx 0x%lx, 0x%lx 0x%lx\n", __func__, cf8, edx, r, *reg);
72         return 0;
73 }
74
75 static uint32_t configread16(uint32_t edx, uint64_t *reg)
76 {
77         uint64_t val;
78         int which = ((edx&2)>>1) * 16;
79         configread32(edx, &val);
80         val >>= which;
81         *reg = val;
82         printf("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
83         return 0;
84 }
85
86 static uint32_t configread8(uint32_t edx, uint64_t *reg)
87 {
88         uint64_t val;
89         int which = (edx&3) * 8;
90         configread32(edx, &val);
91         val >>= which;
92         *reg = val;
93         printf("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
94         return 0;
95 }
96
97 static int configwrite32(uint32_t addr, uint32_t val)
98 {
99         uint32_t *r = &cf8;
100         regp(&r);
101         *r = val;
102         printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
103         return 0;
104 }
105
106 static int configwrite16(uint32_t addr, uint16_t val)
107 {
108         printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
109         return 0;
110 }
111
112 static int configwrite8(uint32_t addr, uint8_t val)
113 {
114         printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
115         return 0;
116 }
117
118 /* this is very minimal. It needs to move to vmm/io.c but we don't
119  * know if this minimal approach will even be workable. It only (for
120  * now) handles pci config space. We'd like to hope that's all we will
121  * need.
122  * It would have been nice had intel encoded the IO exit info as nicely as they
123  * encoded, some of the other exits.
124  */
125 int io(struct guest_thread *vm_thread)
126 {
127
128         /* Get a pointer to the memory at %rip. This is quite messy and part of the
129          * reason we don't want to do this at all. It sucks. Would have been nice
130          * had linux had an option to ONLY do mmio config space access, but no such
131          * luck.
132          */
133         uint8_t *ip8 = NULL;
134         uint16_t *ip16;
135         uintptr_t ip;
136         uint32_t edx;
137         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
138         /* for now, we're going to be a bit crude. In kernel, p is about v, so we just blow away
139          * the upper 34 bits and take the rest + 1M as our address
140          * TODO: put this in vmctl somewhere?
141          */
142         ip = vm_tf->tf_rip & 0x3fffffff;
143         edx = vm_tf->tf_rdx;
144         ip8 = (void *)ip;
145         ip16 = (void *)ip;
146         //printf("io: ip16 %p\n", *ip16, edx);
147
148         if (*ip8 == 0xef) {
149                 vm_tf->tf_rip += 1;
150                 /* out at %edx */
151                 if (edx == 0xcf8) {
152                         //printf("Set cf8 ");
153                         return configaddr(vm_tf->tf_rax);
154                 }
155                 if (edx == 0xcfc) {
156                         //printf("Set cfc ");
157                         return configwrite32(edx, vm_tf->tf_rax);
158                 }
159                 printf("(out rax, edx): unhandled IO address dx @%p is 0x%x\n", ip8, edx);
160                 return -1;
161         }
162         // out %al, %dx
163         if (*ip8 == 0xee) {
164                 vm_tf->tf_rip += 1;
165                 /* out al %edx */
166                 if (edx == 0xcfb) { // special!
167                         printf("Just ignore the damned cfb write\n");
168                         return 0;
169                 }
170                 if ((edx&~3) == 0xcfc) {
171                         //printf("ignoring write to cfc ");
172                         return 0;
173                 }
174                 printf("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8, edx);
175                 return -1;
176         }
177         if (*ip8 == 0xec) {
178                 vm_tf->tf_rip += 1;
179                 //printf("configread8 ");
180                 return configread8(edx, &vm_tf->tf_rax);
181         }
182         if (*ip8 == 0xed) {
183                 vm_tf->tf_rip += 1;
184                 if (edx == 0xcf8) {
185                         //printf("read cf8 0x%lx\n", v->regs.tf_rax);
186                         vm_tf->tf_rax = cf8;
187                         return 0;
188                 }
189                 //printf("configread32 ");
190                 return configread32(edx, &vm_tf->tf_rax);
191         }
192         if (*ip16 == 0xed66) {
193                 vm_tf->tf_rip += 2;
194                 //printf("configread16 ");
195                 return configread16(edx, &vm_tf->tf_rax);
196         }
197         printf("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
198         return -1;
199 }
200