abortive try at setting up tables. Failed miserably.
[akaros.git] / user / vmm / decode.c
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * This file is part of Akaros.
5  *
6  * Akarosn is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2 of the License.
9  * 
10  * Akaros is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * Lesser GNU General Public License for more details.
14  * 
15  * See COPYING.LESSER for details on the GNU Lesser General Public License.
16  * See COPYING for details on the GNU General Public License.
17  */
18
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <pthread.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <parlib/arch/arch.h>
25 #include <parlib/ros_debug.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/uio.h>
31 #include <stdint.h>
32 #include <err.h>
33 #include <sys/mman.h>
34 #include <vmm/vmm.h>
35 #include <vmm/virtio.h>
36 #include <vmm/virtio_mmio.h>
37 #include <vmm/virtio_ids.h>
38 #include <vmm/virtio_config.h>
39
40 int debug_decode = 0;
41 #define DPRINTF(fmt, ...) \
42         if (debug_decode) { printf("decode: " fmt , ## __VA_ARGS__); }
43
44 static char *modrmreg[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"};
45
46 // Since we at most have to decode less than half of each instruction, I'm trying to be dumb here.
47 // Fortunately, for me, what's not hard.
48
49 // Target size -- 1, 2, 4, or 8 bytes. We have yet to see 64 bytes. 
50 // TODO: if we ever see it, test the prefix. Since this only supports the low 1M,
51 // that's not likely.
52 static int target(void *insn, int *store) 
53 {
54         *store = 0;
55         int s = -1;
56         uint8_t *byte = insn;
57         uint16_t *word = insn;
58
59         if (*byte == 0x66) {
60                 s = target(insn+1,store);
61                 // flip the sense of s.
62                 s = s == 4 ? 2 : 4;
63                 return s;
64         }
65         switch(*byte) {
66         case 0x3a:
67         case 0x8a:
68         case 0x88:
69                 s = 1;
70                 break;
71         case 0x89:
72         case 0x8b:
73                 s = 2;
74                 break;
75         case 0x81:
76                 s = 4;  
77                 break;
78         case 0x0f:
79         switch(*word) {
80                 case 0xb70f:
81                         s = 4;
82                         break;
83                 default:
84                         fprintf(stderr, "can't get size of %02x/%04x @ %p\n", *byte, *word, byte);
85                         return -1;
86                         break;
87                 }
88                 break;
89         default:
90                 fprintf(stderr, "can't get size of %02x @ %p\n", *byte, byte);
91                 return -1;
92                 break;
93         }
94
95         switch(*byte) {
96         case 0x3a:
97         case 0x8a:
98         case 0x88:
99         case 0x89:
100         case 0x8b:
101         case 0x81:
102                 *store = !(*byte & 2);
103         }
104         return s;
105 }
106
107 char *regname(uint8_t reg)
108 {
109         return modrmreg[reg];
110 }
111
112 static int insize(void *rip)
113 {
114         uint8_t *kva = rip;
115         int advance = 3;
116         /* the dreaded mod/rm byte. */
117         int mod = kva[1]>>6;
118         int rm = kva[1] & 7;
119
120         switch(kva[0]) {
121         default: 
122                 fprintf(stderr, "BUG! %s got 0x%x\n", __func__, kva[0]);
123         case 0x0f: 
124                 break;
125         case 0x81:
126                 advance = 6;
127                 break;
128         case 0x3a:
129         case 0x8a:
130         case 0x88:
131         case 0x89:
132         case 0x8b:
133                 switch (mod) {
134                 case 0: 
135                         advance = 2 + (rm == 4);
136                         break;
137                 case 1:
138                         advance = 3 + (rm == 4);
139                         break;
140                 case 2: 
141                         advance = 6 + (rm == 4);
142                         break;
143                 case 3:
144                         advance = 2;
145                         break;
146                 }
147                 break;
148         }
149         return advance;
150 }
151
152 // This is a very limited function. It's only here to manage virtio-mmio and low memory
153 // pointer loads. I am hoping it won't grow with time. The intent is that we enter it with
154 // and EPT fault from a region that is deliberately left unbacked by any memory. We return
155 // enough info to let you emulate the operation if you want. Because we have the failing physical
156 // address (gpa) the decode is far simpler because we only need to find the register, how many bytes
157 // to move, and how big the instruction is. I thought about bringing in emulate.c from kvm from xen,
158 // but it has way more stuff than we need.
159 // gpa is a pointer to the gpa. 
160 // int is the reg index which we can use for printing info.
161 // regp points to the register in hw_trapframe from which
162 // to load or store a result.
163 int decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp, int *store, int *size, int *advance)
164 {
165
166         DPRINTF("v is %p\n", v);
167
168         // Duh, which way did he go George? Which way did he go? 
169         // First hit on Google gets you there!
170         // This is the guest physical address of the access.
171         // This is nice, because if we ever go with more complete
172         // instruction decode, knowing this gpa reduces our work:
173         // we don't have to find the source address in registers,
174         // only the register holding or receiving the value.
175         *gpa = v->gpa;
176         DPRINTF("gpa is %p\n", *gpa);
177
178         // To find out what to do, we have to look at
179         // RIP. Technically, we should read RIP, walk the page tables
180         // to find the PA, and read that. But we're in the kernel, so
181         // we take a shortcut for now: read the low 30 bits and use
182         // that as the kernel PA, or our VA, and see what's
183         // there. Hokey. Works.
184         uint8_t *kva = (void *)(v->regs.tf_rip & 0x3fffffff);
185         DPRINTF("kva is %p\n", kva);
186
187         // fail fast. If we can't get the size we're done.
188         *size = target(kva, store);
189         if (*size < 0)
190                 return -1;
191
192         uint16_t ins = *(uint16_t *)kva;
193         DPRINTF("ins is %04x\n", ins);
194         
195         *advance = insize(kva);
196
197         *destreg = (ins>>11) & 7;
198         // Our primitive approach wins big here.
199         // We don't have to decode the register or the offset used
200         // in the computation; that was done by the CPU and is the gpa.
201         // All we need to know is which destination or source register it is.
202         switch (*destreg) {
203         case 0:
204                 *regp = &v->regs.tf_rax;
205                 break;
206         case 1:
207                 *regp = &v->regs.tf_rcx;
208                 break;
209         case 2:
210                 *regp = &v->regs.tf_rdx;
211                 break;
212         case 3:
213                 *regp = &v->regs.tf_rbx;
214                 break;
215         case 4:
216                 *regp = &v->regs.tf_rsp; // uh, right.
217                 break;
218         case 5:
219                 *regp = &v->regs.tf_rbp;
220                 break;
221         case 6:
222                 *regp = &v->regs.tf_rsi;
223                 break;
224         case 7:
225                 *regp = &v->regs.tf_rdi;
226                 break;
227         }
228         return 0;
229 }
230
231 #if 0
232 // stupid emulator since what we need is so limited.
233 int emu(struct vmctl *v, uint64_t gpa, uint8_t destreg, uint64_t *regp, int store, int size, int advance)
234 {
235         uint8_t *kva = f->regs.tf_rip;
236
237         if (
238         switch(kva[0]) {
239
240                                 val = *(uint64_t*) (lowmem + gpa); 
241                                 printf("val %p ", val);
242                                 memcpy(regp, &val, size);
243
244 }
245 #endif