Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use VTL-aware direct hypercall to get/set registers when possible #407

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
540 changes: 350 additions & 190 deletions openhcl/hcl/src/ioctl.rs

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion openhcl/hcl/src/ioctl/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::HclVp;
use super::NoRunner;
use super::ProcessorRunner;
use crate::protocol::hcl_cpu_context_aarch64;
use crate::GuestVtl;
use hvdef::HvArm64RegisterName;
use hvdef::HvRegisterName;
use hvdef::HvRegisterValue;
Expand Down Expand Up @@ -55,10 +56,14 @@ impl super::BackingPrivate for MshvArm64 {

fn try_set_reg(
runner: &mut ProcessorRunner<'_, Self>,
_vtl: GuestVtl,
name: HvRegisterName,
value: HvRegisterValue,
) -> Result<bool, super::Error> {
// Try to set the register in the CPU context, the fastest path.
// Try to set the register in the CPU context, the fastest path. Only
// VTL-shared registers can be set this way: the CPU context only
// exposes the last VTL, and if we entered VTL2 on an interrupt,
// OpenHCL doesn't know what the last VTL is.
// NOTE: x18 is omitted here as it is managed by the hypervisor.
let set = match name.into() {
HvArm64RegisterName::X0
Expand Down Expand Up @@ -96,6 +101,7 @@ impl super::BackingPrivate for MshvArm64 {
true
}
HvArm64RegisterName::X18 => {
// TODO: handle X18 for VTL1
runner.cpu_context_mut().x[18] = value.as_u64();
false
}
Expand All @@ -111,6 +117,7 @@ impl super::BackingPrivate for MshvArm64 {

fn try_get_reg(
runner: &ProcessorRunner<'_, Self>,
_vtl: GuestVtl,
name: HvRegisterName,
) -> Result<Option<HvRegisterValue>, super::Error> {
// Try to get the register from the CPU context, the fastest path.
Expand Down
2 changes: 2 additions & 0 deletions openhcl/hcl/src/ioctl/snp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ impl super::private::BackingPrivate for Snp {

fn try_set_reg(
_runner: &mut ProcessorRunner<'_, Self>,
_vtl: GuestVtl,
_name: HvRegisterName,
_value: HvRegisterValue,
) -> Result<bool, super::Error> {
Expand All @@ -198,6 +199,7 @@ impl super::private::BackingPrivate for Snp {

fn try_get_reg(
_runner: &ProcessorRunner<'_, Self>,
_vtl: GuestVtl,
_name: HvRegisterName,
) -> Result<Option<HvRegisterValue>, super::Error> {
Ok(None)
Expand Down
2 changes: 2 additions & 0 deletions openhcl/hcl/src/ioctl/tdx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ impl super::private::BackingPrivate for Tdx {

fn try_set_reg(
_runner: &mut ProcessorRunner<'_, Self>,
_vtl: GuestVtl,
_name: HvRegisterName,
_value: HvRegisterValue,
) -> Result<bool, super::Error> {
Expand All @@ -335,6 +336,7 @@ impl super::private::BackingPrivate for Tdx {

fn try_get_reg(
_runner: &ProcessorRunner<'_, Self>,
_vtl: GuestVtl,
_name: HvRegisterName,
) -> Result<Option<HvRegisterValue>, super::Error> {
Ok(None)
Expand Down
152 changes: 81 additions & 71 deletions openhcl/hcl/src/ioctl/x64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,14 @@ impl BackingPrivate for MshvX64 {

fn try_set_reg(
runner: &mut ProcessorRunner<'_, Self>,
vtl: GuestVtl,
name: HvRegisterName,
value: HvRegisterValue,
) -> Result<bool, Error> {
// Try to set the register in the CPU context.
// Try to set the register in the CPU context, the fastest path. Only
// VTL-shared registers can be set this way: the CPU context only
// exposes the last VTL, and if we entered VTL2 on an interrupt,
// OpenHCL doesn't know what the last VTL is.
let name = name.into();
let set = match name {
HvX64RegisterName::Rax
Expand Down Expand Up @@ -251,45 +255,48 @@ impl BackingPrivate for MshvX64 {
}

if let Some(reg_page) = runner.reg_page_mut() {
let set = match name {
HvX64RegisterName::Rsp => {
reg_page.gp_registers[(name.0 - HvX64RegisterName::Rax.0) as usize] =
value.as_u64();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_GENERAL;
true
if reg_page.vtl == vtl as u8 {
let set = match name {
HvX64RegisterName::Rsp => {
reg_page.gp_registers[(name.0 - HvX64RegisterName::Rax.0) as usize] =
value.as_u64();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_GENERAL;
true
}
HvX64RegisterName::Rip => {
reg_page.rip = value.as_u64();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_IP;
true
}
HvX64RegisterName::Rflags => {
reg_page.rflags = value.as_u64();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_FLAGS;
true
}
HvX64RegisterName::Es
| HvX64RegisterName::Cs
| HvX64RegisterName::Ss
| HvX64RegisterName::Ds
| HvX64RegisterName::Fs
| HvX64RegisterName::Gs => {
reg_page.segment[(name.0 - HvX64RegisterName::Es.0) as usize] =
value.as_u128();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_SEGMENT;
true
}

// Skip unnecessary register updates.
HvX64RegisterName::Cr0 => reg_page.cr0 == value.as_u64(),
HvX64RegisterName::Cr3 => reg_page.cr3 == value.as_u64(),
HvX64RegisterName::Cr4 => reg_page.cr4 == value.as_u64(),
HvX64RegisterName::Cr8 => reg_page.cr8 == value.as_u64(),
HvX64RegisterName::Efer => reg_page.efer == value.as_u64(),
HvX64RegisterName::Dr7 => reg_page.dr7 == value.as_u64(),
_ => false,
};
if set {
return Ok(true);
}
HvX64RegisterName::Rip => {
reg_page.rip = value.as_u64();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_IP;
true
}
HvX64RegisterName::Rflags => {
reg_page.rflags = value.as_u64();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_FLAGS;
true
}
HvX64RegisterName::Es
| HvX64RegisterName::Cs
| HvX64RegisterName::Ss
| HvX64RegisterName::Ds
| HvX64RegisterName::Fs
| HvX64RegisterName::Gs => {
reg_page.segment[(name.0 - HvX64RegisterName::Es.0) as usize] = value.as_u128();
reg_page.dirty |= 1 << hvdef::HV_X64_REGISTER_CLASS_SEGMENT;
true
}

// Skip unnecessary register updates.
HvX64RegisterName::Cr0 => reg_page.cr0 == value.as_u64(),
HvX64RegisterName::Cr3 => reg_page.cr3 == value.as_u64(),
HvX64RegisterName::Cr4 => reg_page.cr4 == value.as_u64(),
HvX64RegisterName::Cr8 => reg_page.cr8 == value.as_u64(),
HvX64RegisterName::Efer => reg_page.efer == value.as_u64(),
HvX64RegisterName::Dr7 => reg_page.dr7 == value.as_u64(),
_ => false,
};
if set {
return Ok(true);
}
}

Expand All @@ -305,6 +312,7 @@ impl BackingPrivate for MshvX64 {

fn try_get_reg(
runner: &ProcessorRunner<'_, Self>,
vtl: GuestVtl,
name: HvRegisterName,
) -> Result<Option<HvRegisterValue>, Error> {
let name = name.into();
Expand Down Expand Up @@ -356,39 +364,41 @@ impl BackingPrivate for MshvX64 {
}

if let Some(reg_page) = runner.reg_page() {
let value = match name {
HvX64RegisterName::Rsp => Some(HvRegisterValue(
reg_page.gp_registers[(name.0 - HvX64RegisterName::Rax.0) as usize].into(),
)),
HvX64RegisterName::Rip => Some(HvRegisterValue((reg_page.rip).into())),
HvX64RegisterName::Rflags => Some(HvRegisterValue((reg_page.rflags).into())),
HvX64RegisterName::Es
| HvX64RegisterName::Cs
| HvX64RegisterName::Ss
| HvX64RegisterName::Ds
| HvX64RegisterName::Fs
| HvX64RegisterName::Gs => Some(HvRegisterValue(
reg_page.segment[(name.0 - HvX64RegisterName::Es.0) as usize].into(),
)),
HvX64RegisterName::Cr0 => Some(HvRegisterValue((reg_page.cr0).into())),
HvX64RegisterName::Cr3 => Some(HvRegisterValue((reg_page.cr3).into())),
HvX64RegisterName::Cr4 => Some(HvRegisterValue((reg_page.cr4).into())),
HvX64RegisterName::Cr8 => Some(HvRegisterValue((reg_page.cr8).into())),
HvX64RegisterName::Efer => Some(HvRegisterValue((reg_page.efer).into())),
HvX64RegisterName::Dr7 => Some(HvRegisterValue((reg_page.dr7).into())),
HvX64RegisterName::InstructionEmulationHints => Some(HvRegisterValue(
(u64::from(reg_page.instruction_emulation_hints)).into(),
)),
HvX64RegisterName::PendingInterruption => {
Some(u64::from(reg_page.pending_interruption).into())
}
HvX64RegisterName::InterruptState => {
Some(u64::from(reg_page.interrupt_state).into())
if reg_page.vtl == vtl as u8 {
let value = match name {
HvX64RegisterName::Rsp => Some(HvRegisterValue(
reg_page.gp_registers[(name.0 - HvX64RegisterName::Rax.0) as usize].into(),
)),
HvX64RegisterName::Rip => Some(HvRegisterValue((reg_page.rip).into())),
HvX64RegisterName::Rflags => Some(HvRegisterValue((reg_page.rflags).into())),
HvX64RegisterName::Es
| HvX64RegisterName::Cs
| HvX64RegisterName::Ss
| HvX64RegisterName::Ds
| HvX64RegisterName::Fs
| HvX64RegisterName::Gs => Some(HvRegisterValue(
reg_page.segment[(name.0 - HvX64RegisterName::Es.0) as usize].into(),
)),
HvX64RegisterName::Cr0 => Some(HvRegisterValue((reg_page.cr0).into())),
HvX64RegisterName::Cr3 => Some(HvRegisterValue((reg_page.cr3).into())),
HvX64RegisterName::Cr4 => Some(HvRegisterValue((reg_page.cr4).into())),
HvX64RegisterName::Cr8 => Some(HvRegisterValue((reg_page.cr8).into())),
HvX64RegisterName::Efer => Some(HvRegisterValue((reg_page.efer).into())),
HvX64RegisterName::Dr7 => Some(HvRegisterValue((reg_page.dr7).into())),
HvX64RegisterName::InstructionEmulationHints => Some(HvRegisterValue(
(u64::from(reg_page.instruction_emulation_hints)).into(),
)),
HvX64RegisterName::PendingInterruption => {
Some(u64::from(reg_page.pending_interruption).into())
}
HvX64RegisterName::InterruptState => {
Some(u64::from(reg_page.interrupt_state).into())
}
_ => None,
};
if let Some(value) = value {
return Ok(Some(value));
}
_ => None,
};
if let Some(value) = value {
return Ok(Some(value));
}
}

Expand Down
20 changes: 14 additions & 6 deletions openhcl/virt_mshv_vtl/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,11 @@ mod private {
/// message slot.
///
/// This is used for hypervisor-managed and untrusted SINTs.
fn request_untrusted_sint_readiness(this: &mut UhProcessor<'_, Self>, sints: u16);
fn request_untrusted_sint_readiness(
this: &mut UhProcessor<'_, Self>,
vtl: GuestVtl,
sints: u16,
);

/// Copies shared registers (per VSM TLFS spec) from the source VTL to
/// the target VTL that will become active.
Expand Down Expand Up @@ -858,7 +862,7 @@ impl<'a, T: Backing> UhProcessor<'a, T> {
#[cfg(guest_arch = "aarch64")]
let sint_reg =
HvArm64RegisterName(HvArm64RegisterName::Sint0.0 + sint as u32);
self.runner.get_vp_register(sint_reg).unwrap().as_u64()
self.runner.get_vp_register(vtl, sint_reg).unwrap().as_u64()
};
masked_sints |= (HvSynicSint::from(sint_msr).masked() as u16) << sint;
}
Expand Down Expand Up @@ -919,7 +923,7 @@ impl<'a, T: Backing> UhProcessor<'a, T> {
};

if sints & untrusted_sints != 0 {
T::request_untrusted_sint_readiness(self, sints & untrusted_sints);
T::request_untrusted_sint_readiness(self, vtl, sints & untrusted_sints);
}
}

Expand Down Expand Up @@ -1206,22 +1210,25 @@ impl<T: CpuIo, B: Backing> Arm64RegisterState for UhHypercallHandler<'_, '_, T,
fn pc(&mut self) -> u64 {
self.vp
.runner
.get_vp_register(HvArm64RegisterName::XPc)
.get_vp_register(self.intercepted_vtl, HvArm64RegisterName::XPc)
.expect("get vp register cannot fail")
.as_u64()
}

fn set_pc(&mut self, pc: u64) {
self.vp
.runner
.set_vp_register(HvArm64RegisterName::XPc, pc.into())
.set_vp_register(self.intercepted_vtl, HvArm64RegisterName::XPc, pc.into())
.expect("set vp register cannot fail");
}

fn x(&mut self, n: u8) -> u64 {
self.vp
.runner
.get_vp_register(HvArm64RegisterName(HvArm64RegisterName::X0.0 + n as u32))
.get_vp_register(
self.intercepted_vtl,
HvArm64RegisterName(HvArm64RegisterName::X0.0 + n as u32),
)
.expect("get vp register cannot fail")
.as_u64()
}
Expand All @@ -1230,6 +1237,7 @@ impl<T: CpuIo, B: Backing> Arm64RegisterState for UhHypercallHandler<'_, '_, T,
self.vp
.runner
.set_vp_register(
self.intercepted_vtl,
HvArm64RegisterName(HvArm64RegisterName::X0.0 + n as u32),
v.into(),
)
Expand Down
Loading