@@ -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.
1717const 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 : 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 {
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 : u64 , size : usize ) -> u64 {
92+ vmcall ! ( PKVM_GHC_IOREAD , address, size as u64 )
6693}
6794
6895/// Asks the hypervisor to perform an IO write at the given physical address.
6996pub 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- }
97+ vmcall ! ( PKVM_GHC_IOWRITE , address, 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