diff options
author | Avi Kivity <avi@qumranet.com> | 2007-11-20 13:15:52 +0200 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2008-01-30 17:53:09 +0200 |
commit | 6226686954c4cce3d63ffe1777e60360fcbf0b83 (patch) | |
tree | 900614019cbc1c1cf4039d81bfa198624c7316cd | |
parent | 93a0039c8d93074d5f92dfb69f6a7d453905d002 (diff) |
KVM: x86 emulator: prefetch up to 15 bytes of the instruction executed
Instead of fetching one byte at a time, prefetch 15 bytes (or until the next
page boundary) to avoid guest page table walks.
Signed-off-by: Avi Kivity <avi@qumranet.com>
-rw-r--r-- | drivers/kvm/x86_emulate.c | 38 | ||||
-rw-r--r-- | drivers/kvm/x86_emulate.h | 7 |
2 files changed, 43 insertions, 2 deletions
diff --git a/drivers/kvm/x86_emulate.c b/drivers/kvm/x86_emulate.c index 8e2162fc6f7..6e7f774d175 100644 --- a/drivers/kvm/x86_emulate.c +++ b/drivers/kvm/x86_emulate.c @@ -414,8 +414,7 @@ static u16 twobyte_table[256] = { /* Fetch next part of the instruction being emulated. */ #define insn_fetch(_type, _size, _eip) \ ({ unsigned long _x; \ - rc = ops->read_std((unsigned long)(_eip) + ctxt->cs_base, &_x, \ - (_size), ctxt->vcpu); \ + rc = do_insn_fetch(ctxt, ops, (_eip), &_x, (_size)); \ if (rc != 0) \ goto done; \ (_eip) += (_size); \ @@ -446,6 +445,41 @@ static u16 twobyte_table[256] = { register_address_increment(c->eip, rel); \ } while (0) +static int do_fetch_insn_byte(struct x86_emulate_ctxt *ctxt, + struct x86_emulate_ops *ops, + unsigned long linear, u8 *dest) +{ + struct fetch_cache *fc = &ctxt->decode.fetch; + int rc; + int size; + + if (linear < fc->start || linear >= fc->end) { + size = min(15UL, PAGE_SIZE - offset_in_page(linear)); + rc = ops->read_std(linear, fc->data, size, ctxt->vcpu); + if (rc) + return rc; + fc->start = linear; + fc->end = linear + size; + } + *dest = fc->data[linear - fc->start]; + return 0; +} + +static int do_insn_fetch(struct x86_emulate_ctxt *ctxt, + struct x86_emulate_ops *ops, + unsigned long eip, void *dest, unsigned size) +{ + int rc = 0; + + eip += ctxt->cs_base; + while (size--) { + rc = do_fetch_insn_byte(ctxt, ops, eip++, dest++); + if (rc) + return rc; + } + return 0; +} + /* * Given the 'reg' portion of a ModRM byte, and a register block, return a * pointer into the block that addresses the relevant register. diff --git a/drivers/kvm/x86_emulate.h b/drivers/kvm/x86_emulate.h index a62bf14bbf8..4603b2bf348 100644 --- a/drivers/kvm/x86_emulate.h +++ b/drivers/kvm/x86_emulate.h @@ -108,6 +108,12 @@ struct operand { unsigned long val, orig_val, *ptr; }; +struct fetch_cache { + u8 data[15]; + unsigned long start; + unsigned long end; +}; + struct decode_cache { u8 twobyte; u8 b; @@ -130,6 +136,7 @@ struct decode_cache { u8 use_modrm_ea; unsigned long modrm_ea; unsigned long modrm_val; + struct fetch_cache fetch; }; struct x86_emulate_ctxt { |