Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/vm/vm86.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 #include "arch/i386.h"
0019 #include "vm/vm86.h"
0020 #include "kernel.h"
0021 #include "util/printf.h"
0022 
0023 #define DEBUG_VM86 4
0024 
0025 #if DEBUG_VM86 > 0
0026 #define DLOG(fmt,...) DLOG_PREFIX("vm86",fmt,##__VA_ARGS__)
0027 #else
0028 #define DLOG(fmt,...) ;
0029 #endif
0030 
0031 #define com1_printf logger_printf
0032 
0033 /* Stored BIOS interrupt vector table */
0034 vm86_farptr vmx_vm86_IVT[256];
0035 
0036 void
0037 vmx_vm86_global_init (void)
0038 {
0039   extern uint32 vmx_vm86_pgt[1024];
0040   /* Temporarily re-map page 0 */
0041   vmx_vm86_pgt[0] = 7;
0042   flush_tlb_all ();
0043   /* Yes, I'm really dereferencing the address "0x0": back-up the BIOS
0044    * interrupt vector table. */
0045   memcpy ((uint8 *) vmx_vm86_IVT, (uint8 *) 0x0, 256*sizeof(vm86_farptr));
0046 #if DEBUG_VM86 > 3
0047   int i,j;
0048   /* print the first 32 entries of the IVT */
0049   for (i=0;i<4;i++) {
0050     logger_printf ("  ");
0051     for (j=0;j<8;j++) {
0052       logger_printf ("IVT[0x%.02X]=%.04X:%.04X ",
0053                      i*8 + j, vmx_vm86_IVT[i*8+j].segm, vmx_vm86_IVT[i*8+j].offs);
0054     }
0055     logger_printf ("\n");
0056   }
0057 #endif
0058   /* FIXME: Leave page 0 mapped until I implement a vm86 PF handler. */
0059   //  vmx_vm86_pgt[0] = 0;
0060   flush_tlb_all ();
0061 }
0062 
0063 /* Update the virtual machine with a new CS:IP */
0064 static inline void
0065 inc_ip (uint8 *eip, uint32 amount)
0066 {
0067   vm86_farptr new_ip;
0068   new_ip = LIN32_TO_FP (eip + amount);
0069   vmwrite (new_ip.offs, VMXENC_GUEST_RIP);
0070   vmwrite (new_ip.segm, VMXENC_GUEST_CS_SEL);
0071   vmwrite (new_ip.segm << 4, VMXENC_GUEST_CS_BASE);
0072 }
0073 
0074 static inline void
0075 upd_ip (uint32 amount)
0076 {
0077   uint16 ip = vmread (VMXENC_GUEST_RIP) & 0xFFFF;
0078   uint16 cs = vmread (VMXENC_GUEST_CS_SEL) & 0xFFFF;
0079   uint8 *eip = REAL_TO_LIN32 (cs, ip, uint8);
0080   inc_ip (eip, amount);
0081 }
0082 
0083 struct _modrm_byte
0084 {
0085    uint8 rm  : 3;
0086    uint8 reg : 3;
0087    uint8 mod : 2;
0088 };
0089 
0090 static void
0091 decode_modrm_32 (uint8 *eip, uint8 *reg_op, bool *has_sib, uint8 *rm,
0092                  uint8 *disp_size, uint32 *disp)
0093 {
0094   struct _modrm_byte modrm = *((struct _modrm_byte *) eip);
0095 
0096   /* Check if Scale-Index-Base byte present */
0097   *has_sib = modrm.mod < 3 && modrm.rm == 4;
0098 
0099   /* Obtain displacement size */
0100   switch (modrm.mod) {
0101   case 0:
0102     if (modrm.rm == 5) {
0103       *disp_size = 4;
0104       *disp = *((uint32 *) (eip + (*has_sib ? 2 : 1)));
0105     } else {
0106       *disp_size = 0;
0107       *disp = 0;
0108     }
0109     break;
0110   case 1:
0111     *disp_size = 1;
0112     *disp = *((eip + (*has_sib ? 2 : 1)));
0113     break;
0114   case 2:
0115     *disp_size = 4;
0116     *disp = *((uint32 *) (eip + (*has_sib ? 2 : 1)));
0117     break;
0118   case 3:
0119     *disp_size = 0;
0120     *disp = 0;
0121     break;
0122   }
0123 
0124   *reg_op = modrm.reg;
0125   *rm = modrm.rm;
0126 }
0127 
0128 /* Using the current Guest GDTR, try to set the segment registers to
0129  * legal working values.  */
0130 static sint32
0131 pick_segment_regs (void)
0132 {
0133   uint32 i, cs_i = 0, ds_i = 0;
0134   descriptor *ad;
0135   descriptor *ad_bas = (descriptor *) vmread (VMXENC_GUEST_GDTR_BASE);
0136   descriptor *ad_lim =
0137     (descriptor *)(((uint8 *)ad_bas) + vmread (VMXENC_GUEST_GDTR_LIMIT) + 1);
0138 
0139 #if DEBUG_VM86 > 1
0140   com1_printf ("  pick_segment_regs: ad_bas = %p ad_lim = %p\n",
0141                ad_bas, ad_lim);
0142 #endif
0143   for (i = 0, ad = ad_bas; ad < ad_lim; i++, ad++) {
0144 #if DEBUG_VM86 > 1
0145     com1_printf ("    i=%.02X dpl=%.01X type=%.01X\n", i, ad->uDPL, ad->uType);
0146 #endif
0147     if (!cs_i) {
0148       /* pick a code segment */
0149       if (ad->uDPL == 0 && (ad->uType & 0x8))
0150         cs_i = i;
0151     }
0152     if (!ds_i) {
0153       /* pick a data segment */
0154       if (ad->uDPL == 0 && !(ad->uType & 0x8))
0155         ds_i = i;
0156     }
0157   }
0158 
0159   ad = ad_bas;
0160   if (cs_i && ds_i) {
0161     uint32 cs_base, ds_base;
0162     uint32 cs_limit, ds_limit;
0163     /* found both code and data segments */
0164 #define ACCESS(ad)                              \
0165   (( 0x01            << 0x00 ) |                \
0166    ( (ad).uType        << 0x00 ) |              \
0167    ( (ad).uDPL         << 0x05 ) |              \
0168    ( (ad).fPresent     << 0x07 ) |              \
0169    ( (ad).f            << 0x0C ) |              \
0170    ( (ad).f0           << 0x0D ) |              \
0171    ( (ad).fX           << 0x0E ) |              \
0172    ( (ad).fGranularity << 0x0F ))
0173     vmwrite (ACCESS (ad[cs_i]), VMXENC_GUEST_CS_ACCESS);
0174     vmwrite (ACCESS (ad[ds_i]), VMXENC_GUEST_SS_ACCESS);
0175     vmwrite (ACCESS (ad[ds_i]), VMXENC_GUEST_DS_ACCESS);
0176     vmwrite (ACCESS (ad[ds_i]), VMXENC_GUEST_ES_ACCESS);
0177     vmwrite (ACCESS (ad[ds_i]), VMXENC_GUEST_FS_ACCESS);
0178     vmwrite (ACCESS (ad[ds_i]), VMXENC_GUEST_GS_ACCESS);
0179 
0180     cs_base = (ad[cs_i].pBase0 |
0181                (ad[cs_i].pBase1 << 16) |
0182                (ad[cs_i].pBase2 << 24));
0183     ds_base = (ad[ds_i].pBase0 |
0184                (ad[ds_i].pBase1 << 16) |
0185                (ad[ds_i].pBase2 << 24));
0186 
0187     cs_limit = (ad[cs_i].uLimit0 | (ad[cs_i].uLimit1 << 16));
0188     if (ad[cs_i].fGranularity) {
0189       cs_limit <<= 12;
0190       cs_limit |= 0xFFF;
0191     }
0192     ds_limit = (ad[ds_i].uLimit0 | (ad[ds_i].uLimit1 << 16));
0193     if (ad[ds_i].fGranularity) {
0194       ds_limit <<= 12;
0195       ds_limit |= 0xFFF;
0196     }
0197 
0198     vmwrite (cs_i << 3, VMXENC_GUEST_CS_SEL);
0199     vmwrite (ds_i << 3, VMXENC_GUEST_SS_SEL);
0200     vmwrite (ds_i << 3, VMXENC_GUEST_DS_SEL);
0201     vmwrite (ds_i << 3, VMXENC_GUEST_ES_SEL);
0202     vmwrite (ds_i << 3, VMXENC_GUEST_FS_SEL);
0203     vmwrite (ds_i << 3, VMXENC_GUEST_GS_SEL);
0204 
0205     vmwrite (cs_base, VMXENC_GUEST_CS_BASE);
0206     vmwrite (ds_base, VMXENC_GUEST_SS_BASE);
0207     vmwrite (ds_base, VMXENC_GUEST_DS_BASE);
0208     vmwrite (ds_base, VMXENC_GUEST_ES_BASE);
0209     vmwrite (ds_base, VMXENC_GUEST_FS_BASE);
0210     vmwrite (ds_base, VMXENC_GUEST_GS_BASE);
0211 
0212     vmwrite (cs_limit, VMXENC_GUEST_CS_LIMIT);
0213     vmwrite (ds_limit, VMXENC_GUEST_SS_LIMIT);
0214     vmwrite (ds_limit, VMXENC_GUEST_DS_LIMIT);
0215     vmwrite (ds_limit, VMXENC_GUEST_ES_LIMIT);
0216     vmwrite (ds_limit, VMXENC_GUEST_FS_LIMIT);
0217     vmwrite (ds_limit, VMXENC_GUEST_GS_LIMIT);
0218 
0219 #if DEBUG_VM86 > 1
0220     com1_printf ("    CS=%.04X CS.base=%p CS.limit=%p CS.access=%.02X\n"
0221                  "    DS=%.04X DS.base=%p DS.limit=%p DS.access=%.02X\n",
0222                  cs_i << 3, cs_base, cs_limit, vmread (VMXENC_GUEST_CS_ACCESS),
0223                  ds_i << 3, ds_base, ds_limit, vmread (VMXENC_GUEST_DS_ACCESS));
0224 #endif
0225 
0226     return 0;
0227   } else
0228     return -1;
0229 }
0230 
0231 sint32
0232 vmx_vm86_handle_GPF (virtual_machine *vm)
0233 {
0234   bool a32 = FALSE, o32 = FALSE, rep = FALSE, repnz = FALSE;
0235   uint16 ip = vmread (VMXENC_GUEST_RIP) & 0xFFFF;
0236   uint16 cs = vmread (VMXENC_GUEST_CS_SEL) & 0xFFFF;
0237   uint16 sp = vmread (VMXENC_GUEST_RSP) & 0xFFFF;
0238   uint16 ss = vmread (VMXENC_GUEST_SS_SEL) & 0xFFFF;
0239   uint32 eflags = vmread (VMXENC_GUEST_RFLAGS) & 0xFFFFFFFF;
0240   uint8 *eip = REAL_TO_LIN32 (cs, ip, uint8);
0241   uint16 *stk = REAL_TO_LIN32 (ss, sp, uint16);
0242 
0243 #if DEBUG_VM86 > 3
0244   com1_printf ("vmx_vm86_handle_GPF: CS:IP = %.04X:%.04X (I=%0.02X) SS:SP = %.04X:%.04X (ESP=%.08X)\n", cs, ip, eip[0], ss, sp, (uint32) stk);
0245 #endif
0246 
0247  parse:
0248   switch (eip[0]) {
0249   case 0xF0:                    /* LOCK prefix */
0250     break;
0251   case 0x26:                    /* segment-override-prefix: ES */
0252   case 0x2E:                    /* segment-override-prefix: CS */
0253   case 0x36:                    /* segment-override-prefix: SS */
0254   case 0x3E:                    /* segment-override-prefix: DS */
0255   case 0x64:                    /* segment-override-prefix: FS */
0256   case 0x65:                    /* segment-override-prefix: GS */
0257     break;
0258   case 0x66:                    /* indicate 32-bit operand */
0259     o32 = TRUE;
0260     eip++;
0261     goto parse;
0262   case 0x67:
0263     a32 = TRUE;                 /* indicate 32-bit address */
0264     eip++;
0265     goto parse;
0266   case 0xF2:                    /* REPNZ prefix */
0267     repnz = TRUE;
0268     eip++;
0269     goto parse;
0270   case 0xF3:                    /* REP prefix */
0271     rep = TRUE;
0272     eip++;
0273     goto parse;
0274   case 0xCD:                  /* INT instruction */
0275     {
0276       uint8 vec = eip[1];
0277       vm86_farptr dest = vmx_vm86_IVT[vec];
0278       vm86_farptr new_stk;
0279 #if DEBUG_VM86 > 3
0280       com1_printf ("  interrupt to vector %.02X farjump to CS:IP = %.04X:%.04X\n",
0281                    vec, dest.segm, dest.offs);
0282 #endif
0283       /* Simulate an interrupt dispatch by the CPU in real-mode */
0284       stk -= 3;                 /* push 3 values on the stack */
0285       new_stk = LIN32_TO_FP (stk);
0286       /* Write the new stack pointer to the guest */
0287       vmwrite (new_stk.offs, VMXENC_GUEST_RSP);
0288       vmwrite (new_stk.segm, VMXENC_GUEST_SS_SEL);
0289       vmwrite (new_stk.segm << 4, VMXENC_GUEST_SS_BASE);
0290       /* Set the IRETurn CS:IP address to the next instruction */
0291       stk[0] = LIN32_TO_FP (eip + 2).offs;
0292       stk[1] = LIN32_TO_FP (eip + 2).segm;
0293       stk[2] = (uint16) eflags;
0294       /* Write stored flags to memory but change "IF" according to VIF. */
0295       if (eflags & F_VIF)
0296         stk[2] |= F_IF;
0297       else
0298         stk[2] &= ~F_IF;
0299 #if DEBUG_VM86 > 3
0300       com1_printf ("  New SS:SP = %.04X:%.04X  IRET CS:IP = %.04X:%.04X FL = %.04X\n",
0301                    new_stk.segm, new_stk.offs,
0302                    stk[1], stk[0], stk[2]);
0303 #endif
0304       /* Clear the virtual IF */
0305       eflags &= ~F_VIF;
0306       vmwrite (eflags, VMXENC_GUEST_RFLAGS);
0307       /* Now, dispatch to dest far pointer */
0308       vmwrite (dest.offs, VMXENC_GUEST_RIP);
0309       vmwrite (dest.segm, VMXENC_GUEST_CS_SEL);
0310       vmwrite (dest.segm << 4, VMXENC_GUEST_CS_BASE);
0311       return 0;                 /* continue guest */
0312     }
0313   case 0x9C:                    /* PUSHF */
0314     if (o32) {                  /* 32-bit PUSHF */
0315       stk -= 2;
0316       *((uint32 *) stk) = eflags & 0xDFF; /* hide the IF flag */
0317       if (eflags & F_VIF)                 /* set it according to VIF */
0318         *((uint32 *) stk) |= F_IF;
0319       else
0320         *((uint32 *) stk) &= ~F_IF;
0321     } else {                    /* 16-bit PUSHF */
0322       stk--;
0323       *stk = (uint16) eflags & 0xDFF; /* hide the IF flag */
0324       if (eflags & F_VIF)             /* set it according to VIF */
0325         *stk |= F_IF;
0326       else
0327         *stk &= ~F_IF;
0328     }
0329 #if DEBUG_VM86 > 3
0330     com1_printf ("  PUSHF New SS:SP = %.04X:%.04X wrote FL = %.08X\n",
0331                  LIN32_TO_FP (stk).segm, LIN32_TO_FP (stk).offs,
0332                  *((uint32 *) stk) & (o32 ? ~0 : 0xFFFF));
0333 #endif
0334     /* write new CS:IP to guest */
0335     vmwrite (LIN32_TO_FP (eip + 1).offs, VMXENC_GUEST_RIP);
0336     vmwrite (LIN32_TO_FP (eip + 1).segm, VMXENC_GUEST_CS_SEL);
0337     vmwrite ((LIN32_TO_FP (eip + 1).segm) << 4, VMXENC_GUEST_CS_BASE);
0338     /* write new SS:SP to guest */
0339     vmwrite (LIN32_TO_FP (stk).offs, VMXENC_GUEST_RSP);
0340     vmwrite (LIN32_TO_FP (stk).segm, VMXENC_GUEST_SS_SEL);
0341     vmwrite ((LIN32_TO_FP (stk).segm) << 4, VMXENC_GUEST_SS_BASE);
0342     return 0;                   /* continue guest */
0343   case 0x9D:                    /* POPF */
0344     {
0345       uint32 new_eflags;
0346       uint32 f_if = *stk & F_IF;
0347       if (o32) {                  /* 32-bit POPF */
0348         /* Basically, allow the guest to control the bits in the mask 0xDFF */
0349         new_eflags = *((uint32 *) stk) & 0xDFF;
0350         stk += 2;
0351       } else {                    /* 16-bit POPF */
0352         new_eflags = *stk & 0xDFF;
0353         stk++;
0354       }
0355       /* And take the other bit settings from eflags. */
0356       new_eflags |= (eflags & (~0xDFF));
0357       /* Set the virtual IF according to what the guest thinks is the IF. */
0358       if (f_if)
0359         new_eflags |= F_VIF;
0360       else
0361         new_eflags &= ~F_VIF;
0362       vmwrite (new_eflags, VMXENC_GUEST_RFLAGS);
0363 #if DEBUG_VM86 > 3
0364       com1_printf ("  POPF New SS:SP = %.04X:%.04X wrote FL = %.08X\n",
0365                    LIN32_TO_FP (stk).segm, LIN32_TO_FP (stk).offs,
0366                    new_eflags & (o32 ? ~0 : 0xFFFF));
0367 #endif
0368       /* Move IP to next instruction */
0369       inc_ip (eip, 1);
0370       /* write new SS:SP to guest */
0371       vmwrite (LIN32_TO_FP (stk).offs, VMXENC_GUEST_RSP);
0372       vmwrite (LIN32_TO_FP (stk).segm, VMXENC_GUEST_SS_SEL);
0373       vmwrite ((LIN32_TO_FP (stk).segm) << 4, VMXENC_GUEST_SS_BASE);
0374       return 0;           /* continue guest */
0375     }
0376   case 0xCF:                    /* IRET */
0377     {
0378       /* stk[0] = IP; stk[1] = CS; stk[2] = FLAGS */
0379       uint32 new_eflags = stk[2] & 0xDFF;
0380       uint32 f_if = stk[2] & F_IF;
0381       vm86_farptr new_eip = { .segm = stk[1], .offs = stk[0] };
0382 
0383       /* And take the other bit settings from eflags. */
0384       new_eflags |= (eflags & (~0xDFF));
0385       /* Set the virtual IF according to what the guest thinks is the IF. */
0386       if (f_if)
0387         new_eflags |= F_VIF;
0388       else
0389         new_eflags &= ~F_VIF;
0390 
0391       /* write new CS:IP to guest */
0392       vmwrite (new_eip.offs, VMXENC_GUEST_RIP);
0393       vmwrite (new_eip.segm, VMXENC_GUEST_CS_SEL);
0394       vmwrite (new_eip.segm << 4, VMXENC_GUEST_CS_BASE);
0395       /* write new FLAGS to guest */
0396       vmwrite (new_eflags, VMXENC_GUEST_RFLAGS);
0397       /* write new SS:SP to guest */
0398       stk += 3;
0399       vmwrite (LIN32_TO_FP (stk).offs, VMXENC_GUEST_RSP);
0400       vmwrite (LIN32_TO_FP (stk).segm, VMXENC_GUEST_SS_SEL);
0401       vmwrite ((LIN32_TO_FP (stk).segm) << 4, VMXENC_GUEST_SS_BASE);
0402 #if DEBUG_VM86 > 3
0403       com1_printf ("  IRET to CS:IP = %.04X:%.04X  SS:SP = %.04X:%.04X  FL = %.08X\n",
0404                    new_eip.segm, new_eip.offs,
0405                    LIN32_TO_FP (stk).segm, LIN32_TO_FP (stk).offs,
0406                    new_eflags);
0407 #endif
0408       return 0;                 /* continue guest */
0409     }
0410   case 0xFA:                    /* CLI */
0411 #if DEBUG_VM86 > 3
0412     com1_printf ("  CLI\n");
0413 #endif
0414     eflags &= ~F_VIF;           /* Clear the virtual IF instead */
0415     /* write new CS:IP to guest */
0416     vmwrite (LIN32_TO_FP (eip + 1).offs, VMXENC_GUEST_RIP);
0417     vmwrite (LIN32_TO_FP (eip + 1).segm, VMXENC_GUEST_CS_SEL);
0418     vmwrite ((LIN32_TO_FP (eip + 1).segm) << 4, VMXENC_GUEST_CS_BASE);
0419     vmwrite (eflags, VMXENC_GUEST_RFLAGS);
0420     return 0;                   /* continue guest */
0421   case 0xFB:                    /* STI */
0422 #if DEBUG_VM86 > 3
0423     com1_printf ("  STI\n");
0424 #endif
0425     eflags |= F_VIF;            /* Set the virtual IF instead */
0426     /* write new CS:IP to guest */
0427     vmwrite (LIN32_TO_FP (eip + 1).offs, VMXENC_GUEST_RIP);
0428     vmwrite (LIN32_TO_FP (eip + 1).segm, VMXENC_GUEST_CS_SEL);
0429     vmwrite ((LIN32_TO_FP (eip + 1).segm) << 4, VMXENC_GUEST_CS_BASE);
0430     vmwrite (eflags, VMXENC_GUEST_RFLAGS);
0431     return 0;                   /* continue guest */
0432   case 0xE4:                    /* inb imm8, %al */
0433   case 0xE5:                    /* inw imm8, %ax */
0434   case 0xEC:                    /* inb %dx, %al */
0435   case 0xED:                    /* inw %dx, %ax */
0436   case 0x6C:                    /* insb %dx, m8 */
0437   case 0x6D:                    /* insw %dx, m16 */
0438     {
0439       uint16 port;
0440       uint8 isize = 1;
0441 
0442       if (*eip == 0xE4 || *eip == 0xE5) {
0443         /* Port specified as imm8 */
0444         port = eip[1];
0445         isize++;
0446       } else
0447         /* Port specified in %dx register */
0448         port = vm->guest_regs.edx & 0xFFFF;
0449 
0450       if (o32 &&                /* o32 prefix indicate double-words */
0451           (*eip == 0xE5 ||      /* inl imm8, %eax */
0452            *eip == 0xED)) {     /* inl %dx, %eax */
0453         /* 32-bit value */
0454         vm->guest_regs.eax = inl(port);
0455         inc_ip (eip, isize);
0456 #if DEBUG_VM86 > 3
0457         com1_printf ("  %.08X = inl (%.04X)\n", vm->guest_regs.eax, port);
0458 #endif
0459         return 0;               /* continue guest */
0460       } else if (*eip == 0xE5 || *eip == 0xED) {
0461         /* 16-bit value */
0462         vm->guest_regs.eax |= 0xFFFF & inw (port);
0463         inc_ip (eip, isize);
0464 #if DEBUG_VM86 > 3
0465         com1_printf ("  %.04X = inw (%.04X)\n", vm->guest_regs.eax & 0xFFFF, port);
0466 #endif
0467         return 0;               /* continue guest */
0468       } else if (*eip == 0xE4 || *eip == 0xEC) {
0469         /* 8-bit value */
0470         vm->guest_regs.eax |= 0xFF & inb (port);
0471         inc_ip (eip, isize);
0472 #if DEBUG_VM86 > 3
0473         com1_printf ("  %.02X = inb (%.04X)\n", vm->guest_regs.eax & 0xFF, port);
0474 #endif
0475         return 0;               /* continue guest */
0476       } else {
0477         uint8 *dst;
0478         uint16 cx = 1;
0479         /* String port input */
0480 
0481         if (a32) {
0482           /* 32-bit address */
0483           dst = (uint8 *) vm->guest_regs.edi; /* Q: Does this need to
0484                                                * examine the GDT? */
0485         } else {
0486           /* 16-bit address */
0487           dst = REAL_TO_LIN32 (vmread (VMXENC_GUEST_ES_SEL),
0488                                vm->guest_regs.edi & 0xFFFF,
0489                                uint8);
0490         }
0491 
0492         if (rep) {
0493           cx = vm->guest_regs.ecx & 0xFFFF;
0494         }
0495 
0496         if (o32 && *eip == 0x6D) { /* insl %dx, m32 */
0497           /* Transfer double-words */
0498           if (eflags & F_DF)
0499             insl_rev (port, dst, cx);
0500           else
0501             insl (port, dst, cx);
0502 #if DEBUG_VM86 > 3
0503           com1_printf ("  insl (%.04X, %p, %.04X) DF=%.01X\n",
0504                        port, dst, cx, !!(eflags & F_DF));
0505 #endif
0506         } else if (*eip == 0x6D) { /* insw %dx, m16 */
0507           /* Transfer words */
0508           if (eflags & F_DF)
0509             insw_rev (port, dst, cx);
0510           else
0511             insw (port, dst, cx);
0512 #if DEBUG_VM86 > 3
0513           com1_printf ("  insw (%.04X, %p, %.04X) DF=%.01X\n",
0514                        port, dst, cx, !!(eflags & F_DF));
0515 #endif
0516 
0517         } else if (*eip == 0x6C) { /* insb %dx, m8 */
0518           /* Transfer bytes */
0519           if (eflags & F_DF)
0520             insb_rev (port, dst, cx);
0521           else
0522             insb (port, dst, cx);
0523 #if DEBUG_VM86 > 3
0524           com1_printf ("  insb (%.04X, %p, %.04X) DF=%.01X\n",
0525                        port, dst, cx, !!(eflags & F_DF));
0526 #endif
0527         }
0528 
0529         inc_ip (eip, isize);
0530         return 0;               /* continue guest */
0531       }
0532       break;
0533     }
0534   case 0xE6:                    /* outb %al, imm8 */
0535   case 0xE7:                    /* outw %ax, imm8 */
0536   case 0xEE:                    /* outb %al, %dx */
0537   case 0xEF:                    /* outw %ax, %dx */
0538   case 0x6E:                    /* outsb m8, %dx */
0539   case 0x6F:                    /* outsw m16, %dx */
0540     {
0541       uint16 port;
0542       uint8 isize = 1;
0543 
0544       if (*eip == 0xE6 || *eip == 0xE7) {
0545         /* Port specified as imm8 */
0546         port = eip[1];
0547         isize++;
0548       } else
0549         /* Port specified in %dx register */
0550         port = vm->guest_regs.edx & 0xFFFF;
0551 
0552       if (o32 &&                /* o32 prefix indicates double-words */
0553           (*eip == 0xE7 ||      /* outl %eax, imm8 */
0554            *eip == 0xEF)) {     /* outl %eax, %dx */
0555         /* 32-bit value */
0556         outl (vm->guest_regs.eax, port);
0557         inc_ip (eip, isize);
0558 #if DEBUG_VM86 > 3
0559         com1_printf ("  outl (%.08X, %.04X)\n", vm->guest_regs.eax, port);
0560 #endif
0561         return 0;               /* continue guest */
0562       } else if (*eip == 0xE7 || *eip == 0xEF) {
0563         /* 16-bit value */
0564         outw (vm->guest_regs.eax & 0xFFFF, port);
0565         inc_ip (eip, isize);
0566 #if DEBUG_VM86 > 3
0567         com1_printf ("  outw (%.04X, %.04X)\n", vm->guest_regs.eax & 0xFFFF, port);
0568 #endif
0569         return 0;               /* continue guest */
0570       } else if (*eip == 0xE6 || *eip == 0xEE) {
0571         /* 8-bit value */
0572         outb (vm->guest_regs.eax & 0xFF, port);
0573         inc_ip (eip, isize);
0574 #if DEBUG_VM86 > 3
0575         com1_printf ("  outb (%.02X, %.04X)\n", vm->guest_regs.eax & 0xFF, port);
0576 #endif
0577         return 0;               /* continue guest */
0578       } else {
0579         uint8 *src;
0580         uint16 cx = 1;
0581         /* String port output */
0582 
0583         if (a32) {
0584           /* 32-bit address */
0585           src = (uint8 *) vm->guest_regs.esi; /* Q: Does this need to
0586                                                * examine the GDT? */
0587         } else {
0588           /* 16-bit address */
0589           src = REAL_TO_LIN32 (vmread (VMXENC_GUEST_DS_SEL),
0590                                vm->guest_regs.esi & 0xFFFF,
0591                                uint8);
0592         }
0593 
0594         if (rep) {
0595           cx = vm->guest_regs.ecx & 0xFFFF;
0596         }
0597 
0598         if (o32 && *eip == 0x6F) { /* outsl m32, %dx */
0599           /* Transfer double-words */
0600           if (eflags & F_DF)
0601             outsl_rev (port, src, cx);
0602           else
0603             outsl (port, src, cx);
0604 #if DEBUG_VM86 > 3
0605           com1_printf ("  outsl (%.04X, %p, %.04X) DF=%.01X\n",
0606                        port, src, cx, !!(eflags & F_DF));
0607 #endif
0608         } else if (*eip == 0x6F) { /* outsw m16, %dx */
0609           /* Transfer words */
0610           if (eflags & F_DF)
0611             outsw_rev (port, src, cx);
0612           else
0613             outsw (port, src, cx);
0614 #if DEBUG_VM86 > 3
0615           com1_printf ("  outsw (%.04X, %p, %.04X) DF=%.01X\n",
0616                        port, src, cx, !!(eflags & F_DF));
0617 #endif
0618 
0619         } else if (*eip == 0x6E) { /* outsb m8, %dx */
0620           /* Transfer bytes */
0621           if (eflags & F_DF)
0622             outsb_rev (port, src, cx);
0623           else
0624             outsb (port, src, cx);
0625 #if DEBUG_VM86 > 3
0626           com1_printf ("  outsb (%.04X, %p, %.04X) DF=%.01X\n",
0627                        port, src, cx, !!(eflags & F_DF));
0628 #endif
0629         }
0630 
0631         inc_ip (eip, isize);
0632         return 0;               /* continue guest */
0633       }
0634 
0635       break;
0636     }
0637   case 0xF4:                    /* HLT */
0638 #if DEBUG_VM86 > 3
0639     com1_printf ("  HLT\n");
0640 #endif
0641     return -1;
0642 
0643 
0644   case 0x0F:                    /* ESCAPE to 2-byte opcodes */
0645     eip++;
0646     switch (*eip) {
0647     case 0x01:                  /* LGDT / LIDT */
0648       {
0649         uint8 reg_op, disp_size, rm;
0650         uint16 limit;
0651         uint32 disp, base;
0652         bool has_sib;
0653         eip++;
0654         decode_modrm_32 (eip, &reg_op, &has_sib, &rm, &disp_size, &disp);
0655         if (reg_op == 2 &&      /* LGDT */
0656             a32 && o32) {
0657           /* example: 67660F011530910000 o32 lgdt [dword 0x9130]  */
0658           if (rm == 5) {
0659             /* disp is the address of the gdt ptr */
0660             limit = *((uint16 *) disp);
0661             base = *((uint32 *) (disp + 2));
0662           } else {
0663             limit = 0;
0664             base = 0;
0665           }
0666 #if DEBUG_VM86 > 1
0667           com1_printf ("  LGDT base=%.08X limit=%.04X\n", base, limit);
0668 #endif
0669           vmwrite (base, VMXENC_GUEST_GDTR_BASE);
0670           vmwrite (limit, VMXENC_GUEST_GDTR_LIMIT);
0671           inc_ip (eip, 1 + (has_sib ? 1 : 0) + disp_size);
0672           return 0;               /* continue guest */
0673         } else if (reg_op == 3) { /* LIDT */
0674 #if DEBUG_VM86 > 1
0675           com1_printf ("  LIDT\n");
0676 #endif
0677         }
0678         break;
0679       }
0680 #define VM86_CR0_MASK (~0x80000001)
0681     case 0x20:                  /* MOVL %CR0, reg */
0682       {
0683         uint8 reg_op, disp_size, rm;
0684         uint32 disp;
0685         bool has_sib;
0686         eip++;
0687         decode_modrm_32 (eip, &reg_op, &has_sib, &rm, &disp_size, &disp);
0688 #if DEBUG_VM86 > 1
0689         com1_printf ("  MOVL %%CR0, %%%s  CR0=%.08X\n",
0690                      VM_REG_NAME (rm), vmread (VMXENC_GUEST_CR0) & (VM86_CR0_MASK));
0691 #endif
0692         VM_REG (rm) = (vmread (VMXENC_GUEST_CR0) & VM86_CR0_MASK);
0693         inc_ip (eip, 1);
0694         return 0;               /* continue guest */
0695       }
0696     case 0x22:                  /* MOVL reg, %CR0 */
0697       {
0698         uint8 reg_op, disp_size, rm;
0699         uint32 disp, new_cr0;
0700         bool has_sib;
0701 
0702         eip++;
0703         decode_modrm_32 (eip, &reg_op, &has_sib, &rm, &disp_size, &disp);
0704         new_cr0 = VM_REG (rm);
0705         if (new_cr0 & 0x1) {
0706           /* Guest has decided to enable P-mode, therefore we must
0707            * disable our virtual real-mode emulation. */
0708           vm->realmode = FALSE;
0709           vmwrite (vmread (VMXENC_GUEST_RFLAGS) & (~F_VM), VMXENC_GUEST_RFLAGS);
0710           if (pick_segment_regs () < 0) {
0711 #if DEBUG_VM86 > 1
0712             com1_printf ("  failed to pick segment registers.\n");
0713 #endif
0714             return -1;
0715           }
0716 
0717           /* Technically it doesn't have to be this way, but let's
0718            * assume for now that a FAR JUMP immediately follows any
0719            * enabling of P-mode. */
0720           eip++;
0721           if (eip[0] == 0x66 && eip[1] == 0xEA) {
0722             uint32 offs = *((uint32 *) (eip + 2));
0723             uint16 segm = *((uint16 *) (eip + 6));
0724             descriptor *ad = (descriptor *) vmread (VMXENC_GUEST_GDTR_BASE);
0725             uint32 cs_access = ACCESS (ad[segm >> 3]);
0726             uint32 cs_base =
0727               ad[segm >> 3].pBase0 |
0728               (ad[segm >> 3].pBase1 << 8) |
0729               (ad[segm >> 3].pBase2 << 16);
0730             uint32 cs_limit = (ad[segm >> 3].uLimit0 | (ad[segm >> 3].uLimit1 << 16));
0731             if (ad[segm >> 3].fGranularity) {
0732               cs_limit <<= 12;
0733               cs_limit |= 0xFFF;
0734             }
0735             /* Jump to new offset and set CS. */
0736             vmwrite (offs, VMXENC_GUEST_RIP);
0737             vmwrite (segm, VMXENC_GUEST_CS_SEL);
0738             vmwrite (cs_access, VMXENC_GUEST_CS_ACCESS);
0739             vmwrite (cs_base, VMXENC_GUEST_CS_BASE);
0740             vmwrite (cs_limit, VMXENC_GUEST_CS_LIMIT);
0741 #if DEBUG_VM86 > 1
0742             com1_printf ("  P-mode enabled: FAR JUMP to %.04X:%p"
0743                          " access=%.02X base=%p limit=%p\n",
0744                          segm, offs, cs_access, cs_base, cs_limit);
0745 #endif
0746           } else {
0747 #if DEBUG_VM86 > 1
0748             com1_printf ("  invalid P-mode toggle: not followed by FAR JUMP.\n");
0749 #endif
0750             return -1;
0751           }
0752 
0753         } else
0754           inc_ip (eip, 1);
0755         new_cr0 |= ~VM86_CR0_MASK;
0756         vmwrite (new_cr0, VMXENC_GUEST_CR0);
0757 #if DEBUG_VM86 > 1
0758         com1_printf ("  MOVL %%%s, %%CR0  new CR0=%.08X CS=%.04X GDTR=%.08X:%.04X\n",
0759                      VM_REG_NAME (rm), new_cr0, vmread (VMXENC_GUEST_CS_SEL),
0760                      vmread (VMXENC_GUEST_GDTR_BASE), vmread (VMXENC_GUEST_GDTR_LIMIT));
0761 #endif
0762 
0763         return 0;               /* continue guest */
0764       }
0765     }
0766 
0767   default:
0768 #if DEBUG_VM86 > 1
0769     com1_printf ("  Unknown opcode %.02X\n", eip[0]);
0770 #endif
0771     break;
0772   }
0773 
0774   return -1;
0775 }
0776 #undef ACCESS
0777 
0778 /*
0779  * Local Variables:
0780  * indent-tabs-mode: nil
0781  * mode: C
0782  * c-file-style: "gnu"
0783  * c-basic-offset: 2
0784  * End:
0785  */
0786 
0787 /* vi: set et sw=2 sts=2: */