openhcl_boot/arch/x86_64/
snp.rs1use super::address_space::LocalMap;
7use core::arch::asm;
8use memory_range::MemoryRange;
9use minimal_rt::arch::msr::read_msr;
10use minimal_rt::arch::msr::write_msr;
11use x86defs::X86X_AMD_MSR_GHCB;
12use x86defs::snp::GhcbInfo;
13
14pub struct Ghcb;
15
16#[derive(Debug)]
17pub enum AcceptGpaStatus {
18 Success,
19 Retry,
20}
21
22#[expect(dead_code)] #[derive(Debug)]
24pub enum AcceptGpaError {
25 MemorySecurityViolation {
26 error_code: u32,
27 carry_flag: u32,
28 page_number: u64,
29 large_page: bool,
30 validate: bool,
31 },
32 Unknown,
33}
34
35impl Ghcb {
36 fn sev_vmgexit() {
41 unsafe {
44 asm! {r#"
45 rep vmmcall
46 "#
47 }
48 }
49 }
50
51 pub fn change_page_visibility(range: MemoryRange, host_visible: bool) {
52 let previous_value = unsafe { read_msr(X86X_AMD_MSR_GHCB) };
54 for page_number in range.start_4k_gpn()..range.end_4k_gpn() {
55 let extra_data = if host_visible {
56 x86defs::snp::GHCB_DATA_PAGE_STATE_SHARED
57 } else {
58 x86defs::snp::GHCB_DATA_PAGE_STATE_PRIVATE
59 };
60
61 let val = (extra_data << 52) | (page_number << 12) | GhcbInfo::PAGE_STATE_CHANGE.0;
62
63 let val = unsafe {
65 write_msr(X86X_AMD_MSR_GHCB, val);
66 Self::sev_vmgexit();
67 read_msr(X86X_AMD_MSR_GHCB)
68 };
69
70 assert!(
74 val == GhcbInfo::PAGE_STATE_UPDATED.0,
75 "GhcbInfo::PAGE_STATE_UPDATED returned msr value {val}"
76 );
77 }
78
79 unsafe { write_msr(X86X_AMD_MSR_GHCB, previous_value) };
81 }
82}
83
84fn pvalidate(
86 page_number: u64,
87 va: u64,
88 large_page: bool,
89 validate: bool,
90) -> Result<AcceptGpaStatus, AcceptGpaError> {
91 if large_page {
92 assert!(va % x86defs::X64_LARGE_PAGE_SIZE == 0);
93 } else {
94 assert!(va % hvdef::HV_PAGE_SIZE == 0)
95 }
96
97 let validate_page = validate as u32;
98 let page_size = large_page as u32;
99 let mut error_code: u32;
100 let mut carry_flag: u32 = 0;
101
102 unsafe {
104 asm!(r#"
105 pvalidate
106 jnc 2f
107 inc {carry_flag:e}
108 2:
109 "#,
110 in("rax") va,
111 in("ecx") page_size,
112 in("edx") validate_page,
113 lateout("eax") error_code,
114 carry_flag = inout(reg) carry_flag);
115 }
116
117 const SEV_SUCCESS: u32 = 0;
118 const SEV_FAIL_SIZEMISMATCH: u32 = 6;
119
120 match (error_code, carry_flag) {
121 (SEV_SUCCESS, 0) => Ok(AcceptGpaStatus::Success),
122 (SEV_FAIL_SIZEMISMATCH, _) => Ok(AcceptGpaStatus::Retry),
123 _ => Err(AcceptGpaError::MemorySecurityViolation {
124 error_code,
125 carry_flag,
126 page_number,
127 large_page,
128 validate,
129 }),
130 }
131}
132
133pub fn set_page_acceptance(
136 local_map: &mut LocalMap<'_>,
137 range: MemoryRange,
138 validate: bool,
139) -> Result<(), AcceptGpaError> {
140 let pages_per_large_page = x86defs::X64_LARGE_PAGE_SIZE / hvdef::HV_PAGE_SIZE;
141 let mut page_count = range.page_count_4k();
142 let mut page_base = range.start_4k_gpn();
143
144 while page_count != 0 {
145 let mapping = local_map.map_pages(
149 MemoryRange::from_4k_gpn_range(page_base..page_base + 1),
150 true,
151 );
152 if page_base % pages_per_large_page == 0 && page_count >= pages_per_large_page {
153 let res = pvalidate(page_base, mapping.data.as_ptr() as u64, true, validate)?;
154 match res {
155 AcceptGpaStatus::Success => {
156 page_count -= pages_per_large_page;
157 page_base += pages_per_large_page;
158 continue;
159 }
160 AcceptGpaStatus::Retry => (),
161 }
162 }
163
164 let res = pvalidate(page_base, mapping.data.as_ptr() as u64, false, validate)?;
166 match res {
167 AcceptGpaStatus::Success => {
168 page_count -= 1;
169 page_base += 1;
170 }
171 AcceptGpaStatus::Retry => {
172 return Err(AcceptGpaError::Unknown);
174 }
175 }
176 }
177
178 Ok(())
179}