Skip to content

Commit a583136

Browse files
Improve pKVM hypercalls
The pKVM hypervisor forwards PKVM_GHC_IOWRITE and PKVM_GHC_IOWREAD to pKVM host for emulation. In such case, guest registers values in RAX, RBX, RCX, RDX and RSI might become visible to the host. To prevent potential unintended information leakage, introduce vmcall macro which zeros not used hyperacall arguments, and use it for PKVM_GHC_IOWRITE/PKVM_GHC_IOWREAD hypercall to make sure that unused RDX or RSI are set to 0.
1 parent 12685bf commit a583136

File tree

1 file changed

+48
-38
lines changed

1 file changed

+48
-38
lines changed

src/transport/x86_64/hypercalls.rs

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const KVM_CPUID_SIGNATURE: u32 = 0x40000000;
99

1010
// See `include/uapi/linux/kvm_para.h`. (These hypercalls numbers can change depending on the
1111
// upstream progress.)
12-
const KVM_HC_PKVM_OP: u32 = 20;
13-
const PKVM_GHC_IOREAD: u32 = KVM_HC_PKVM_OP + 3;
14-
const PKVM_GHC_IOWRITE: u32 = KVM_HC_PKVM_OP + 4;
12+
const KVM_HC_PKVM_OP: u64 = 20;
13+
const PKVM_GHC_IOREAD: u64 = KVM_HC_PKVM_OP + 3;
14+
const PKVM_GHC_IOWRITE: u64 = KVM_HC_PKVM_OP + 4;
1515

1616
/// The maximum number of bytes that can be read or written by a single IO hypercall.
1717
const HYP_IO_MAX: usize = 8;
@@ -42,48 +42,58 @@ pub fn cpuid_signature() -> [u8; 4] {
4242
signature.to_le_bytes()
4343
}
4444

45-
/// Asks the hypervisor to perform an IO read at the given physical address.
46-
pub fn hyp_io_read(address: u64, size: usize) -> u64 {
47-
let data;
45+
fn __vmcall_impl(hypcall: u64, a1: u64, a2: u64, a3: u64, a4: u64) -> u64 {
46+
let ret: u64;
4847

49-
// SAFETY: Assembly call. Arguments for vmcall are passed via rax, rbx, rcx and rdx. Ideally
50-
// using a named argument in the inline asm for rbx would be more straightforward, but when
51-
// "rbx" is used directly LLVM complains that it is used internally.
48+
// SAFETY: Assembly call. Arguments for vmcall are passed via rax, rbx, rcx, rdx and rsi.
49+
// Ideally using a named argument in the inline asm for rbx would be more straightforward,
50+
// but when "rbx" is used directly LLVM complains that it is used internally.
5251
//
53-
// Therefore use r8 temporary, push rbx to the stack, perform proper call and pop rbx again.
52+
// Therefore use temp register to store rbx content and restore it back afterwards.
5453
unsafe {
55-
asm!(
56-
"push rbx",
57-
"mov rbx, r8",
58-
"vmcall",
59-
"pop rbx",
60-
inout("rax") u64::from(PKVM_GHC_IOREAD) => data,
61-
in("r8") address,
62-
in("rcx") size,
63-
);
64-
}
65-
data
54+
core::arch::asm!(
55+
"xchg %rbx, {0:r}",
56+
"vmcall",
57+
"xchg %rbx, {0:r}",
58+
in(reg) a1,
59+
inout("rax") hypcall => ret,
60+
in("rcx") a2,
61+
in("rdx") a3,
62+
in("rsi") a4,
63+
options(att_syntax));
64+
};
65+
ret
66+
}
67+
68+
// This block uses inline assembly to perform a vmcall, interacting directly with the hypervisor.
69+
// The pKVM hypervisor can share RAX/RBX/RCX/RDX/RSI with pKVM host during hypercall
70+
// handling.
71+
macro_rules! vmcall {
72+
($hypcall:expr) => {
73+
__vmcall_impl($hypcall, 0, 0, 0, 0)
74+
};
75+
($hypcall:expr, $a1:expr) => {
76+
__vmcall_impl($hypcall, $a1, 0, 0, 0)
77+
};
78+
($hypcall:expr, $a1:expr, $a2:expr) => {
79+
__vmcall_impl($hypcall, $a1, $a2, 0, 0)
80+
};
81+
($hypcall:expr, $a1:expr, $a2:expr, $a3:expr) => {
82+
__vmcall_impl($hypcall, $a1, $a2, $a3, 0)
83+
};
84+
($hypcall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
85+
__vmcall_impl($hypcall, $a1, $a2, $a3, $a4)
86+
};
87+
}
88+
89+
/// Asks the hypervisor to perform an IO read at the given physical address.
90+
pub fn hyp_io_read(address: u64, size: usize) -> u64 {
91+
vmcall!(PKVM_GHC_IOREAD, address, size as u64)
6692
}
6793

6894
/// Asks the hypervisor to perform an IO write at the given physical address.
6995
pub fn hyp_io_write(address: u64, size: usize, data: u64) {
70-
// SAFETY: Assembly call. Arguments for vmcall are passed via rax, rbx, rcx and rdx. Ideally
71-
// using a named argument in the inline asm for rbx would be more straightforward but when
72-
// "rbx" is used directly used LLVM complains that it is used internally.
73-
//
74-
// Therefore use r8 temporary, push rbx to the stack, perform proper call and pop rbx again.
75-
unsafe {
76-
asm!(
77-
"push rbx",
78-
"mov rbx, r8",
79-
"vmcall",
80-
"pop rbx",
81-
in("rax") PKVM_GHC_IOWRITE,
82-
in("r8") address,
83-
in("rcx") size,
84-
in("rdx") data,
85-
);
86-
}
96+
vmcall!(PKVM_GHC_IOWRITE, address, size as u64, data);
8797
}
8898

8999
/// A region of physical address space which may be accessed by IO read and/or write hypercalls.

0 commit comments

Comments
 (0)