Get rid of ersatz ARRAY_SIZE macros in code we control.
[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 uint32_t configaddr(uint32_t val)
57 {
58         printf("%s 0x%lx\n", __func__, val);
59         cf8 = val;
60         return 0;
61 }
62
63 static uint32_t configread32(uint32_t edx, uint64_t *reg)
64 {
65         uint32_t *r = &cf8;
66         regp(&r);
67         *reg = *r;
68         printf("%s: 0x%lx 0x%lx, 0x%lx 0x%lx\n", __func__, cf8, edx, r, *reg);
69         return 0;
70 }
71
72 static uint32_t configread16(uint32_t edx, uint64_t *reg)
73 {
74         uint64_t val;
75         int which = ((edx&2)>>1) * 16;
76         configread32(edx, &val);
77         val >>= which;
78         *reg = val;
79         printf("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
80         return 0;
81 }
82
83 static uint32_t configread8(uint32_t edx, uint64_t *reg)
84 {
85         uint64_t val;
86         int which = (edx&3) * 8;
87         configread32(edx, &val);
88         val >>= which;
89         *reg = val;
90         printf("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
91         return 0;
92 }
93
94 static int configwrite32(uint32_t addr, uint32_t val)
95 {
96         uint32_t *r = &cf8;
97         regp(&r);
98         *r = val;
99         printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
100         return 0;
101 }
102
103 static int configwrite16(uint32_t addr, uint16_t val)
104 {
105         printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
106         return 0;
107 }
108
109 static int configwrite8(uint32_t addr, uint8_t val)
110 {
111         printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
112         return 0;
113 }
114
115 /* this is very minimal. It needs to move to vmm/io.c but we don't
116  * know if this minimal approach will even be workable. It only (for
117  * now) handles pci config space. We'd like to hope that's all we will
118  * need.
119  * It would have been nice had intel encoded the IO exit info as nicely as they
120  * encoded, some of the other exits.
121  */
122 int io(struct guest_thread *vm_thread)
123 {
124
125         /* Get a pointer to the memory at %rip. This is quite messy and part of the
126          * reason we don't want to do this at all. It sucks. Would have been nice
127          * had linux had an option to ONLY do mmio config space access, but no such
128          * luck.
129          */
130         uint8_t *ip8 = NULL;
131         uint16_t *ip16;
132         uintptr_t ip;
133         uint32_t edx;
134         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
135         /* for now, we're going to be a bit crude. In kernel, p is about v, so we just blow away
136          * the upper 34 bits and take the rest + 1M as our address
137          * TODO: put this in vmctl somewhere?
138          */
139         ip = vm_tf->tf_rip & 0x3fffffff;
140         edx = vm_tf->tf_rdx;
141         ip8 = (void *)ip;
142         ip16 = (void *)ip;
143         //printf("io: ip16 %p\n", *ip16, edx);
144
145         if (*ip8 == 0xef) {
146                 vm_tf->tf_rip += 1;
147                 /* out at %edx */
148                 if (edx == 0xcf8) {
149                         //printf("Set cf8 ");
150                         return configaddr(vm_tf->tf_rax);
151                 }
152                 if (edx == 0xcfc) {
153                         //printf("Set cfc ");
154                         return configwrite32(edx, vm_tf->tf_rax);
155                 }
156                 printf("(out rax, edx): unhandled IO address dx @%p is 0x%x\n", ip8, edx);
157                 return -1;
158         }
159         // out %al, %dx
160         if (*ip8 == 0xee) {
161                 vm_tf->tf_rip += 1;
162                 /* out al %edx */
163                 if (edx == 0xcfb) { // special!
164                         printf("Just ignore the damned cfb write\n");
165                         return 0;
166                 }
167                 if ((edx&~3) == 0xcfc) {
168                         //printf("ignoring write to cfc ");
169                         return 0;
170                 }
171                 printf("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8, edx);
172                 return -1;
173         }
174         if (*ip8 == 0xec) {
175                 vm_tf->tf_rip += 1;
176                 //printf("configread8 ");
177                 return configread8(edx, &vm_tf->tf_rax);
178         }
179         if (*ip8 == 0xed) {
180                 vm_tf->tf_rip += 1;
181                 if (edx == 0xcf8) {
182                         //printf("read cf8 0x%lx\n", v->regs.tf_rax);
183                         vm_tf->tf_rax = cf8;
184                         return 0;
185                 }
186                 //printf("configread32 ");
187                 return configread32(edx, &vm_tf->tf_rax);
188         }
189         if (*ip16 == 0xed66) {
190                 vm_tf->tf_rip += 2;
191                 //printf("configread16 ");
192                 return configread16(edx, &vm_tf->tf_rax);
193         }
194         printf("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
195         return -1;
196 }
197