diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2011-07-22 14:39:51 +0930 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2011-07-22 14:39:51 +0930 |
commit | 996ba96a97f7406052486682846d68935a60e986 (patch) | |
tree | f02f603742129314a497d3406b02a7f929893d9c | |
parent | 8d431f41603acff8a20cf5df99bc8958c91879c1 (diff) | |
download | linux-3.10-996ba96a97f7406052486682846d68935a60e986.tar.gz linux-3.10-996ba96a97f7406052486682846d68935a60e986.tar.bz2 linux-3.10-996ba96a97f7406052486682846d68935a60e986.zip |
lguest: Fix in/out emulation
We were blatting too much of the register. Linux didn't care, but in
theory it might.
Reported-by: Jonas Maebe <jonas.maebe@elis.ugent.be>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r-- | drivers/lguest/x86/core.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 3b9b810cbf2..65af42f2d59 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -269,7 +269,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu) static int emulate_insn(struct lg_cpu *cpu) { u8 insn; - unsigned int insnlen = 0, in = 0, shift = 0; + unsigned int insnlen = 0, in = 0, small_operand = 0; /* * The eip contains the *virtual* address of the Guest's instruction: * walk the Guest's page tables to find the "physical" address. @@ -300,11 +300,10 @@ static int emulate_insn(struct lg_cpu *cpu) } /* - * 0x66 is an "operand prefix". It means it's using the upper 16 bits - * of the eax register. + * 0x66 is an "operand prefix". It means a 16, not 32 bit in/out. */ if (insn == 0x66) { - shift = 16; + small_operand = 1; /* The instruction is 1 byte so far, read the next byte. */ insnlen = 1; insn = lgread(cpu, physaddr + insnlen, u8); @@ -340,11 +339,14 @@ static int emulate_insn(struct lg_cpu *cpu) * traditionally means "there's nothing there". */ if (in) { - /* Lower bit tells is whether it's a 16 or 32 bit access */ - if (insn & 0x1) - cpu->regs->eax = 0xFFFFFFFF; - else - cpu->regs->eax |= (0xFFFF << shift); + /* Lower bit tells means it's a 32/16 bit access */ + if (insn & 0x1) { + if (small_operand) + cpu->regs->eax |= 0xFFFF; + else + cpu->regs->eax = 0xFFFFFFFF; + } else + cpu->regs->eax |= 0xFF; } /* Finally, we've "done" the instruction, so move past it. */ cpu->regs->eip += insnlen; |