📄 vm86.c
字号:
setreg32(regs, r, result); TRACE((regs, regs->eip - eip, "addl *0x%x, %%e%s", addr, rnames[r])); } else { val1 = getreg16(regs, r); val2 = read16(addr); result = val1 + val2; setreg16(regs, r, result); TRACE((regs, regs->eip - eip, "addw *0x%x, %%%s", addr, rnames[r])); } break; } if (opc == 0x00) hi_bit = (1<<7); else hi_bit = (prefix & DATA32) ? (1<<31) : (1<<15); set_eflags_add(hi_bit, val1, val2, result, regs); return 1;}/* * We need to handle pop opcodes that address memory beyond the 64KB * segment limit that VM8086 mode enforces. */static intpop(struct regs *regs, unsigned prefix, unsigned opc){ unsigned eip = regs->eip - 1; unsigned modrm = fetch8(regs); unsigned addr = operand(prefix, regs, modrm); if ((modrm & 0xC0) == 0xC0) /* no registers */ return 0; switch (opc) { case 0x8F: /* pop r/m16 */ if ((modrm >> 3) & 7) return 0; if (prefix & DATA32) write32(addr, pop32(regs)); else write16(addr, pop16(regs)); TRACE((regs, regs->eip - eip, "pop *0x%x", addr)); break; /* other pop opcodes ... */ } return 1;}static intmov_to_seg(struct regs *regs, unsigned prefix, unsigned opc){ unsigned modrm = fetch8(regs); /* * Emulate segment loads in: * 1) real->protected mode. * 2) protected->real mode. */ if (mode != VM86_REAL_TO_PROTECTED && mode != VM86_PROTECTED_TO_REAL) return 0; /* Register source only. */ if ((modrm & 0xC0) != 0xC0) goto fail; switch ((modrm & 0x38) >> 3) { case 0: /* es */ regs->ves = getreg16(regs, modrm); if (mode == VM86_PROTECTED_TO_REAL) return 1; saved_rm_regs.ves = 0; oldctx.es_sel = regs->ves; return 1; /* case 1: cs */ case 2: /* ss */ regs->uss = getreg16(regs, modrm); if (mode == VM86_PROTECTED_TO_REAL) return 1; saved_rm_regs.uss = 0; oldctx.ss_sel = regs->uss; return 1; case 3: /* ds */ regs->vds = getreg16(regs, modrm); if (mode == VM86_PROTECTED_TO_REAL) return 1; saved_rm_regs.vds = 0; oldctx.ds_sel = regs->vds; return 1; case 4: /* fs */ regs->vfs = getreg16(regs, modrm); if (mode == VM86_PROTECTED_TO_REAL) return 1; saved_rm_regs.vfs = 0; oldctx.fs_sel = regs->vfs; return 1; case 5: /* gs */ regs->vgs = getreg16(regs, modrm); if (mode == VM86_PROTECTED_TO_REAL) return 1; saved_rm_regs.vgs = 0; oldctx.gs_sel = regs->vgs; return 1; } fail: printf("%s:%d: missed opcode %02x %02x\n", __FUNCTION__, __LINE__, opc, modrm); return 0;}/* * Emulate a segment load in protected mode */static intload_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes){ uint64_t gdt_phys_base; unsigned long long entry; /* protected mode: use seg as index into gdt */ if (sel > oldctx.gdtr_limit) return 0; if (sel == 0) { arbytes->fields.null_bit = 1; return 1; } gdt_phys_base = guest_linear_to_phys(oldctx.gdtr_base); if (gdt_phys_base != (uint32_t)gdt_phys_base) { printf("gdt base address above 4G\n"); cpuid_addr_value(gdt_phys_base + 8 * (sel >> 3), &entry); } else entry = ((unsigned long long *)(long)gdt_phys_base)[sel >> 3]; /* Check the P bit first */ if (!((entry >> (15+32)) & 0x1) && sel != 0) return 0; *base = (((entry >> (56-24)) & 0xFF000000) | ((entry >> (32-16)) & 0x00FF0000) | ((entry >> ( 16)) & 0x0000FFFF)); *limit = (((entry >> (48-16)) & 0x000F0000) | (entry & 0x0000FFFF)); arbytes->bytes = 0; arbytes->fields.seg_type = (entry >> (8+32)) & 0xF; /* TYPE */ arbytes->fields.s = (entry >> (12+32)) & 0x1; /* S */ if (arbytes->fields.s) arbytes->fields.seg_type |= 1; /* accessed */ arbytes->fields.dpl = (entry >> (13+32)) & 0x3; /* DPL */ arbytes->fields.p = (entry >> (15+32)) & 0x1; /* P */ arbytes->fields.avl = (entry >> (20+32)) & 0x1; /* AVL */ arbytes->fields.default_ops_size = (entry >> (22+32)) & 0x1; /* D */ if (entry & (1ULL << (23+32))) { /* G */ arbytes->fields.g = 1; *limit = (*limit << 12) | 0xFFF; } return 1;}/* * Emulate a protected mode segment load, falling back to clearing it if * the descriptor was invalid. */static voidload_or_clear_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes){ if (!load_seg(sel, base, limit, arbytes)) load_seg(0, base, limit, arbytes);}static unsigned char rm_irqbase[2];/* * Transition to protected mode */static voidprotected_mode(struct regs *regs){ extern char stack_top[]; oldctx.rm_irqbase[0] = rm_irqbase[0]; oldctx.rm_irqbase[1] = rm_irqbase[1]; regs->eflags &= ~(EFLAGS_TF|EFLAGS_VM); oldctx.eip = regs->eip; oldctx.esp = regs->uesp; oldctx.eflags = regs->eflags; /* reload all segment registers */ if (!load_seg(regs->cs, &oldctx.cs_base, &oldctx.cs_limit, &oldctx.cs_arbytes)) panic("Invalid %%cs=0x%x for protected mode\n", regs->cs); oldctx.cs_sel = regs->cs; load_or_clear_seg(oldctx.es_sel, &oldctx.es_base, &oldctx.es_limit, &oldctx.es_arbytes); load_or_clear_seg(oldctx.ss_sel, &oldctx.ss_base, &oldctx.ss_limit, &oldctx.ss_arbytes); load_or_clear_seg(oldctx.ds_sel, &oldctx.ds_base, &oldctx.ds_limit, &oldctx.ds_arbytes); load_or_clear_seg(oldctx.fs_sel, &oldctx.fs_base, &oldctx.fs_limit, &oldctx.fs_arbytes); load_or_clear_seg(oldctx.gs_sel, &oldctx.gs_base, &oldctx.gs_limit, &oldctx.gs_arbytes); /* initialize jump environment to warp back to protected mode */ regs->uss = DATA_SELECTOR; regs->uesp = (unsigned long)stack_top; regs->cs = CODE_SELECTOR; regs->eip = (unsigned long)switch_to_protected_mode; /* this should get us into 32-bit mode */}/* * Start real-mode emulation */static voidreal_mode(struct regs *regs){ regs->eflags |= EFLAGS_VM | 0x02; /* * When we transition from protected to real-mode and we * have not reloaded the segment descriptors yet, they are * interpreted as if they were in protect mode. * We emulate this behavior by assuming that these memory * reference are below 1MB and set %ss, %ds, %es accordingly. */ if (regs->uss != 0) { if (regs->uss >= HIGHMEM) panic("%%ss 0x%lx higher than 1MB", regs->uss); regs->uss = address(regs, regs->uss, 0) >> 4; } else { regs->uss = saved_rm_regs.uss; } if (regs->vds != 0) { if (regs->vds >= HIGHMEM) panic("%%ds 0x%lx higher than 1MB", regs->vds); regs->vds = address(regs, regs->vds, 0) >> 4; } else { regs->vds = saved_rm_regs.vds; } if (regs->ves != 0) { if (regs->ves >= HIGHMEM) panic("%%es 0x%lx higher than 1MB", regs->ves); regs->ves = address(regs, regs->ves, 0) >> 4; } else { regs->ves = saved_rm_regs.ves; } /* this should get us into 16-bit mode */}/* * This is the smarts of the emulator and handles the mode transitions. The * emulator handles 4 different modes. 1) VM86_REAL: emulated real-mode, * Just handle those instructions that are not supported under VM8086. * 2) VM86_REAL_TO_PROTECTED: going from real-mode to protected mode. In * this we single step through the instructions until we reload the * new %cs (some OSes do a lot of computations before reloading %cs). 2) * VM86_PROTECTED_TO_REAL when we are going from protected to real mode. In * this case we emulate the instructions by hand. Finally, 4) VM86_PROTECTED * when we transitioned to protected mode and we should abandon the * emulator. No instructions are emulated when in VM86_PROTECTED mode. */voidset_mode(struct regs *regs, enum vm86_mode newmode){ switch (newmode) { case VM86_REAL: if (mode == VM86_PROTECTED_TO_REAL || mode == VM86_REAL_TO_PROTECTED) { regs->eflags &= ~EFLAGS_TF; real_mode(regs); } else if (mode != VM86_REAL) panic("unexpected real mode transition"); break; case VM86_REAL_TO_PROTECTED: if (mode == VM86_REAL) { regs->eflags |= EFLAGS_TF; saved_rm_regs.vds = regs->vds; saved_rm_regs.ves = regs->ves; saved_rm_regs.vfs = regs->vfs; saved_rm_regs.vgs = regs->vgs; saved_rm_regs.uss = regs->uss; oldctx.ds_sel = 0; oldctx.es_sel = 0; oldctx.fs_sel = 0; oldctx.gs_sel = 0; oldctx.ss_sel = 0; } else if (mode != VM86_REAL_TO_PROTECTED) panic("unexpected real-to-protected mode transition"); break; case VM86_PROTECTED_TO_REAL: if (mode != VM86_PROTECTED) panic("unexpected protected-to-real mode transition"); break; case VM86_PROTECTED: if (mode != VM86_REAL_TO_PROTECTED) panic("unexpected protected mode transition"); protected_mode(regs); break; } mode = newmode; if (mode != VM86_PROTECTED) TRACE((regs, 0, states[mode]));}static voidjmpl(struct regs *regs, int prefix){ unsigned n = regs->eip; unsigned cs, eip; eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); cs = fetch16(regs); TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); regs->cs = cs; regs->eip = eip; if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); else if (mode == VM86_PROTECTED_TO_REAL) /* jump to real mode */ set_mode(regs, VM86_REAL); else panic("jmpl");}static voidjmpl_indirect(struct regs *regs, int prefix, unsigned modrm){ unsigned n = regs->eip; unsigned cs, eip; unsigned addr; addr = operand(prefix, regs, modrm); eip = (prefix & DATA32) ? read32(addr) : read16(addr); addr += (prefix & DATA32) ? 4 : 2; cs = read16(addr); TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); regs->cs = cs; regs->eip = eip; if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); else if (mode == VM86_PROTECTED_TO_REAL) /* jump to real mode */ set_mode(regs, VM86_REAL); else panic("jmpl");}static voidretl(struct regs *regs, int prefix){ unsigned cs, eip; if (prefix & DATA32) { eip = pop32(regs); cs = MASK16(pop32(regs)); } else { eip = pop16(regs); cs = pop16(regs); } TRACE((regs, 1, "retl (to 0x%x:0x%x)", cs, eip)); regs->cs = cs; regs->eip = eip; if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); else if (mode == VM86_PROTECTED_TO_REAL) /* jump to real mode */ set_mode(regs, VM86_REAL); else panic("retl");}static voidinterrupt(struct regs *regs, int n){ TRACE((regs, 0, "external interrupt %d", n)); push16(regs, regs->eflags); push16(regs, regs->cs); push16(regs, regs->eip); regs->eflags &= ~EFLAGS_IF; regs->eip = read16(address(regs, 0, n * 4)); regs->cs = read16(address(regs, 0, n * 4 + 2));}/* * Most port I/O operations are passed unmodified. We do have to be * careful and make sure the emulated program isn't remapping the * interrupt vectors. The following simple state machine catches * these attempts and rewrites them. */static intoutbyte(struct regs *regs, unsigned prefix, unsigned opc){ static char icw2[2] = { 0 }; int al, port; switch (opc) { case 0xE6: /* outb port, al */ port = fetch8(regs); break; case 0xEE: /* outb (%dx), al */ port = MASK16(regs->edx); break; default: return 0; } al = regs->eax & 0xFF; switch (port) { case PIC_MASTER + PIC_CMD: if (al & (1 << 4)) /* A0=0,D4=1 -> ICW1 */ icw2[0] = 1; break; case PIC_MASTER + PIC_IMR: if (icw2[0]) { icw2[0] = 0; printf("Remapping master: ICW2 0x%x -> 0x%x\n", al, NR_EXCEPTION_HANDLER); rm_irqbase[0] = al; al = NR_EXCEPTION_HANDLER; } break; case PIC_SLAVE + PIC_CMD: if (al & (1 << 4)) /* A0=0,D4=1 -> ICW1 */ icw2[1] = 1; break; case PIC_SLAVE + PIC_IMR: if (icw2[1]) { icw2[1] = 0; printf("Remapping slave: ICW2 0x%x -> 0x%x\n", al, NR_EXCEPTION_HANDLER+8); rm_irqbase[1] = al; al = NR_EXCEPTION_HANDLER+8; } break; } outb(port, al); return 1;}static intinbyte(struct regs *regs, unsigned prefix, unsigned opc){ int port; switch (opc) { case 0xE4: /* inb al, port */ port = fetch8(regs); break; case 0xEC: /* inb al, (%dx) */ port = MASK16(regs->edx); break; default: return 0; } regs->eax = (regs->eax & ~0xFF) | inb(port); return 1;}static voidpushrm(struct regs *regs, int prefix, unsigned modrm){ unsigned n = regs->eip; unsigned addr; unsigned data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -