diff --git a/repos/os/run/vmm_x86.run b/repos/os/run/vmm_x86.run new file mode 100644 index 0000000000..7197c3cc48 --- /dev/null +++ b/repos/os/run/vmm_x86.run @@ -0,0 +1,206 @@ +# +# \brief rudimentary x86 virtual-machine monitor interface test +# \author Alexander Boettcher +# \date 2018-08-26 +# + +assert_spec x86 + +if { [get_cmd_switch --autopilot] } { + # nightly x86 32bit test machine has no vmx support + if {[have_spec x86_32] && ![have_include "power_on/qemu"]} { + puts "\n Run script is not supported on this platform. \n"; + exit 0 + } + + if {[have_include "power_on/qemu"] && [have_spec sel4]} { + puts "\n Run script is not supported on this platform. \n"; + exit 0 + } + + if {[have_spec nova]} { + } else { + puts "\n Run script is not supported on this platform. \n"; + exit 0 + } +} + +set build_components { + core init + timer + test/vmm_x86 +} +build $build_components +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + +} + +set boot_modules { + core ld.lib.so init + timer + test-vmm_x86 +} +build_boot_image $boot_modules + +append qemu_args " -cpu phenom -smp 2" +append qemu_args " -nographic " + +#run_genode_until {.*vcpu 1 : 7\. vm exit -.*\n} 20 +#run_genode_until forever +run_genode_until "vmm test finished" 25 + +set output_saved $output +grep_output {^\[init -> vmm\] vcpu 0 :.*} +set output_0 $output +unify_output {\[init -> vmm\] vcpu 0 : [0-9]+} "vcpu 0 : XX" +# transform INTEL HLT to AMD HLT number +unify_output "vcpu 0 : XX. vm exit - reason 0xc" "vcpu 0 : XX. vm exit - reason 0x78" +# AMD with NPT +unify_output "vcpu 0 : XX. vm exit - reason 0xfc handled by 'ep'" "" +# AMD w/o NPT +unify_output "vcpu 0 : XX. vm exit - reason 0x4e handled by 'ep'" "" +# Intel with EPT +unify_output "vcpu 0 : XX. vm exit - reason 0x30 handled by 'ep'" "" +unify_output "vcpu 0 : XX. vm exit - guest fault address: 0xfffffff0" "" +unify_output "vcpu 0 : XX. vm exit - resume vcpu" "" +trim_lines +set output_0 $output + +set output $output_saved +grep_output {^\[init -> vmm\] vcpu 1 :.*} +unify_output {\[init -> vmm\] vcpu 1 : [0-9]+} "vcpu 1 : XX" +# transform INTEL HLT to AMD HLT number +unify_output "vcpu 1 : XX. vm exit - reason 0xc" "vcpu 1 : XX. vm exit - reason 0x78" +# AMD with NPT +unify_output "vcpu 1 : XX. vm exit - reason 0xfc handled by 'ep'" "" +# AMD w/o NPT +unify_output "vcpu 1 : XX. vm exit - reason 0x4e handled by 'ep'" "" +# Intel with EPT +unify_output "vcpu 1 : XX. vm exit - reason 0x30 handled by 'ep'" "" +unify_output "vcpu 1 : XX. vm exit - guest fault address: 0xfffffff0" "" +unify_output "vcpu 1 : XX. vm exit - resume vcpu" "" +trim_lines +set output_1 $output + +set output $output_saved +grep_output {^\[init -> vmm\] vcpu 2 :.*} +unify_output {\[init -> vmm\] vcpu 2 : [0-9]+} "vcpu 2 : XX" +# transform INTEL HLT to AMD HLT number +unify_output "vcpu 2 : XX. vm exit - reason 0xc" "vcpu 2 : XX. vm exit - reason 0x78" +# AMD with NPT +unify_output "vcpu 2 : XX. vm exit - reason 0xfc handled by 'second ep'" "" +# AMD w/o NPT +unify_output "vcpu 2 : XX. vm exit - reason 0x4e handled by 'second ep'" "" +# Intel with EPT +unify_output "vcpu 2 : XX. vm exit - reason 0x30 handled by 'second ep'" "" +unify_output "vcpu 2 : XX. vm exit - guest fault address: 0xfffffff0" "" +unify_output "vcpu 2 : XX. vm exit - resume vcpu" "" +trim_lines +set output_2 $output + +set output $output_saved +grep_output {^\[init -> vmm\] vcpu 3 :.*} +unify_output {\[init -> vmm\] vcpu 3 : [0-9]+} "vcpu 3 : XX" +# transform INTEL HLT to AMD HLT number +unify_output "vcpu 3 : XX. vm exit - reason 0xc" "vcpu 3 : XX. vm exit - reason 0x78" +# AMD with NPT +unify_output "vcpu 3 : XX. vm exit - reason 0xfc handled by 'second ep'" "" +# AMD w/o NPT +unify_output "vcpu 3 : XX. vm exit - reason 0x4e handled by 'second ep'" "" +# Intel with EPT +unify_output "vcpu 3 : XX. vm exit - reason 0x30 handled by 'second ep'" "" +unify_output "vcpu 3 : XX. vm exit - guest fault address: 0xfffffff0" "" +unify_output "vcpu 3 : XX. vm exit - resume vcpu" "" +trim_lines +set output_3 $output + +puts "comparing output ..." + +puts $output_0 +set output $output_0 +compare_output_to { +[init -> vmm] vcpu 0 : created +vcpu 0 : XX. vm exit - reason 0xfe handled by 'ep' +vcpu 0 : XX. vm exit - reason 0x78 handled by 'ep' +vcpu 0 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff0 +} + +puts $output_1 +set output $output_1 +compare_output_to { +[init -> vmm] vcpu 1 : created +vcpu 1 : XX. vm exit - reason 0xfe handled by 'ep' +vcpu 1 : XX. vm exit - reason 0x78 handled by 'ep' +vcpu 1 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff0 +vcpu 1 : XX. vm exit - reason 0xff handled by 'ep' +vcpu 1 : XX. vm exit - due to pause() request - ip=0xfff0 +vcpu 1 : XX. vm exit - reason 0x78 handled by 'ep' +vcpu 1 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff0 +vcpu 1 : XX. vm exit - reason 0xff handled by 'ep' +vcpu 1 : XX. vm exit - due to pause() request - ip=0xfff0 +vcpu 1 : XX. vm exit - reason 0xff handled by 'ep' +vcpu 1 : XX. vm exit - due to pause() request - ip=0xfff2 +vcpu 1 : XX. vm exit - reason 0x78 handled by 'ep' +vcpu 1 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff4 +vcpu 1 : XX. vm exit - reason 0xff handled by 'ep' +vcpu 1 : XX. vm exit - due to pause() request - ip=0xfff4 +} + +puts $output_2 +set output $output_2 +compare_output_to { +[init -> vmm] vcpu 2 : created +vcpu 2 : XX. vm exit - reason 0xfe handled by 'second ep' +vcpu 2 : XX. vm exit - reason 0x78 handled by 'second ep' +vcpu 2 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff0 +vcpu 2 : XX. vm exit - reason 0xff handled by 'second ep' +vcpu 2 : XX. vm exit - due to pause() request - ip=0xfff0 +vcpu 2 : XX. vm exit - reason 0x78 handled by 'second ep' +vcpu 2 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff0 +vcpu 2 : XX. vm exit - reason 0xff handled by 'second ep' +vcpu 2 : XX. vm exit - due to pause() request - ip=0xfff0 +vcpu 2 : XX. vm exit - reason 0x78 handled by 'second ep' +vcpu 2 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff1 +vcpu 2 : XX. vm exit - reason 0xff handled by 'second ep' +vcpu 2 : XX. vm exit - due to pause() request - ip=0xfff1 +} + +puts $output_3 +set output $output_3 +compare_output_to { +[init -> vmm] vcpu 3 : created +vcpu 3 : XX. vm exit - reason 0xfe handled by 'second ep' +vcpu 3 : XX. vm exit - reason 0x78 handled by 'second ep' +vcpu 3 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff0 +} + +set output $output_saved +grep_output {(Error)} +compare_output_to {} + diff --git a/repos/os/src/test/vmm_x86/component.cc b/repos/os/src/test/vmm_x86/component.cc new file mode 100644 index 0000000000..1b3fc01558 --- /dev/null +++ b/repos/os/src/test/vmm_x86/component.cc @@ -0,0 +1,496 @@ +/* + * \brief VM session interface test for x86 + * \author Alexander Boettcher + * \date 2018-09-26 + * + */ + +/* + * Copyright (C) 2018-2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class Vm; + +namespace Intel_exit { + enum { + CPUID = 0x0a, + HLT = 0x0c, + INVALID_STATE = 0x21, + EPT = 0x30 + }; +} + +namespace Amd_exit { + enum { + PF = 0x4e, + HLT = 0x78, + TRIPLE_FAULT = 0x7f, + NPT = 0xfc + }; +} + +class Vcpu { + + private: + + Vm &_vm; + Genode::Vm_connection &_vm_con; + Genode::Vm_handler _handler; + Genode::Vm_session_client::Vcpu_id _vcpu; + Genode::Attached_dataspace _state_ds; + Genode::Vm_state &_state; + + bool _svm; + bool _vmx; + + /* test state start - some status information to steer the test */ + unsigned _exit_count { 0 }; + unsigned _pause_count { 0 }; + unsigned _timer_count { 0 }; + unsigned _pause_at_timer { 0 }; + enum { + INITIAL, + HALTED, + PAUSED, + UNKNOWN, + RUNNING + } _test_state { INITIAL }; + /* test state end */ + + void _handle_vm_exception(); + + void _cpu_init() + { + enum { INTEL_CTRL_PRIMARY_HLT = 1 << 7 }; + enum { INTEL_CTRL_SECOND_UG = 1 << 7 }; + enum { AMD_CTRL_SECOND_VMRUN = 1 << 0 }; + + /* http://www.sandpile.org/x86/initial.htm */ + + typedef Genode::Vm_state::Segment Segment; + typedef Genode::Vm_state::Range Range; + + _state = Genode::Vm_state {}; + + _state.flags.value(2); + _state.ip. value(0xfff0); + _state.cr0. value(0x10); + _state.cs. value(Segment{0xf000, 0x93, 0xffff, 0xffff0000}); + _state.ss. value(Segment{0, 0x93, _state.cs.value().limit, 0}); + _state.dx. value(0x600); + _state.es. value(Segment{0, _state.ss.value().ar, + _state.cs.value().limit, 0}); + _state.ds. value(Segment{0, _state.ss.value().ar, + _state.cs.value().limit, 0}); + _state.fs. value(Segment{0, _state.ss.value().ar, + _state.cs.value().limit, 0}); + _state.gs. value(Segment{0, _state.ss.value().ar, + _state.cs.value().limit, 0}); + _state.tr. value(Segment{0, 0x8b, 0xffff, 0}); + _state.ldtr. value(Segment{0, 0x1000, _state.tr.value().limit, 0}); + _state.gdtr. value(Range {0, 0xffff}); + _state.idtr. value(Range {0, _state.gdtr.value().limit}); + _state.dr7. value(0x400); + + if (_vmx) { + _state.ctrl_primary.value(INTEL_CTRL_PRIMARY_HLT); + /* required for seL4 */ + _state.ctrl_secondary.value(INTEL_CTRL_SECOND_UG); + } + if (_svm) { + /* required for native AMD hardware (!= Qemu) for NOVA */ + _state.ctrl_secondary.value(AMD_CTRL_SECOND_VMRUN); + } + } + + void _exit_config(Genode::Vm_state &state, unsigned exit) + { + /* touch the register state required for the specific vm exit */ + state.ip.value(0); + + if (exit == Intel_exit::EPT || exit == Amd_exit::NPT || + exit == Amd_exit::PF) + { + state.qual_primary.value(0); + state.qual_secondary.value(0); + } + } + + public: + + Vcpu(Genode::Entrypoint &ep, Genode::Vm_connection &vm_con, + Genode::Allocator &alloc, Genode::Env &env, Vm &vm, + bool svm, bool vmx) + : + _vm(vm), _vm_con(vm_con), + _handler(ep, *this, &Vcpu::_handle_vm_exception, &Vcpu::_exit_config), + /* construct vcpu */ + _vcpu(_vm_con.with_upgrade([&]() { + return _vm_con.create_vcpu(alloc, env, _handler); })), + /* get state of vcpu */ + _state_ds(env.rm(), _vm_con.cpu_state(_vcpu)), + _state(*_state_ds.local_addr()), + _svm(svm), _vmx(vmx) + { + Genode::log("vcpu ", _vcpu.id, " : created"); + } + + Genode::Vm_session_client::Vcpu_id id() const { return _vcpu; } + + void skip_instruction(unsigned bytes) + { + _state = Genode::Vm_state {}; + _state.ip.value(_state.ip.value() + bytes); + } + + /* + * state information and state requests to steer the test + */ + bool halted() const { + return _test_state == HALTED; } + bool paused_1st() const { + return _test_state == PAUSED && _pause_count == 1; } + bool paused_2nd() const { + return _test_state == PAUSED && _pause_count == 2; } + bool paused_3rd() const { + return _test_state == PAUSED && _pause_count == 3; } + bool paused_4th() const { + return _test_state == PAUSED && _pause_count == 4; } + + void break_endless_loop() + { + _pause_at_timer = _timer_count + 3; + } + bool pause_endless_loop() + { + if (_pause_at_timer == 0) + return false; + + if (_timer_count < _pause_at_timer) + return false; + + _pause_at_timer = 0; + return true; + } + + void claim_state_unknown() { _test_state = UNKNOWN; } + + void timer_triggered() { _timer_count ++; } +}; + +class Vm { + + private: + + enum { STACK_SIZE = 2*1024*sizeof(long) }; + + Genode::Vm_connection _vm_con; + Genode::Heap _heap; + bool _svm; + bool _vmx; + Genode::Entrypoint &_ep_first; /* running on first CPU */ + Genode::Entrypoint _ep_second; /* running on second CPU */ + Vcpu _vcpu0; + Vcpu _vcpu1; + Vcpu _vcpu2; + Vcpu _vcpu3; + Genode::Dataspace_capability _memory; /* guest memory */ + + /* just to trigger some events after some time */ + Timer::Connection _timer; + Genode::Signal_handler _timer_handler; + + void _handle_timer(); + + bool _cpu_name(char const * name) + { + using Genode::uint32_t; + + uint32_t cpuid = 0, edx = 0, ebx = 0, ecx = 0; + asm volatile ("cpuid" : "+a" (cpuid), "=d" (edx), "=b"(ebx), "=c"(ecx)); + + return ebx == *reinterpret_cast(name) && + edx == *reinterpret_cast(name + 4) && + ecx == *reinterpret_cast(name + 8); + } + + bool _amd() { return _cpu_name("AuthenticAMD"); } + bool _intel() { return _cpu_name("GenuineIntel"); } + + /* lookup which hardware assisted feature the CPU supports */ + bool _vm_feature(Genode::Env &env, char const *name) + { + try { + Genode::Attached_rom_dataspace info { env, "platform_info"}; + + return info.xml().sub_node("hardware").sub_node("features").attribute_value(name, false); + } catch (...) { } + + return false; + } + + public: + + Vm(Genode::Env &env) + : + _vm_con(env), + _heap(env.ram(), env.rm()), + _svm(_amd() && _vm_feature(env, "svm")), + _vmx(_intel() && _vm_feature(env, "vmx")), + _ep_first(env.ep()), + _ep_second(env, STACK_SIZE, "second ep", + env.cpu().affinity_space().location_of_index(1)), + _vcpu0(_ep_first, _vm_con, _heap, env, *this, _svm, _vmx), + _vcpu1(_ep_first, _vm_con, _heap, env, *this, _svm, _vmx), + _vcpu2(_ep_second, _vm_con, _heap, env, *this, _svm, _vmx), + _vcpu3(_ep_second, _vm_con, _heap, env, *this, _svm, _vmx), + _memory(env.ram().alloc(4096)), + _timer(env), + _timer_handler(_ep_first, *this, &Vm::_handle_timer) + { + if (!_svm && !_vmx) { + Genode::error("no SVM nor VMX support detected"); + throw 0; + } + + /* prepare guest memory with some instructions for testing */ + Genode::uint8_t * guest = env.rm().attach(_memory); +#if 0 + *(guest + 0xff0) = 0x0f; /* CPUID instruction */ + *(guest + 0xff1) = 0xa2; +#endif + *(guest + 0xff0) = 0xf4; /* HLT instruction */ + *(guest + 0xff1) = 0xf4; /* HLT instruction */ + *(guest + 0xff2) = 0xeb; /* JMP - endless loop to RIP */ + *(guest + 0xff3) = 0xfe; /* JMP of -2 byte (size of JMP inst) */ + *(guest + 0xff4) = 0xf4; /* HLT instruction */ + env.rm().detach(guest); + + Genode::log ("let vCPUs run - first EP"); + _vm_con.run(_vcpu0.id()); + _vm_con.run(_vcpu1.id()); + + Genode::log ("let vCPUs run - second EP"); + _vm_con.run(_vcpu2.id()); + _vm_con.run(_vcpu3.id()); + + _timer.sigh(_timer_handler); + _timer.trigger_periodic(1000 * 1000 /* in us */); + } + + Genode::Dataspace_capability handle_guest_memory_exit() + { + /* + * A real VMM would now have to lookup the right dataspace for + * the given guest physical region. This simple test has just one + * supported region ... + */ + return _memory; + } +}; + + +/** + * Handle timer events - used to trigger some pause/resume on vCPU for testing + */ +void Vm::_handle_timer() +{ + _vcpu0.timer_triggered(); + _vcpu1.timer_triggered(); + _vcpu2.timer_triggered(); + _vcpu3.timer_triggered(); + + /* + * We're running in context of _ep_first. Try to trigger remotely + * for vCPU2 (handled by _ep_second actually) pause/run. Remotely means + * that vCPU2 is not on the same physical CPU as _ep_first. + */ + if (_vcpu2.halted()) { + /* test to trigger a Genode signal even if we're already blocked */ + _vm_con.pause(_vcpu2.id()); + } + + if (_vcpu2.paused_1st()) { + Genode::log(Genode::Thread::myself()->name(), " : request resume of vcpu ", _vcpu2.id().id); + + /* continue after first paused state */ + _vm_con.run(_vcpu2.id()); + } else if (_vcpu2.paused_2nd()) { + Genode::log(Genode::Thread::myself()->name(), " : request resume of vcpu ", _vcpu2.id().id); + + /* skip over next hlt instructions after second paused state */ + _vcpu2.skip_instruction(1*1 /* 1x hlt instruction size */); + + /* reset state to unknown, otherwise we may enter this a second time */ + _vcpu2.claim_state_unknown(); + + /* the next instruction is again a hlt */ + _vm_con.run(_vcpu2.id()); + } + + /* + * pause/run for vCPU0 in context of right _ep_first - meaning both + * are on same physical CPU. + */ + if (_vcpu1.pause_endless_loop()) { + Genode::log("pause endless loop"); + /* guest in endless jmp loop - request to stop it asap */ + _vm_con.pause(_vcpu1.id()); + return; + } + + if (_vcpu1.halted()) { + Genode::log(Genode::Thread::myself()->name(), " : request pause of vcpu ", _vcpu1.id().id); + /* test to trigger a Genode signal even if we're already blocked */ + _vm_con.pause(_vcpu1.id()); + } + + if (_vcpu1.paused_1st()) { + Genode::log(Genode::Thread::myself()->name(), " : request resume (A) of vcpu ", _vcpu1.id().id); + + /* continue after first paused state */ + _vm_con.run(_vcpu1.id()); + } else if (_vcpu1.paused_2nd()) { + Genode::log(Genode::Thread::myself()->name(), " : request resume (B) of vcpu ", _vcpu1.id().id); + + /* skip over next 2 hlt instructions after second paused state */ + _vcpu1.skip_instruction(2*1 /* 2x hlt instruction size */); + + /* reset state to unknown, otherwise we may enter this a second time */ + _vcpu1.claim_state_unknown(); + + /* the next instruction is actually a jmp endless loop */ + _vm_con.run(_vcpu1.id()); + + /* request on the next timeout to stop the jmp endless loop */ + _vcpu1.break_endless_loop(); + } else if (_vcpu1.paused_3rd()) { + Genode::log(Genode::Thread::myself()->name(), " : request resume (C) of vcpu ", _vcpu1.id().id); + + _vcpu1.skip_instruction(1*2 /* 1x jmp endless loop size */); + _vm_con.run(_vcpu1.id()); + } else if (_vcpu1.paused_4th()) { + Genode::log("vmm test finished"); + } +} + +/** + * Handle VM exits ... + */ +void Vcpu::_handle_vm_exception() +{ + using namespace Genode; + + enum { VMEXIT_STARTUP = 0xfe, VMEXIT_PAUSED = 0xff }; + + unsigned const exit = _state.exit_reason; + + _state = Vm_state {}; + + _exit_count++; + + log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - ", + "reason ", Hex(exit), " handled by '", + Genode::Thread::myself()->name(), "'"); + + if (exit == VMEXIT_STARTUP) { + _cpu_init(); + } + + if (exit == Intel_exit::INVALID_STATE) { + error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " halting vCPU - invalid guest state"); + _test_state = UNKNOWN; + return; + } + + if (exit == Amd_exit::TRIPLE_FAULT) { + error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " halting vCPU - triple fault"); + _test_state = UNKNOWN; + return; + } + + if (exit == VMEXIT_PAUSED) { + log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " due to pause() request - ip=", Hex(_state.ip.value())); + _pause_count ++; + _test_state = PAUSED; + return; + } + + if (exit == Intel_exit::HLT || exit == Amd_exit::HLT) { + log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " halting vCPU - guest called HLT - ip=", Hex(_state.ip.value())); + _test_state = HALTED; + return; + } + + if (exit == Intel_exit::EPT || + exit == Amd_exit::NPT || exit == Amd_exit::PF) + { + addr_t const guest_fault_addr = _state.qual_secondary.value(); + addr_t const guest_map_addr = _state.qual_secondary.value() & ~0xFFFUL; + + log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " guest fault address: ", Hex(guest_fault_addr)); + + Dataspace_capability cap = _vm.handle_guest_memory_exit(); + if (!cap.valid()) { + error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " halting vCPU - guest memory lookup failed"); + _test_state = UNKNOWN; + /* no memory - we halt the vcpu */ + return; + } + if (guest_fault_addr != 0xfffffff0UL) { + error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " unknown guest fault address"); + return; + } + + _vm_con.attach(cap, guest_map_addr); + } + + if (_exit_count >= 5) { + error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - " + " halting vCPU - unknown state"); + _test_state = UNKNOWN; + return; + } + + log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - resume vcpu"); + + _test_state = RUNNING; + _vm_con.run(_vcpu); +} + + +class Vmm { + + private: + + Vm _vm; + + public: + + Vmm(Genode::Env &env) + : _vm(env) + { } +}; + +void Component::construct(Genode::Env & env) { static Vmm vmm(env); } diff --git a/repos/os/src/test/vmm_x86/target.mk b/repos/os/src/test/vmm_x86/target.mk new file mode 100644 index 0000000000..c5b3fe048b --- /dev/null +++ b/repos/os/src/test/vmm_x86/target.mk @@ -0,0 +1,4 @@ +TARGET = test-vmm_x86 +REQUIRES = x86 +SRC_CC = component.cc +LIBS = base diff --git a/tool/autopilot.list b/tool/autopilot.list index 8d4dd6b3ce..070ddb48e4 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -74,4 +74,5 @@ vbox5_win7_64_share verify vfs_import vmm +vmm_x86 microcode