Skip to content

Commit cc7bd63

Browse files
jaszczyk-grzegorzthedataking
authored andcommitted
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 bf45933 commit cc7bd63

File tree

1 file changed

+46
-35
lines changed

1 file changed

+46
-35
lines changed

src/transport/x86_64/hypercalls.rs

Lines changed: 46 additions & 35 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,59 @@ 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: usize, 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 {
5554
asm!(
56-
"push rbx",
57-
"mov rbx, r8",
55+
"xchg %rbx, {0:r}",
5856
"vmcall",
59-
"pop rbx",
60-
inout("rax") u64::from(PKVM_GHC_IOREAD) => data,
61-
in("r8") address,
62-
in("rcx") size,
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)
6364
);
64-
}
65-
data
65+
};
66+
ret
67+
}
68+
69+
// This block uses inline assembly to perform a vmcall, interacting directly with the hypervisor.
70+
// The pKVM hypervisor can share RAX/RBX/RCX/RDX/RSI with pKVM host during hypercall
71+
// handling.
72+
macro_rules! vmcall {
73+
($hypcall:expr) => {
74+
__vmcall_impl($hypcall, 0, 0, 0, 0)
75+
};
76+
($hypcall:expr, $a1:expr) => {
77+
__vmcall_impl($hypcall, $a1, 0, 0, 0)
78+
};
79+
($hypcall:expr, $a1:expr, $a2:expr) => {
80+
__vmcall_impl($hypcall, $a1, $a2, 0, 0)
81+
};
82+
($hypcall:expr, $a1:expr, $a2:expr, $a3:expr) => {
83+
__vmcall_impl($hypcall, $a1, $a2, $a3, 0)
84+
};
85+
($hypcall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
86+
__vmcall_impl($hypcall, $a1, $a2, $a3, $a4)
87+
};
88+
}
89+
90+
/// Asks the hypervisor to perform an IO read at the given physical address.
91+
pub fn hyp_io_read(address: usize, size: usize) -> u64 {
92+
vmcall!(PKVM_GHC_IOREAD, address as u64, size as u64)
6693
}
6794

6895
/// Asks the hypervisor to perform an IO write at the given physical address.
6996
pub fn hyp_io_write(address: usize, 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-
}
97+
vmcall!(PKVM_GHC_IOWRITE, address as u64, size as u64, data);
8798
}
8899

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

0 commit comments

Comments
 (0)