vmm: refactor userspace's emsr_fakewrite()
[akaros.git] / user / vmm / io.c
1 #include <parlib/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.
42  * Use cf8.  only for readonly access.  this will fail if we ever want to do
43  * 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
118          * the reason we don't want to do this at all. It sucks. Would have been
119          * nice had linux had an option to ONLY do mmio config space access, but
120          * no such luck.  */
121         uint8_t *ip8 = NULL;
122         uint16_t *ip16;
123         uintptr_t ip;
124         uint32_t edx;
125         uint32_t eax;
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         eax = vm_tf->tf_rax;
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                 printd("(out rax, edx): unhandled IO address dx @%p is 0x%x\n",
166                        ip8, edx);
167                 return 0;
168         }
169         /* TODO: sort out these various OUT operations */
170         // out %al, %dx
171         if (*ip8 == 0xee) {
172                 vm_tf->tf_rip += 1;
173                 /* out al %edx */
174                 if (edx == 0xcfb) { // special!
175                         printd("Just ignore the damned cfb write\n");
176                         return 0;
177                 }
178                 if ((edx&~3) == 0xcfc) {
179                         //printf("ignoring write to cfc ");
180                         return 0;
181                 }
182                 if (edx == 0xcf9) {
183                         // on real hardware, an outb to 0xcf9 with bit 2 set is
184                         // about as hard a reset as you can get. It yanks the
185                         // reset on everything, including all the cores.  It
186                         // usually happens after the kernel has done lots of
187                         // work to carefully quiesce the machine but, once it
188                         // happens, game is over. Hence, an exit(0) is most
189                         // appropriate, since it's not an error.
190                         if (eax & (1 << 2)) {
191                                 printf("outb to PCI reset port with bit 2 set: time to die\n");
192                                 exit(0);
193                         }
194                         return 0;
195                 }
196
197                 /* Another case where we print a message but it's not an error.
198                  * */
199                 printd("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8,
200                        edx); return 0;
201         }
202         /* Silently accept OUT imm8, al */
203         if (*ip8 == 0xe6) {
204                 vm_tf->tf_rip += 2;
205                 return 0;
206         }
207         /* Silently accept OUT dx, ax with opcode size modifier */
208         if (*ip16 ==  0xef66) {
209                 vm_tf->tf_rip += 2;
210                 return 0;
211         }
212         if (*ip8 == 0xec) {
213                 vm_tf->tf_rip += 1;
214                 //printf("configread8 ");
215                 configread8(edx, &vm_tf->tf_rax);
216                 return 0;
217         }
218         if (*ip8 == 0xed) {
219                 vm_tf->tf_rip += 1;
220                 if (edx == 0xcf8) {
221                         //printf("read cf8 0x%lx\n", v->regs.tf_rax);
222                         vm_tf->tf_rax = cf8;
223                         return 0;
224                 }
225                 //printf("configread32 ");
226                 configread32(edx, &vm_tf->tf_rax);
227                 return 0;
228         }
229         /* Detects when something is read from the PIC, so
230          * a value signifying there is no PIC is given.
231          */
232         if (*ip16 == 0x21e4) {
233                 vm_tf->tf_rip += 2;
234                 vm_tf->tf_rax |= 0x00000ff;
235                 return 0;
236         }
237         if (*ip16 == 0xed66) {
238                 vm_tf->tf_rip += 2;
239                 //printf("configread16 ");
240                 configread16(edx, &vm_tf->tf_rax);
241                 return 0;
242         }
243
244         /* This is, so far, the only case in which we indicate
245          * failure: we can't even decode the instruction. We've
246          * implemented the common cases above, and recently this
247          * failure has been seen only when the RIP is set to some
248          * bizarre value and we start fetching instructions from
249          * (e.g.) the middle of a page table. PTEs look like IO
250          * instructions to the CPU.
251          */
252         printf("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
253         return -1;
254 }
255