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