nova: support transfer of IA32_TSC_AUX MSR for vCPUs

The 32-bit MSR is returned by rdtscp in ecx register and used to detect
the CPU ID the timestamp was taken on.

Issue #4314
This commit is contained in:
Alexander Boettcher 2021-11-02 14:59:15 +01:00 committed by Christian Helmuth
parent f4e52863c0
commit 533015b93e
8 changed files with 105 additions and 29 deletions

View File

@ -316,6 +316,7 @@ namespace Nova {
R8_R15 = 1U << 22, /* R8 .. R15 */
SYSCALL_SWAPGS = 1U << 23, /* SYSCALL and SWAPGS MSRs */
TPR = 1U << 24, /* TPR and TPR threshold */
TSC_AUX = 1U << 25, /* IA32_TSC_AUX used by rdtscp */
FPU = 1U << 31, /* FPU state */
IRQ = EFL | STA | INJ | TSC,
@ -590,7 +591,7 @@ namespace Nova {
mword_t reserved1;
#endif
} gdtr, idtr;
unsigned long long tsc_val, tsc_off;
unsigned long long tsc_val, tsc_off, tsc_aux;
} __attribute__((packed));
mword_t mr[(4096 - 4 * sizeof(mword_t)) / sizeof(mword_t)];
};

View File

@ -1 +1 @@
2ff8c9abf7389a2bdb7e0aa8aa4d2fec688c51af
b06f9132099cd798147f6951f27a5d3a17f28fa4

View File

@ -4,7 +4,7 @@ DOWNLOADS := nova.git
# r10 branch
URL(nova) := https://github.com/alex-ab/NOVA.git
REV(nova) := 5c64bba1ee59902eb2a4ce4abe4b867eaf085dac
REV(nova) := 825c1f94c82ae03f554c02b91430eca6983329da
DIR(nova) := src/kernel/nova
PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch))

View File

@ -31,7 +31,7 @@ CC_OPT += -DCONFIG_MEMORY_DYN_MIN=0x1c00000 \
CC_OPT_PIC :=
ifeq ($(filter-out $(SPECS),32bit),)
override CC_MARCH = -m32
CC_WARN += -Wframe-larger-than=96
CC_WARN += -Wframe-larger-than=104
CC_OPT += -mpreferred-stack-boundary=2 -mregparm=3
else
ifeq ($(filter-out $(SPECS),64bit),)

View File

@ -135,6 +135,7 @@ struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
mtd |= Nova::Mtd::INJ;
mtd |= Nova::Mtd::STA;
mtd |= Nova::Mtd::TSC;
mtd |= Nova::Mtd::TSC_AUX;
mtd |= Nova::Mtd::EFER;
mtd |= Nova::Mtd::PDPTE;
mtd |= Nova::Mtd::SYSCALL_SWAPGS;
@ -316,6 +317,10 @@ void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb, unsigned exit_reason,
state().tsc_offset.charge(utcb.tsc_off);
}
if (utcb.mtd & Nova::Mtd::TSC_AUX) {
state().tsc_aux.charge(utcb.tsc_aux);
}
if (utcb.mtd & Nova::Mtd::EFER) {
state().efer.charge(utcb.read_efer());
}
@ -508,6 +513,11 @@ void Nova_vcpu::_write_nova_state(Nova::Utcb &utcb)
utcb.tsc_off = state().tsc_offset.value();
}
if (state().tsc_aux.charged()) {
utcb.mtd |= Nova::Mtd::TSC_AUX;
utcb.tsc_aux = state().tsc_aux.value();
}
if (state().efer.charged()) {
utcb.mtd |= Nova::Mtd::EFER;
utcb.write_efer(state().efer.value());

View File

@ -145,6 +145,7 @@ class Genode::Vcpu_state
Register<uint64_t> tsc;
Register<uint64_t> tsc_offset;
Register<uint64_t> tsc_aux;
Register<addr_t> efer;

View File

@ -25,6 +25,9 @@ if { [get_cmd_switch --autopilot] } {
}
}
# ia32_tsc_aux with rdtscp
set test_rdtscp [have_spec nova]
set build_components {
core init
timer
@ -114,6 +117,8 @@ unify_output "vcpu 0 : XX. vm exit - reason 0x4e handled by 'ep'" ""
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" ""
# remove rdtscp output from unified output, some kernel don't support tsc_aux
unify_output {vcpu 0 : XX. vm exit - rdtscp.* host=0x[0-9]} ""
trim_lines
set output_0 $output
@ -130,6 +135,8 @@ unify_output "vcpu 1 : XX. vm exit - reason 0x4e handled by 'ep'" ""
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" ""
# remove rdtscp output from unified output, some kernel don't support tsc_aux
unify_output {vcpu 1 : XX. vm exit - rdtscp.* host=0x[0-9]} ""
trim_lines
set output_1 $output
@ -146,6 +153,8 @@ unify_output "vcpu 2 : XX. vm exit - reason 0x4e handled by 'second ep'" ""
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" ""
# remove rdtscp output from unified output, some kernel don't support tsc_aux
unify_output {vcpu 2 : XX. vm exit - rdtscp.* host=0x[0-9]} ""
trim_lines
set output_2 $output
@ -162,18 +171,44 @@ unify_output "vcpu 3 : XX. vm exit - reason 0x4e handled by 'second ep'" ""
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" ""
# remove rdtscp output from unified output, some kernel don't support tsc_aux
unify_output {vcpu 3 : XX. vm exit - rdtscp.* host=0x[0-9]} ""
trim_lines
set output_3 $output
if {$test_rdtscp} {
set output $output_saved
grep_output {^\[init -> vmm\] vcpu [0-9]+ :.*rdtscp cx.*}
unify_output {\[init -> vmm\] vcpu 0 : [0-9]+} "vcpu 0 : XX"
unify_output {\[init -> vmm\] vcpu 1 : [0-9]+} "vcpu 1 : XX"
unify_output {\[init -> vmm\] vcpu 2 : [0-9]+} "vcpu 2 : XX"
unify_output {\[init -> vmm\] vcpu 3 : [0-9]+} "vcpu 3 : XX"
set output [lsort -stride 11 -index 1 $output]
unify_output "vcpu" "\nvcpu"
set output_rdtscp $output
}
puts "\ncomparing output ..."
if {$test_rdtscp} {
puts $output_rdtscp
puts ""
set output $output_rdtscp
compare_output_to {
vcpu 0 : XX. vm exit - rdtscp cx guest=0xaffe0000 host=0x0
vcpu 1 : XX. vm exit - rdtscp cx guest=0xaffe0001 host=0x0
vcpu 2 : XX. vm exit - rdtscp cx guest=0xaffe0002 host=0x1
vcpu 3 : XX. vm exit - rdtscp cx guest=0xaffe0003 host=0x1
}
}
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
vcpu 0 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff3
}
puts $output_1
@ -182,19 +217,19 @@ 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 - halting vCPU - guest called HLT - ip=0xfff3
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 - due to pause() request - ip=0xfff3
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 - halting vCPU - guest called HLT - ip=0xfff3
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 - due to pause() request - ip=0xfff3
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 - due to pause() request - ip=0xfff5
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 - halting vCPU - guest called HLT - ip=0xfff7
vcpu 1 : XX. vm exit - reason 0xff handled by 'ep'
vcpu 1 : XX. vm exit - due to pause() request - ip=0xfff4
vcpu 1 : XX. vm exit - due to pause() request - ip=0xfff7
}
puts $output_2
@ -203,17 +238,17 @@ 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 - halting vCPU - guest called HLT - ip=0xfff3
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 - due to pause() request - ip=0xfff3
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 - halting vCPU - guest called HLT - ip=0xfff3
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 - due to pause() request - ip=0xfff3
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 - halting vCPU - guest called HLT - ip=0xfff4
vcpu 2 : XX. vm exit - reason 0xff handled by 'second ep'
vcpu 2 : XX. vm exit - due to pause() request - ip=0xfff1
vcpu 2 : XX. vm exit - due to pause() request - ip=0xfff4
}
puts $output_3
@ -222,7 +257,7 @@ 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
vcpu 3 : XX. vm exit - halting vCPU - guest called HLT - ip=0xfff3
}
set output $output_saved

View File

@ -55,6 +55,13 @@ namespace Vmm {
Vm_connection::Exit_config const exit_config {
/* ... */
};
static uint32_t rdtscp()
{
uint32_t lo = 0, hi = 0, tsc_aux = 0;
asm volatile ("rdtscp" : "=a" (lo), "=d" (hi), "=c" (tsc_aux) :: "memory");
return tsc_aux;
}
}
class Vmm::Vcpu
@ -82,6 +89,7 @@ class Vmm::Vcpu
/* test state end */
unsigned _exit_count { 0 };
unsigned _pause_count { 0 };
unsigned _hlt_count { 0 };
unsigned _timer_count { 0 };
unsigned _pause_at_timer { 0 };
@ -90,7 +98,10 @@ class Vmm::Vcpu
void _cpu_init()
{
enum { INTEL_CTRL_PRIMARY_HLT = 1 << 7 };
enum { INTEL_CTRL_SECOND_UG = 1 << 7 };
enum {
INTEL_CTRL_SECOND_UG = 1 << 7,
INTEL_CTRL_SECOND_RDTSCP_ENABLE = 1 << 3,
};
enum { AMD_CTRL_SECOND_VMRUN = 1 << 0 };
/* http://www.sandpile.org/x86/initial.htm */
@ -122,13 +133,18 @@ class Vmm::Vcpu
if (_vmx) {
state.ctrl_primary.charge(INTEL_CTRL_PRIMARY_HLT);
/* required for seL4 */
state.ctrl_secondary.charge(INTEL_CTRL_SECOND_UG);
state.ctrl_secondary.charge(INTEL_CTRL_SECOND_UG | /* required for seL4 */
INTEL_CTRL_SECOND_RDTSCP_ENABLE);
}
if (_svm) {
/* required for native AMD hardware (!= Qemu) for NOVA */
state.ctrl_secondary.charge(AMD_CTRL_SECOND_VMRUN);
}
/* Store id of CPU for rdtscp, similar as some OS do and some
* magic number to check for testing purpuse
*/
state.tsc_aux.charge((0xaffeU << 16) | _id);
}
public:
@ -276,13 +292,17 @@ class Vmm::Vm
/* prepare guest memory with some instructions for testing */
uint8_t * guest = env.rm().attach(_memory);
// *(guest + 0xff0) = 0x0f; /* CPUID instruction */
// *(guest + 0xff1) = 0xa2;
*(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 */
unsigned byte = 0xff0;
guest[byte++] = 0x0f; /* rdtscp */
guest[byte++] = 0x01;
guest[byte++] = 0xf9;
guest[byte++] = 0xf4; /* HLT instruction */
guest[byte++] = 0xf4; /* HLT instruction */
guest[byte++] = 0xeb; /* JMP - endless loop to RIP */
guest[byte++] = 0xfe; /* JMP of -2 byte (size of JMP inst) */
guest[byte++] = 0xf4; /* HLT instruction */
env.rm().detach(guest);
log ("let vCPUs run - first EP");
@ -448,7 +468,16 @@ void Vmm::Vcpu::_handle_vcpu_exit()
case Exit::AMD_HLT:
log("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" halting vCPU - guest called HLT - ip=", Hex(state.ip.value()));
if (_hlt_count == 0) {
uint32_t const tsc_aux_host = rdtscp();
log("vcpu ", _id, " : ", _exit_count, ". vm exit - rdtscp cx"
" guest=", Hex(state.cx.value()), " host=", Hex(tsc_aux_host));
}
_test_state = State::HALTED;
_hlt_count ++;
return;
case Exit::INTEL_EPT: