freebsd.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
freebsd.c (12246B)
---
1 // Code specific to x86 hosts running FreeBSD.
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <ucontext.h>
10 #include <machine/ucontext.h>
11
12 #include <machine/segments.h>
13 #include <machine/sysarch.h>
14
15 #include "vx32.h"
16 #include "vx32impl.h"
17 #include "os.h"
18
19 #if __FreeBSD__ < 7
20 #warning "libvx32 and FreeBSD 5 and 6's libpthread are not compatible."
21 #endif
22
23 #ifdef __i386__
24 static void setbase(struct segment_descriptor *desc, unsigned long base)
25 #elif defined __amd64__
26 static void setbase(struct user_segment_descriptor *desc, unsigned long base)
27 #endif
28 {
29 desc->sd_lobase = base & 0xffffff;
30 desc->sd_hibase = base >> 24;
31 }
32
33 #ifdef __i386__
34 static void setlimit(struct segment_descriptor *desc, unsigned long limit)
35 #elif defined __amd64__
36 static void setlimit(struct user_segment_descriptor *desc, unsigned long limit)
37 #endif
38 {
39 desc->sd_lolimit = limit & 0xffff;
40 desc->sd_hilimit = limit >> 16;
41 }
42
43 /*
44 #ifdef __amd64__
45 union descriptor {
46 struct user_segment_descriptor sd;
47 struct gate_descriptor gd;
48 };
49 #endif
50 */
51
52 int vxemu_map(vxemu *emu, vxmmap *mm)
53 {
54 int s, sel;
55 struct vxproc *vxp;
56 union descriptor desc;
57
58 vxp = emu->proc;
59
60 if (emu->ldt_base != (uintptr_t)mm->base || emu->ldt_size != mm->size) {
61 // Set up the process's data segment selector (for DS,ES,SS).
62 memset(&desc, 0, sizeof(desc));
63 setbase(&desc.sd, (unsigned long)mm->base);
64 setlimit(&desc.sd, (mm->size - 1) >> VXPAGESHIFT);
65 desc.sd.sd_type = SDT_MEMRWA;
66 desc.sd.sd_dpl = 3;
67 desc.sd.sd_p = 1;
68 desc.sd.sd_def32 = 1;
69 desc.sd.sd_gran = 1;
70 if(emu->datasel == 0){
71 #ifdef __i386__
72 if ((s = i386_set_ldt(LDT_AUTO_ALLOC, &desc, 1)) < 0)
73 #elif defined __amd64__
74 if ((s = sysarch(I386_SET_GSBASE, &desc)) < 0)
75 #endif
76 return -1;
77 emu->datasel = (s<<3) + 4 + 3; // 4=LDT, 3=RPL
78 #ifdef __i386__
79 }else if (i386_set_ldt(emu->datasel >> 3, &desc, 1) < 0)
80 #elif defined __amd64__
81 }else if (sysarch(I386_SET_GSBASE, &desc) < 0)
82 #endif
83 return -1;
84
85 // Set up the process's vxemu segment selector (for FS).
86 setbase(&desc.sd, (unsigned long)emu);
87 setlimit(&desc.sd, (VXCODEBUFSIZE - 1) >> VXPAGESHIFT);
88 if(emu->emusel == 0){
89 #ifdef __i386__
90 if ((s = i386_set_ldt(LDT_AUTO_ALLOC, &desc, 1)) < 0)
91 #elif defined __amd64__
92 if ((s = sysarch(I386_SET_GSBASE, &desc)) < 0)
93 #endif
94 return -1;
95 emu->emusel = (s<<3) + 4 + 3; // 4=LDT, 3=RPL
96 #ifdef __i386__
97 }else if (i386_set_ldt(emu->emusel >> 3, &desc, 1) < 0)
98 #elif defined __amd64__
99 }else if (sysarch(I386_SET_GSBASE, &desc) < 0)
100 #endif
101 return -1;
102
103 emu->ldt_base = (uintptr_t)mm->base;
104 emu->ldt_size = mm->size;
105 }
106
107 #ifdef __amd64__
108 /*
109 // Set up 32-bit mode code and data segments (not vxproc-specific),
110 // giving access to the full low 32-bit of linear address space.
111 // The code segment is necessary to get into 32-bit compatibility mode;
112 // the data segment is needed because Linux for x86-64
113 // doesn't give 64-bit processes a "real" data segment by default
114 // but instead just loads zero into the data segment selectors!
115 emu->runptr.sel = FLATCODE;
116 setbase(&desc.sd, 0);
117 setlimit(&desc.sd, 0xfffff);
118 if ((s = sysarch(I386_SET_GSBASE, &desc)) < 0)
119 return -1;
120
121 desc.entry_number = FLATDATA / 8;
122 desc.contents = MODIFY_LDT_CONTENTS_DATA;
123 if (modify_ldt(1, &desc, sizeof(desc)) < 0)
124 return -1;
125
126 // Set up a far return vector in emu->retptr
127 // for getting back into 64-bit long mode.
128 extern void vxrun_return();
129 asm volatile("movw %%cs,%0" : "=r" (emu->retptr.sel));
130 emu->retptr.ofs = (uint32_t)(intptr_t)vxrun_return;
131 */
132 #endif
133
134 return 0;
135 }
136
137 static void dumpmcontext(mcontext_t *ctx, uint32_t cr2)
138 {
139 #ifdef i386
140 vxprint(
141 "eax %08x ebx %08x ecx %08x edx %08x\n"
142 "esi %08x edi %08x ebp %08x esp %08x\n"
143 "eip %08x efl %08x cs %04x\n"
144 "err %08x trapno %08x cr2 %08x\n",
145 ctx->mc_eax, ctx->mc_ebx, ctx->mc_ecx, ctx->mc_edx,
146 ctx->mc_esi, ctx->mc_edi, ctx->mc_ebp, ctx->mc_esp,
147 ctx->mc_eip, ctx->mc_eflags, ctx->mc_cs,
148 ctx->mc_err, ctx->mc_trapno, cr2);
149 #else
150 vxprint(
151 "rax %016lx rbx %016lx\nrcx %016lx rdx %016lx\n"
152 "rsi %016lx rdi %016lx\nrbp %016lx rsp %016lx\n"
153 "r8 %016lx r9 %016lx\nr10 %016lx r11 %016lx\n"
154 "r12 %016lx r13 %016lx\nr14 %016lx r15 %016lx\n"
155 "rip %016lx efl %016lx cs %04x ss %04x\n"
156 "err %016lx trapno %016lx cr2 %016lx\n",
157 ctx->mc_rax, ctx->mc_rbx, ctx->mc_rcx, ctx->mc_rdx,
158 ctx->mc_rsi, ctx->mc_rdi, ctx->mc_rbp, ctx->mc_rsp,
159 ctx->mc_r8, ctx->mc_r9, ctx->mc_r10, ctx->mc_r11,
160 ctx->mc_r12, ctx->mc_r13, ctx->mc_r14, ctx->mc_r15,
161 ctx->mc_rip, ctx->mc_rflags, ctx->mc_cs, ctx->mc_ss,
162 ctx->mc_err, ctx->mc_trapno, cr2);
163 #endif
164 }
165
166 static void
167 fprestore(int *state, int fmt)
168 {
169 #ifdef __i386__
170 if(fmt == _MC_FPFMT_387)
171 asm volatile("frstor 0(%%eax); fwait\n" : : "a" (state) : "memory");
172 else
173 #endif
174 if(fmt == _MC_FPFMT_XMM){
175 /* Have to 16-align the 512-byte state */
176 char buf[512+16], *p;
177 p = buf;
178 if((long)p&15)
179 p += 16 - (long)p&15;
180 memmove(p, state, 512);
181 #ifdef __i386__
182 asm volatile("fxrstor 0(%%eax); fwait\n" : : "a" (p) : "memory");
183 #elif defined(__amd64__)
184 asm volatile("fxrstor 0(%%rax); fwait\n" : : "a" (p) : "memory");
185 #endif
186 }else
187 abort();
188 }
189
190 int vx32_sighandler(int signo, siginfo_t *si, void *v)
191 {
192 int r;
193 uint32_t magic;
194 uint16_t vs, oldvs;
195 vxproc *vxp;
196 vxemu *emu;
197 ucontext_t *uc;
198 mcontext_t *mc;
199
200 uc = v;
201 mc = &uc->uc_mcontext;
202
203 // We can't be sure that vxemu is running,
204 // and thus that %VSEG is actually mapped to a
205 // valid vxemu. The only way to tell is to look at %VSEG.
206
207 // First sanity check vxproc segment number.
208 // FreeBSD reset the register before entering the handler!
209 #ifdef __i386__
210 asm("movw %"VSEGSTR",%0"
211 : "=r" (oldvs));
212 vs = mc->mc_vs & 0xFFFF; /* mc_vs #defined in os.h */
213 #elif defined(__amd64__)
214 if (sysarch(I386_GET_GSBASE, &vs) < 0)
215 return 0;
216 #endif
217
218 #ifdef __i386__
219 if(0) vxprint("vx32_sighandler signo=%d eip=%#x esp=%#x vs=%#x currentvs=%#x\n",
220 signo, mc->mc_eip, mc->mc_esp, vs, oldvs);
221 #elif defined(__amd64__)
222 if(0) vxprint("vx32_sighandler signo=%d rip=%#x rsp=%#x vs=%#x currentvs=%#x\n",
223 signo, mc->mc_rip, mc->mc_rsp, vs, oldvs);
224 #endif
225
226 if ((vs & 7) != 7) // LDT, RPL=3
227 return 0;
228
229 // Okay, assume mapped; check for vxemu by reading
230 // first word from vs. Have to put vs into the segment
231 // register and then take it back out.
232 asm("movw %"VSEGSTR",%1\n"
233 "movw %2,%"VSEGSTR"\n"
234 "movl %"VSEGSTR":%3,%0\n"
235 "movw %1,%"VSEGSTR"\n"
236 : "=r" (magic), "=r" (oldvs)
237 : "r" (vs), "m" (((vxemu*)0)->magic));
238 if (magic != VXEMU_MAGIC)
239 return 0;
240
241 // Okay, we're convinced.
242
243 // Find current vxproc and vxemu.
244 #ifdef __i386__
245 asm("movw %"VSEGSTR",%1\n"
246 "movw %2,%"VSEGSTR"\n"
247 "movl %"VSEGSTR":%3,%0\n"
248 "movw %1,%"VSEGSTR"\n"
249 : "=r" (vxp), "=r" (oldvs)
250 : "r" (vs), "m" (((vxemu*)0)->proc));
251 #elif defined(__amd64__)
252 asm("movw %"VSEGSTR",%1\n"
253 "movw %2,%"VSEGSTR"\n"
254 "movw %"VSEGSTR":%3,%0\n"
255 "movw %1,%"VSEGSTR"\n"
256 : "=r" (vxp), "=r" (oldvs)
257 : "r" (vs), "m" (((vxemu*)0)->proc));
258 #endif
259 emu = vxp->emu;
260
261 // Get back our regular host segment register state,
262 // so that thread-local storage and such works.
263 vxrun_cleanup(emu);
264
265 // dumpmcontext(mc, (uint32_t)si->si_addr);
266
267 uint32_t addr;
268 int newtrap;
269 addr = 0;
270 switch(signo){
271 case SIGSEGV:
272 newtrap = VXTRAP_PAGEFAULT;
273 #ifdef __i386__
274 addr = (uint32_t)si->si_addr;
275 #elif defined(__amd64__)
276 addr = (uint64_t)si->si_addr;
277 #endif
278 break;
279 case SIGBUS:
280 /*
281 * On FreeBSD, SIGBUS means segmentation limit fault.
282 * The supplied address is bogus.
283 */
284 newtrap = VXTRAP_PAGEFAULT;
285 addr = 0;
286 break;
287
288 case SIGFPE:
289 // vxprint("fpe %d\n", si->si_code);
290 newtrap = VXTRAP_FLOAT;
291 addr = 0;
292 break;
293
294 case SIGVTALRM:
295 newtrap = VXTRAP_IRQ + VXIRQ_TIMER;
296 addr = 0;
297 break;
298
299 case SIGTRAP:
300 // FreeBSD sends SIGTRAP when it gets a processor
301 // debug exception, which is caused by single-stepping
302 // with the TF bit, among other things.
303 // It appears that FreeBSD does not turn the flag back on
304 // before entering the signal handler.
305 addr = 0;
306 newtrap = VXTRAP_SINGLESTEP;
307 #ifdef __i386__
308 mc->mc_eflags &= ~EFLAGS_TF; // Just in case.
309 #elif defined(__amd64__)
310 mc->mc_rflags &= ~EFLAGS_TF; // Just in case.
311 #endif
312 break;
313
314 default:
315 newtrap = VXTRAP_SIGNAL + signo;
316 break;
317 }
318
319 int replaced_trap = 0;
320 if (emu->cpu_trap) {
321 // There's already a pending trap!
322 // Handle the new trap, and assume that when it
323 // finishes, restarting the code at cpu.eip will trigger
324 // the old trap again.
325 // Have to fix up eip for int 0x30 and syscall instructions.
326 if (emu->cpu_trap == VXTRAP_SYSCALL ||
327 (emu->cpu_trap&VXTRAP_CATEGORY) == VXTRAP_SOFT)
328 emu->cpu.eip -= 2;
329 replaced_trap = emu->cpu_trap;
330 }
331 emu->cpu_trap = newtrap;
332
333 #ifdef __i386__
334 r = vxemu_sighandler(emu, mc->mc_eip);
335 #elif defined(__amd64__)
336 r = vxemu_sighandler(emu, mc->mc_rip);
337 #endif
338
339 if (r == VXSIG_SINGLESTEP){
340 // Vxemu_sighandler wants us to single step.
341 // Execution state is in intermediate state - don't touch.
342 #ifdef __i386__
343 mc->mc_eflags |= EFLAGS_TF; // x86 TF (single-step) bit
344 #elif defined(__amd64__)
345 mc->mc_rflags |= EFLAGS_TF;
346 #endif
347 vxrun_setup(emu);
348 return 1;
349 }
350
351 // Copy execution state into emu.
352 if ((r & VXSIG_SAVE_ALL) == VXSIG_SAVE_ALL) {
353 #ifdef __i386__
354 emu->cpu.reg[EAX] = mc->mc_eax;
355 emu->cpu.reg[EBX] = mc->mc_ebx;
356 emu->cpu.reg[ECX] = mc->mc_ecx;
357 emu->cpu.reg[EDX] = mc->mc_edx;
358 emu->cpu.reg[ESI] = mc->mc_esi;
359 emu->cpu.reg[EDI] = mc->mc_edi;
360 emu->cpu.reg[ESP] = mc->mc_esp; // or esp_at_signal ???
361 emu->cpu.reg[EBP] = mc->mc_ebp;
362 emu->cpu.eflags = mc->mc_eflags;
363 #elif defined(__amd64__)
364 emu->cpu.reg[EAX] = mc->mc_rax;
365 emu->cpu.reg[EBX] = mc->mc_rbx;
366 emu->cpu.reg[ECX] = mc->mc_rcx;
367 emu->cpu.reg[EDX] = mc->mc_rdx;
368 emu->cpu.reg[ESI] = mc->mc_rsi;
369 emu->cpu.reg[EDI] = mc->mc_rdi;
370 emu->cpu.reg[ESP] = mc->mc_rsp; // or esp_at_signal ???
371 emu->cpu.reg[EBP] = mc->mc_rbp;
372 emu->cpu.eflags = mc->mc_rflags;
373 #endif
374 } else if (r & VXSIG_SAVE_ALL) {
375 if (r & VXSIG_SAVE_EAX)
376 #ifdef __i386__
377 emu->cpu.reg[EAX] = mc->mc_eax;
378 #elif defined(__amd64__)
379 emu->cpu.reg[EAX] = mc->mc_rax;
380 #endif
381 if (r & VXSIG_SAVE_EBX)
382 #ifdef __i386__
383 emu->cpu.reg[EBX] = mc->mc_ebx;
384 #elif defined(__amd64__)
385 emu->cpu.reg[EBX] = mc->mc_rbx;
386 #endif
387 if (r & VXSIG_SAVE_ECX)
388 #ifdef __i386__
389 emu->cpu.reg[ECX] = mc->mc_ecx;
390 #elif defined(__amd64__)
391 emu->cpu.reg[ECX] = mc->mc_rcx;
392 #endif
393 if (r & VXSIG_SAVE_EDX)
394 #ifdef __i386__
395 emu->cpu.reg[EDX] = mc->mc_edx;
396 #elif defined(__amd64__)
397 emu->cpu.reg[EDX] = mc->mc_rdx;
398 #endif
399 if (r & VXSIG_SAVE_ESI)
400 #ifdef __i386__
401 emu->cpu.reg[ESI] = mc->mc_esi;
402 #elif defined(__amd64__)
403 emu->cpu.reg[ESI] = mc->mc_rsi;
404 #endif
405 if (r & VXSIG_SAVE_EDI)
406 #ifdef __i386__
407 emu->cpu.reg[EDI] = mc->mc_edi;
408 #elif defined(__amd64__)
409 emu->cpu.reg[EDI] = mc->mc_rdi;
410 #endif
411 if (r & VXSIG_SAVE_ESP)
412 #ifdef __i386__
413 emu->cpu.reg[ESP] = mc->mc_esp; // or esp_at_signal ???
414 #elif defined(__amd64__)
415 emu->cpu.reg[ESP] = mc->mc_rsp; // or esp_at_signal ???
416 #endif
417 if (r & VXSIG_SAVE_EBP)
418 #ifdef __i386__
419 emu->cpu.reg[EBP] = mc->mc_ebp;
420 #elif defined(__amd64__)
421 emu->cpu.reg[EBP] = mc->mc_rbp;
422 #endif
423 if (r & VXSIG_SAVE_EFLAGS)
424 #ifdef __i386__
425 emu->cpu.eflags = mc->mc_eflags;
426 #elif defined(__amd64__)
427 emu->cpu.eflags = mc->mc_rflags;
428 #endif
429 }
430 r &= ~VXSIG_SAVE_ALL;
431
432 if (r & VXSIG_SAVE_EBX_AS_EIP)
433 #ifdef __i386__
434 emu->cpu.eip = mc->mc_ebx;
435 #elif defined(__amd64__)
436 emu->cpu.eip = mc->mc_rbx;
437 #endif
438 r &= ~VXSIG_SAVE_EBX_AS_EIP;
439
440 if (r & VXSIG_ADD_COUNT_TO_ESP) {
441 emu->cpu.reg[ESP] += (uint16_t)(r >> VXSIG_COUNT_SHIFT);
442 r &= ~VXSIG_ADD_COUNT_TO_ESP;
443 r &= ~(0xFFFF << VXSIG_COUNT_SHIFT);
444 }
445
446 if (r & VXSIG_INC_ECX) {
447 emu->cpu.reg[ECX]++;
448 r &= ~VXSIG_INC_ECX;
449 }
450
451 if (r == VXSIG_TRAP) {
452 if (emu->trapenv == NULL)
453 return 0;
454 emu->cpu.traperr = mc->mc_err;
455 emu->cpu.trapva = addr;
456 #ifdef __i386__
457 memmove(&mc->mc_gs, &emu->trapenv->mc_gs, 19*4);
458 #elif defined(__amd64__)
459 memmove(&mc->mc_onstack, &emu->trapenv->mc_onstack, sizeof(mcontext_t));
460 #endif
461 return 1;
462 }
463
464 // The signal handler is confused; so are we.
465 return 0;
466 }
467