This patch provides a way to establish an "identity" guest which has a 1:1 gva->hva translation. This allows the host to switch to guest mode, call a function in the same address space, and return. Because long mode virtual addresses are 47 bits long, and some hosts have smaller physical addresses, we target 32-bit mode only. On x86_64 the code needs to be run with 'setarch i386 -3' to limit the address space to 3GB, so the address space occupied by the local APIC is left unused. Signed-off-by: Avi Kivity <avi@xxxxxxxxxx> --- api/identity.cc | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ api/identity.h | 28 ++++++++++++++++++ config-x86-common.mak | 2 + 3 files changed, 106 insertions(+), 0 deletions(-) create mode 100644 api/identity.cc create mode 100644 api/identity.h diff --git a/api/identity.cc b/api/identity.cc new file mode 100644 index 0000000..8e86db1 --- /dev/null +++ b/api/identity.cc @@ -0,0 +1,76 @@ + +#include "identity.h" +#include <stdio.h> + +namespace identity { + +typedef unsigned long ulong; + +void setup_vm(kvm::vm& vm) +{ + vm.set_memory_region(0, NULL, 0, 3UL << 30); + vm.set_tss_addr(3UL << 30); +} + +void vcpu::setup_sregs() +{ + kvm_sregs sregs = { }; + kvm_segment dseg = { }; + dseg.base = 0; dseg.limit = -1U; dseg.type = 3; dseg.present = 1; + dseg.dpl = 3; dseg.db = 1; dseg.s = 1; dseg.l = 0; dseg.g = 1; + kvm_segment cseg = dseg; + cseg.type = 11; + + sregs.cs = cseg; asm ("mov %%cs, %0" : "=rm"(sregs.cs.selector)); + sregs.ds = dseg; asm ("mov %%ds, %0" : "=rm"(sregs.ds.selector)); + sregs.es = dseg; asm ("mov %%es, %0" : "=rm"(sregs.es.selector)); + sregs.fs = dseg; asm ("mov %%fs, %0" : "=rm"(sregs.fs.selector)); + sregs.gs = dseg; asm ("mov %%gs, %0" : "=rm"(sregs.gs.selector)); + sregs.ss = dseg; asm ("mov %%ss, %0" : "=rm"(sregs.ss.selector)); + + uint32_t gsbase; + asm ("mov %%gs:0, %0" : "=r"(gsbase)); + sregs.gs.base = gsbase; + + sregs.tr.base = reinterpret_cast<ulong>(&*_stack.begin()); + sregs.tr.type = 11; + sregs.tr.s = 0; + sregs.tr.present = 1; + + sregs.cr0 = 0x11; /* PE, ET, !PG */ + sregs.cr4 = 0; + sregs.efer = 0; + sregs.apic_base = 0xfee00000; + _vcpu.set_sregs(sregs); +} + +void vcpu::thunk(vcpu* zis) +{ + zis->_guest_func(); + asm volatile("outb %%al, %%dx" : : "a"(0), "d"(0)); +} + +void vcpu::setup_regs() +{ + kvm_regs regs = {}; + regs.rflags = 0x3202; + regs.rsp = reinterpret_cast<ulong>(&*_stack.end()); + regs.rsp &= ~15UL; + ulong* sp = reinterpret_cast<ulong *>(regs.rsp); + *--sp = reinterpret_cast<ulong>((char*)this); + *--sp = 0; + regs.rsp = reinterpret_cast<ulong>(sp); + regs.rip = reinterpret_cast<ulong>(&vcpu::thunk); + printf("rip %llx\n", regs.rip); + _vcpu.set_regs(regs); +} + +vcpu::vcpu(kvm::vcpu& vcpu, boost::function<void ()> guest_func, + unsigned long stack_size) + : _vcpu(vcpu), _guest_func(guest_func), _stack(stack_size) +{ + setup_sregs(); + setup_regs(); +} + +} diff --git a/api/identity.h b/api/identity.h new file mode 100644 index 0000000..025177a --- /dev/null +++ b/api/identity.h @@ -0,0 +1,28 @@ +#ifndef API_IDENTITY_H +#define API_IDENTITY_H + +#include "kvmxx.h" +#include <boost/function.hpp> +#include <vector> + +namespace identity { + +void setup_vm(kvm::vm& vm); + +class vcpu { +public: + vcpu(kvm::vcpu& vcpu, boost::function<void ()> guest_func, + unsigned long stack_size = 256 * 1024); +private: + static void thunk(vcpu* vcpu); + void setup_regs(); + void setup_sregs(); +private: + kvm::vcpu& _vcpu; + boost::function<void ()> _guest_func; + std::vector<char> _stack; +}; + +} + +#endif diff --git a/config-x86-common.mak b/config-x86-common.mak index b541c1c..0f3387b 100644 --- a/config-x86-common.mak +++ b/config-x86-common.mak @@ -79,3 +79,5 @@ arch_clean: $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o -include $(TEST_DIR)/.*.d lib/.*.d lib/x86/.*.d + +api/%.o: CFLAGS += -m32 \ No newline at end of file -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html