Revision f4beb510
b/linux-user/main.c | ||
---|---|---|
100 | 100 |
return 0; |
101 | 101 |
} |
102 | 102 |
|
103 |
void write_dt(void *ptr, unsigned long addr, unsigned long limit, |
|
104 |
int seg32_bit)
|
|
103 |
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
|
104 |
int flags)
|
|
105 | 105 |
{ |
106 |
unsigned int e1, e2, limit_in_pages; |
|
107 |
limit_in_pages = 0; |
|
108 |
if (limit > 0xffff) { |
|
109 |
limit = limit >> 12; |
|
110 |
limit_in_pages = 1; |
|
111 |
} |
|
106 |
unsigned int e1, e2; |
|
112 | 107 |
e1 = (addr << 16) | (limit & 0xffff); |
113 | 108 |
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); |
114 |
e2 |= limit_in_pages << 23; /* byte granularity */ |
|
115 |
e2 |= seg32_bit << 22; /* 32 bit segment */ |
|
109 |
e2 |= flags; |
|
110 |
stl((uint8_t *)ptr, e1); |
|
111 |
stl((uint8_t *)ptr + 4, e2); |
|
112 |
} |
|
113 |
|
|
114 |
static void set_gate(void *ptr, unsigned int type, unsigned int dpl, |
|
115 |
unsigned long addr, unsigned int sel) |
|
116 |
{ |
|
117 |
unsigned int e1, e2; |
|
118 |
e1 = (addr & 0xffff) | (sel << 16); |
|
119 |
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); |
|
116 | 120 |
stl((uint8_t *)ptr, e1); |
117 | 121 |
stl((uint8_t *)ptr + 4, e2); |
118 | 122 |
} |
119 | 123 |
|
120 | 124 |
uint64_t gdt_table[6]; |
125 |
uint64_t idt_table[256]; |
|
126 |
|
|
127 |
/* only dpl matters as we do only user space emulation */ |
|
128 |
static void set_idt(int n, unsigned int dpl) |
|
129 |
{ |
|
130 |
set_gate(idt_table + n, 0, dpl, 0, 0); |
|
131 |
} |
|
121 | 132 |
|
122 | 133 |
void cpu_loop(CPUX86State *env) |
123 | 134 |
{ |
... | ... | |
128 | 139 |
for(;;) { |
129 | 140 |
trapnr = cpu_x86_exec(env); |
130 | 141 |
switch(trapnr) { |
142 |
case 0x80: |
|
143 |
/* linux syscall */ |
|
144 |
env->regs[R_EAX] = do_syscall(env, |
|
145 |
env->regs[R_EAX], |
|
146 |
env->regs[R_EBX], |
|
147 |
env->regs[R_ECX], |
|
148 |
env->regs[R_EDX], |
|
149 |
env->regs[R_ESI], |
|
150 |
env->regs[R_EDI], |
|
151 |
env->regs[R_EBP]); |
|
152 |
break; |
|
153 |
case EXCP0B_NOSEG: |
|
154 |
case EXCP0C_STACK: |
|
155 |
info.si_signo = SIGBUS; |
|
156 |
info.si_errno = 0; |
|
157 |
info.si_code = TARGET_SI_KERNEL; |
|
158 |
info._sifields._sigfault._addr = 0; |
|
159 |
queue_signal(info.si_signo, &info); |
|
160 |
break; |
|
131 | 161 |
case EXCP0D_GPF: |
132 | 162 |
if (env->eflags & VM_MASK) { |
133 | 163 |
handle_vm86_fault(env); |
134 | 164 |
} else { |
135 |
pc = env->seg_cache[R_CS].base + env->eip; |
|
136 |
if (pc[0] == 0xcd && pc[1] == 0x80) { |
|
137 |
/* syscall */ |
|
138 |
env->eip += 2; |
|
139 |
env->regs[R_EAX] = do_syscall(env, |
|
140 |
env->regs[R_EAX], |
|
141 |
env->regs[R_EBX], |
|
142 |
env->regs[R_ECX], |
|
143 |
env->regs[R_EDX], |
|
144 |
env->regs[R_ESI], |
|
145 |
env->regs[R_EDI], |
|
146 |
env->regs[R_EBP]); |
|
147 |
} else { |
|
148 |
/* XXX: more precise info */ |
|
149 |
info.si_signo = SIGSEGV; |
|
150 |
info.si_errno = 0; |
|
151 |
info.si_code = TARGET_SI_KERNEL; |
|
152 |
info._sifields._sigfault._addr = 0; |
|
153 |
queue_signal(info.si_signo, &info); |
|
154 |
} |
|
165 |
info.si_signo = SIGSEGV; |
|
166 |
info.si_errno = 0; |
|
167 |
info.si_code = TARGET_SI_KERNEL; |
|
168 |
info._sifields._sigfault._addr = 0; |
|
169 |
queue_signal(info.si_signo, &info); |
|
155 | 170 |
} |
156 | 171 |
break; |
157 | 172 |
case EXCP0E_PAGE: |
... | ... | |
365 | 380 |
env->regs[R_ESP] = regs->esp; |
366 | 381 |
env->eip = regs->eip; |
367 | 382 |
|
383 |
/* linux interrupt setup */ |
|
384 |
env->idt.base = (void *)idt_table; |
|
385 |
env->idt.limit = sizeof(idt_table) - 1; |
|
386 |
set_idt(0, 0); |
|
387 |
set_idt(1, 0); |
|
388 |
set_idt(2, 0); |
|
389 |
set_idt(3, 3); |
|
390 |
set_idt(4, 3); |
|
391 |
set_idt(5, 3); |
|
392 |
set_idt(6, 0); |
|
393 |
set_idt(7, 0); |
|
394 |
set_idt(8, 0); |
|
395 |
set_idt(9, 0); |
|
396 |
set_idt(10, 0); |
|
397 |
set_idt(11, 0); |
|
398 |
set_idt(12, 0); |
|
399 |
set_idt(13, 0); |
|
400 |
set_idt(14, 0); |
|
401 |
set_idt(15, 0); |
|
402 |
set_idt(16, 0); |
|
403 |
set_idt(17, 0); |
|
404 |
set_idt(18, 0); |
|
405 |
set_idt(19, 0); |
|
406 |
set_idt(0x80, 3); |
|
407 |
|
|
368 | 408 |
/* linux segment setup */ |
369 | 409 |
env->gdt.base = (void *)gdt_table; |
370 | 410 |
env->gdt.limit = sizeof(gdt_table) - 1; |
371 |
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xffffffff, 1); |
|
372 |
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xffffffff, 1); |
|
411 |
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, |
|
412 |
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
|
413 |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); |
|
414 |
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, |
|
415 |
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
|
416 |
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); |
|
373 | 417 |
cpu_x86_load_seg(env, R_CS, __USER_CS); |
374 | 418 |
cpu_x86_load_seg(env, R_DS, __USER_DS); |
375 | 419 |
cpu_x86_load_seg(env, R_ES, __USER_DS); |
b/op-i386.c | ||
---|---|---|
364 | 364 |
|
365 | 365 |
num = (EAX & 0xffff); |
366 | 366 |
den = (T0 & 0xff); |
367 |
if (den == 0) |
|
367 |
if (den == 0) { |
|
368 |
EIP = PARAM1; |
|
368 | 369 |
raise_exception(EXCP00_DIVZ); |
370 |
} |
|
369 | 371 |
q = (num / den) & 0xff; |
370 | 372 |
r = (num % den) & 0xff; |
371 | 373 |
EAX = (EAX & 0xffff0000) | (r << 8) | q; |
... | ... | |
377 | 379 |
|
378 | 380 |
num = (int16_t)EAX; |
379 | 381 |
den = (int8_t)T0; |
380 |
if (den == 0) |
|
382 |
if (den == 0) { |
|
383 |
EIP = PARAM1; |
|
381 | 384 |
raise_exception(EXCP00_DIVZ); |
385 |
} |
|
382 | 386 |
q = (num / den) & 0xff; |
383 | 387 |
r = (num % den) & 0xff; |
384 | 388 |
EAX = (EAX & 0xffff0000) | (r << 8) | q; |
... | ... | |
390 | 394 |
|
391 | 395 |
num = (EAX & 0xffff) | ((EDX & 0xffff) << 16); |
392 | 396 |
den = (T0 & 0xffff); |
393 |
if (den == 0) |
|
397 |
if (den == 0) { |
|
398 |
EIP = PARAM1; |
|
394 | 399 |
raise_exception(EXCP00_DIVZ); |
400 |
} |
|
395 | 401 |
q = (num / den) & 0xffff; |
396 | 402 |
r = (num % den) & 0xffff; |
397 | 403 |
EAX = (EAX & 0xffff0000) | q; |
... | ... | |
404 | 410 |
|
405 | 411 |
num = (EAX & 0xffff) | ((EDX & 0xffff) << 16); |
406 | 412 |
den = (int16_t)T0; |
407 |
if (den == 0) |
|
413 |
if (den == 0) { |
|
414 |
EIP = PARAM1; |
|
408 | 415 |
raise_exception(EXCP00_DIVZ); |
416 |
} |
|
409 | 417 |
q = (num / den) & 0xffff; |
410 | 418 |
r = (num % den) & 0xffff; |
411 | 419 |
EAX = (EAX & 0xffff0000) | q; |
... | ... | |
435 | 443 |
|
436 | 444 |
num = EAX | ((uint64_t)EDX << 32); |
437 | 445 |
den = T0; |
438 |
if (den == 0) |
|
446 |
if (den == 0) { |
|
447 |
EIP = PARAM1; |
|
439 | 448 |
raise_exception(EXCP00_DIVZ); |
449 |
} |
|
440 | 450 |
#ifdef BUGGY_GCC_DIV64 |
441 | 451 |
r = div64(&q, num, den); |
442 | 452 |
#else |
... | ... | |
454 | 464 |
|
455 | 465 |
num = EAX | ((uint64_t)EDX << 32); |
456 | 466 |
den = T0; |
457 |
if (den == 0) |
|
467 |
if (den == 0) { |
|
468 |
EIP = PARAM1; |
|
458 | 469 |
raise_exception(EXCP00_DIVZ); |
470 |
} |
|
459 | 471 |
#ifdef BUGGY_GCC_DIV64 |
460 | 472 |
r = idiv64(&q, num, den); |
461 | 473 |
#else |
... | ... | |
614 | 626 |
EIP = PARAM1; |
615 | 627 |
} |
616 | 628 |
|
617 |
void OPPROTO op_int_im(void) |
|
629 |
#if 0 |
|
630 |
/* full interrupt support (only useful for real CPU emulation, not |
|
631 |
finished) - I won't do it any time soon, finish it if you want ! */ |
|
632 |
void raise_interrupt(int intno, int is_int, int error_code, |
|
633 |
unsigned int next_eip) |
|
634 |
{ |
|
635 |
SegmentDescriptorTable *dt; |
|
636 |
uint8_t *ptr; |
|
637 |
int type, dpl, cpl; |
|
638 |
uint32_t e1, e2; |
|
639 |
|
|
640 |
dt = &env->idt; |
|
641 |
if (intno * 8 + 7 > dt->limit) |
|
642 |
raise_exception_err(EXCP0D_GPF, intno * 8 + 2); |
|
643 |
ptr = dt->base + intno * 8; |
|
644 |
e1 = ldl(ptr); |
|
645 |
e2 = ldl(ptr + 4); |
|
646 |
/* check gate type */ |
|
647 |
type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; |
|
648 |
switch(type) { |
|
649 |
case 5: /* task gate */ |
|
650 |
case 6: /* 286 interrupt gate */ |
|
651 |
case 7: /* 286 trap gate */ |
|
652 |
case 14: /* 386 interrupt gate */ |
|
653 |
case 15: /* 386 trap gate */ |
|
654 |
break; |
|
655 |
default: |
|
656 |
raise_exception_err(EXCP0D_GPF, intno * 8 + 2); |
|
657 |
break; |
|
658 |
} |
|
659 |
dpl = (e2 >> DESC_DPL_SHIFT) & 3; |
|
660 |
cpl = env->segs[R_CS] & 3; |
|
661 |
/* check privledge if software int */ |
|
662 |
if (is_int && dpl < cpl) |
|
663 |
raise_exception_err(EXCP0D_GPF, intno * 8 + 2); |
|
664 |
/* check valid bit */ |
|
665 |
if (!(e2 & DESC_P_MASK)) |
|
666 |
raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2); |
|
667 |
} |
|
668 |
|
|
669 |
#else |
|
670 |
|
|
671 |
/* |
|
672 |
* is_int is TRUE if coming from the int instruction. next_eip is the |
|
673 |
* EIP value AFTER the interrupt instruction. It is only relevant if |
|
674 |
* is_int is TRUE. |
|
675 |
*/ |
|
676 |
void raise_interrupt(int intno, int is_int, int error_code, |
|
677 |
unsigned int next_eip) |
|
678 |
{ |
|
679 |
SegmentDescriptorTable *dt; |
|
680 |
uint8_t *ptr; |
|
681 |
int dpl, cpl; |
|
682 |
uint32_t e2; |
|
683 |
|
|
684 |
dt = &env->idt; |
|
685 |
ptr = dt->base + (intno * 8); |
|
686 |
e2 = ldl(ptr + 4); |
|
687 |
|
|
688 |
dpl = (e2 >> DESC_DPL_SHIFT) & 3; |
|
689 |
cpl = 3; |
|
690 |
/* check privledge if software int */ |
|
691 |
if (is_int && dpl < cpl) |
|
692 |
raise_exception_err(EXCP0D_GPF, intno * 8 + 2); |
|
693 |
|
|
694 |
/* Since we emulate only user space, we cannot do more than |
|
695 |
exiting the emulation with the suitable exception and error |
|
696 |
code */ |
|
697 |
if (is_int) |
|
698 |
EIP = next_eip; |
|
699 |
env->exception_index = intno; |
|
700 |
env->error_code = error_code; |
|
701 |
|
|
702 |
cpu_loop_exit(); |
|
703 |
} |
|
704 |
|
|
705 |
#endif |
|
706 |
|
|
707 |
/* shortcuts to generate exceptions */ |
|
708 |
void raise_exception_err(int exception_index, int error_code) |
|
709 |
{ |
|
710 |
raise_interrupt(exception_index, 0, error_code, 0); |
|
711 |
} |
|
712 |
|
|
713 |
void raise_exception(int exception_index) |
|
714 |
{ |
|
715 |
raise_interrupt(exception_index, 0, 0, 0); |
|
716 |
} |
|
717 |
|
|
718 |
void OPPROTO op_raise_interrupt(void) |
|
618 | 719 |
{ |
619 | 720 |
int intno; |
721 |
unsigned int next_eip; |
|
620 | 722 |
intno = PARAM1; |
621 |
EIP = PARAM2;
|
|
622 |
raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
|
|
723 |
next_eip = PARAM2;
|
|
724 |
raise_interrupt(intno, 1, 0, next_eip);
|
|
623 | 725 |
} |
624 | 726 |
|
625 | 727 |
void OPPROTO op_raise_exception(void) |
... | ... | |
634 | 736 |
int eflags; |
635 | 737 |
eflags = cc_table[CC_OP].compute_all(); |
636 | 738 |
if (eflags & CC_O) { |
637 |
EIP = PARAM1; |
|
638 |
raise_exception(EXCP04_INTO); |
|
739 |
raise_interrupt(EXCP04_INTO, 1, 0, PARAM1); |
|
639 | 740 |
} |
640 | 741 |
FORCE_RET(); |
641 | 742 |
} |
... | ... | |
674 | 775 |
low = ldsw((uint8_t *)A0); |
675 | 776 |
high = ldsw((uint8_t *)A0 + 2); |
676 | 777 |
v = (int16_t)T0; |
677 |
if (v < low || v > high) |
|
778 |
if (v < low || v > high) { |
|
779 |
EIP = PARAM1; |
|
678 | 780 |
raise_exception(EXCP05_BOUND); |
781 |
} |
|
679 | 782 |
FORCE_RET(); |
680 | 783 |
} |
681 | 784 |
|
... | ... | |
685 | 788 |
low = ldl((uint8_t *)A0); |
686 | 789 |
high = ldl((uint8_t *)A0 + 4); |
687 | 790 |
v = T0; |
688 |
if (v < low || v > high) |
|
791 |
if (v < low || v > high) { |
|
792 |
EIP = PARAM1; |
|
689 | 793 |
raise_exception(EXCP05_BOUND); |
794 |
} |
|
690 | 795 |
FORCE_RET(); |
691 | 796 |
} |
692 | 797 |
|
... | ... | |
1116 | 1221 |
|
1117 | 1222 |
/* segment handling */ |
1118 | 1223 |
|
1119 |
/* XXX: use static VM86 information */
|
|
1120 |
void load_seg(int seg_reg, int selector) |
|
1224 |
/* only works if protected mode and not VM86 */
|
|
1225 |
void load_seg(int seg_reg, int selector, unsigned cur_eip)
|
|
1121 | 1226 |
{ |
1122 | 1227 |
SegmentCache *sc; |
1123 | 1228 |
SegmentDescriptorTable *dt; |
... | ... | |
1126 | 1231 |
uint8_t *ptr; |
1127 | 1232 |
|
1128 | 1233 |
sc = &env->seg_cache[seg_reg]; |
1129 |
if (env->eflags & VM_MASK) { |
|
1130 |
sc->base = (void *)(selector << 4); |
|
1131 |
sc->limit = 0xffff; |
|
1132 |
sc->seg_32bit = 0; |
|
1234 |
if ((selector & 0xfffc) == 0) { |
|
1235 |
/* null selector case */ |
|
1236 |
if (seg_reg == R_SS) { |
|
1237 |
EIP = cur_eip; |
|
1238 |
raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
|
1239 |
} else { |
|
1240 |
/* XXX: each access should trigger an exception */ |
|
1241 |
sc->base = NULL; |
|
1242 |
sc->limit = 0; |
|
1243 |
sc->seg_32bit = 1; |
|
1244 |
} |
|
1133 | 1245 |
} else { |
1134 | 1246 |
if (selector & 0x4) |
1135 | 1247 |
dt = &env->ldt; |
1136 | 1248 |
else |
1137 | 1249 |
dt = &env->gdt; |
1138 | 1250 |
index = selector & ~7; |
1139 |
if ((index + 7) > dt->limit) |
|
1140 |
raise_exception_err(EXCP0D_GPF, selector); |
|
1251 |
if ((index + 7) > dt->limit) { |
|
1252 |
EIP = cur_eip; |
|
1253 |
raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
|
1254 |
} |
|
1141 | 1255 |
ptr = dt->base + index; |
1142 | 1256 |
e1 = ldl(ptr); |
1143 | 1257 |
e2 = ldl(ptr + 4); |
1258 |
if (!(e2 & DESC_S_MASK) || |
|
1259 |
(e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { |
|
1260 |
EIP = cur_eip; |
|
1261 |
raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
|
1262 |
} |
|
1263 |
|
|
1264 |
if (seg_reg == R_SS) { |
|
1265 |
if ((e2 & (DESC_CS_MASK | DESC_W_MASK)) == 0) { |
|
1266 |
EIP = cur_eip; |
|
1267 |
raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
|
1268 |
} |
|
1269 |
} else { |
|
1270 |
if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { |
|
1271 |
EIP = cur_eip; |
|
1272 |
raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
|
1273 |
} |
|
1274 |
} |
|
1275 |
|
|
1276 |
if (!(e2 & DESC_P_MASK)) { |
|
1277 |
EIP = cur_eip; |
|
1278 |
if (seg_reg == R_SS) |
|
1279 |
raise_exception_err(EXCP0C_STACK, selector & 0xfffc); |
|
1280 |
else |
|
1281 |
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); |
|
1282 |
} |
|
1283 |
|
|
1144 | 1284 |
sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); |
1145 | 1285 |
sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000); |
1146 | 1286 |
if (e2 & (1 << 23)) |
... | ... | |
1156 | 1296 |
|
1157 | 1297 |
void OPPROTO op_movl_seg_T0(void) |
1158 | 1298 |
{ |
1159 |
load_seg(PARAM1, T0 & 0xffff); |
|
1299 |
load_seg(PARAM1, T0 & 0xffff, PARAM2); |
|
1300 |
} |
|
1301 |
|
|
1302 |
/* faster VM86 version */ |
|
1303 |
void OPPROTO op_movl_seg_T0_vm(void) |
|
1304 |
{ |
|
1305 |
int selector; |
|
1306 |
|
|
1307 |
selector = T0 & 0xffff; |
|
1308 |
/* env->segs[] access */ |
|
1309 |
*(uint32_t *)((char *)env + PARAM1) = selector; |
|
1310 |
/* env->seg_cache[] access */ |
|
1311 |
((SegmentCache *)((char *)env + PARAM2))->base = (void *)(selector << 4); |
|
1160 | 1312 |
} |
1161 | 1313 |
|
1162 | 1314 |
void OPPROTO op_movl_T0_seg(void) |
Also available in: Unified diff