Commit f4beb510a41980e119f787746442ca1b87c06754

Authored by bellard
1 parent d731dae8

precise exceptions - more accurate interrupt semantics


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@193 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 2 changed files with 250 additions and 54 deletions
linux-user/main.c
... ... @@ -100,24 +100,35 @@ int cpu_x86_inl(CPUX86State *env, int addr)
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,30 +139,34 @@ void cpu_loop(CPUX86State *env)
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,11 +380,40 @@ int main(int argc, char **argv)
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);
... ...
op-i386.c
... ... @@ -364,8 +364,10 @@ void OPPROTO op_divb_AL_T0(void)
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,8 +379,10 @@ void OPPROTO op_idivb_AL_T0(void)
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,8 +394,10 @@ void OPPROTO op_divw_AX_T0(void)
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,8 +410,10 @@ void OPPROTO op_idivw_AX_T0(void)
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,8 +443,10 @@ void OPPROTO op_divl_EAX_T0(void)
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,8 +464,10 @@ void OPPROTO op_idivl_EAX_T0(void)
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,12 +626,102 @@ void OPPROTO op_jmp_im(void)
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,8 +736,7 @@ void OPPROTO op_into(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,8 +775,10 @@ void OPPROTO op_boundw(void)
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,8 +788,10 @@ void OPPROTO op_boundl(void)
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,8 +1221,8 @@ void OPPROTO op_das(void)
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,21 +1231,56 @@ void load_seg(int seg_reg, int selector)
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,7 +1296,19 @@ void load_seg(int seg_reg, int selector)
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)
... ...