1use super::Hcl;
7use super::HclVp;
8use super::MshvVtl;
9use super::NoRunner;
10use super::ProcessorRunner;
11use super::hcl_pvalidate_pages;
12use super::hcl_rmpadjust_pages;
13use super::hcl_rmpquery_pages;
14use super::mshv_pvalidate;
15use super::mshv_rmpadjust;
16use super::mshv_rmpquery;
17use crate::GuestVtl;
18use crate::vmsa::VmsaWrapper;
19use hv1_structs::VtlArray;
20use hvdef::HV_PAGE_SIZE;
21use hvdef::HvRegisterName;
22use hvdef::HvRegisterValue;
23use memory_range::MemoryRange;
24use sidecar_client::SidecarVp;
25use std::cell::UnsafeCell;
26use std::os::fd::AsRawFd;
27use std::sync::atomic::AtomicU32;
28use std::sync::atomic::Ordering;
29use thiserror::Error;
30use x86defs::snp::SevAvicPage;
31use x86defs::snp::SevRmpAdjust;
32use x86defs::snp::SevVmsa;
33
34pub struct Snp<'a> {
36 vmsa: VtlArray<&'a UnsafeCell<SevVmsa>, 2>,
37 avic_pages: VtlArray<&'a UnsafeCell<SevAvicPage>, 2>,
38}
39
40pub struct SnpStimer0Update {
42 pub count: u64,
44 pub programmed_ref_time: u64,
46 pub expired: bool,
48}
49
50#[derive(Debug, Error)]
52#[expect(missing_docs)]
53pub enum SnpError {
54 #[error("operating system error")]
55 Os(#[source] nix::Error),
56 #[error("isa error {0:?}")]
57 Isa(u32),
58}
59
60#[derive(Debug, Error)]
62#[expect(missing_docs)]
63pub enum SnpPageError {
64 #[error("pvalidate failed")]
65 Pvalidate(#[source] SnpError),
66 #[error("rmpadjust failed")]
67 Rmpadjust(#[source] SnpError),
68 #[error("rmpquery failed")]
69 Rmpquery(#[source] SnpError),
70}
71
72impl MshvVtl {
73 pub fn pvalidate_pages(
77 &self,
78 range: MemoryRange,
79 validate: bool,
80 terminate_on_failure: bool,
81 ) -> Result<(), SnpPageError> {
82 tracing::debug!(%range, validate, terminate_on_failure, "pvalidate");
83 let ret = unsafe {
87 hcl_pvalidate_pages(
88 self.file.as_raw_fd(),
89 &mshv_pvalidate {
90 start_pfn: range.start() / HV_PAGE_SIZE,
91 page_count: (range.end() - range.start()) / HV_PAGE_SIZE,
92 validate: validate as u8,
93 terminate_on_failure: terminate_on_failure as u8,
94 ram: 0,
95 padding: [0; 1],
96 },
97 )
98 .map_err(SnpError::Os)
99 .map_err(SnpPageError::Pvalidate)?
100 };
101
102 if ret != 0 {
103 return Err(SnpPageError::Pvalidate(SnpError::Isa(ret as u32)));
104 }
105
106 Ok(())
107 }
108
109 pub fn rmpadjust_pages(
113 &self,
114 range: MemoryRange,
115 value: SevRmpAdjust,
116 terminate_on_failure: bool,
117 ) -> Result<(), SnpPageError> {
118 let ret = unsafe {
121 hcl_rmpadjust_pages(
122 self.file.as_raw_fd(),
123 &mshv_rmpadjust {
124 start_pfn: range.start() / HV_PAGE_SIZE,
125 page_count: (range.end() - range.start()) / HV_PAGE_SIZE,
126 value: value.into(),
127 terminate_on_failure: terminate_on_failure as u8,
128 ram: 0,
129 padding: Default::default(),
130 },
131 )
132 .map_err(SnpError::Os)
133 .map_err(SnpPageError::Rmpadjust)?
134 };
135
136 if ret != 0 {
137 return Err(SnpPageError::Rmpadjust(SnpError::Isa(ret as u32)));
138 }
139
140 Ok(())
141 }
142
143 pub fn rmpquery_page(&self, gpa: u64, vtl: GuestVtl) -> Result<SevRmpAdjust, SnpPageError> {
146 let page_count = 1u64;
147 let mut flags = [u64::from(SevRmpAdjust::new().with_target_vmpl(match vtl {
148 GuestVtl::Vtl0 => 2,
149 GuestVtl::Vtl1 => 1,
150 })); 1];
151
152 let mut page_size = [0; 1];
153 let mut pages_processed = 0u64;
154
155 debug_assert!(flags.len() == page_count as usize);
156 debug_assert!(page_size.len() == page_count as usize);
157
158 let query = mshv_rmpquery {
159 start_pfn: gpa / HV_PAGE_SIZE,
160 page_count,
161 terminate_on_failure: 0,
162 ram: 0,
163 padding: Default::default(),
164 flags: flags.as_mut_ptr(),
165 page_size: page_size.as_mut_ptr(),
166 pages_processed: &mut pages_processed,
167 };
168
169 unsafe {
171 hcl_rmpquery_pages(self.file.as_raw_fd(), &query)
172 .map_err(SnpError::Os)
173 .map_err(SnpPageError::Rmpquery)?;
174 }
175
176 assert!(pages_processed <= page_count);
177
178 Ok(SevRmpAdjust::from(flags[0]))
179 }
180}
181
182impl<'a> super::private::BackingPrivate<'a> for Snp<'a> {
183 fn new(vp: &'a HclVp, sidecar: Option<&SidecarVp<'_>>, _hcl: &Hcl) -> Result<Self, NoRunner> {
184 assert!(sidecar.is_none());
185 let super::BackingState::Snp {
186 vtl0_apic_page,
187 vtl1_apic_page,
188 vmsa,
189 } = &vp.backing
190 else {
191 return Err(NoRunner::MismatchedIsolation);
192 };
193
194 let vtl1_apic_page = unsafe { &*vtl1_apic_page.base().cast() };
197
198 Ok(Self {
199 avic_pages: [vtl0_apic_page.as_ref(), vtl1_apic_page].into(),
200 vmsa: vmsa.each_ref().map(|mp| mp.as_ref()),
201 })
202 }
203
204 fn try_set_reg(
205 _runner: &mut ProcessorRunner<'a, Self>,
206 _vtl: GuestVtl,
207 _name: HvRegisterName,
208 _value: HvRegisterValue,
209 ) -> bool {
210 false
211 }
212
213 fn must_flush_regs_on(_runner: &ProcessorRunner<'a, Self>, _name: HvRegisterName) -> bool {
214 false
215 }
216
217 fn try_get_reg(
218 _runner: &ProcessorRunner<'a, Self>,
219 _vtl: GuestVtl,
220 _name: HvRegisterName,
221 ) -> Option<HvRegisterValue> {
222 None
223 }
224
225 fn flush_register_page(_runner: &mut ProcessorRunner<'a, Self>) {}
226}
227
228impl<'a> ProcessorRunner<'a, Snp<'a>> {
229 fn snp_context_ptr(&self) -> *mut crate::protocol::snp_vp_context {
230 unsafe { (&raw mut (*self.run.get()).context).cast() }
234 }
235
236 pub fn set_stimer0_config(&mut self, config: Option<u64>) {
238 unsafe {
241 let context = self.snp_context_ptr();
242 let flags = &*((&raw mut (*context).stimer0_flags).cast::<AtomicU32>());
243 if let Some(config) = config {
244 (&raw mut (*context).stimer0_config).write(config);
245 flags.fetch_or(
246 crate::protocol::MSHV_VTL_SNP_STIMER0_CONFIG_VALID,
247 Ordering::Release,
248 );
249 } else {
250 flags.fetch_and(
251 !crate::protocol::MSHV_VTL_SNP_STIMER0_CONFIG_VALID,
252 Ordering::Release,
253 );
254 }
255 }
256 }
257
258 pub fn take_stimer0_update(&mut self) -> Option<SnpStimer0Update> {
260 unsafe {
263 let context = self.snp_context_ptr();
264 let flags = &*((&raw mut (*context).stimer0_flags).cast::<AtomicU32>());
265 let value = flags.fetch_and(
266 !(crate::protocol::MSHV_VTL_SNP_STIMER0_KERNEL_UPDATE
267 | crate::protocol::MSHV_VTL_SNP_STIMER0_EXPIRED),
268 Ordering::AcqRel,
269 );
270 if value & crate::protocol::MSHV_VTL_SNP_STIMER0_KERNEL_UPDATE == 0 {
271 return None;
272 }
273
274 Some(SnpStimer0Update {
275 count: (&raw const (*context).stimer0_count).read(),
276 programmed_ref_time: (&raw const (*context).stimer0_programmed_ref_time).read(),
277 expired: value & crate::protocol::MSHV_VTL_SNP_STIMER0_EXPIRED != 0,
278 })
279 }
280 }
281
282 pub fn vmsa(&self, vtl: GuestVtl) -> VmsaWrapper<'_, &SevVmsa> {
284 let vmsa = unsafe { &*self.state.vmsa[vtl].get() };
287
288 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
289 }
290
291 pub fn vmsa_mut(&mut self, vtl: GuestVtl) -> VmsaWrapper<'_, &mut SevVmsa> {
293 let vmsa = unsafe { &mut *self.state.vmsa[vtl].get() };
296
297 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
298 }
299
300 pub fn vmsas_mut(&mut self) -> [VmsaWrapper<'_, &mut SevVmsa>; 2] {
302 self.state
303 .vmsa
304 .each_mut()
305 .map(|vmsa| {
306 let vmsa = unsafe { &mut *vmsa.get() };
309
310 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
311 })
312 .into_inner()
313 }
314
315 pub fn secure_avic_vtl0_pfn(&self, cpu_index: u32) -> u64 {
318 self.hcl.secure_avic_vtl0_pfn(cpu_index)
319 }
320
321 pub fn secure_avic_page(&self, vtl: GuestVtl) -> &SevAvicPage {
323 unsafe { &*self.state.avic_pages[vtl].get() }
326 }
327
328 pub fn secure_avic_page_mut(&mut self, vtl: GuestVtl) -> &mut SevAvicPage {
330 unsafe { &mut *self.state.avic_pages[vtl].get() }
333 }
334
335 pub fn secure_avic_page_vmsa_mut(
337 &mut self,
338 vtl: GuestVtl,
339 ) -> (&mut SevAvicPage, VmsaWrapper<'_, &mut SevVmsa>) {
340 let avic_page = unsafe { &mut *self.state.avic_pages[vtl].get() };
343 let vmsa = self.vmsa_mut(vtl);
344
345 (avic_page, vmsa)
346 }
347
348 pub fn secure_avic_page_proxy_irr_exit_vtl0_mut(
350 &mut self,
351 ) -> (&mut SevAvicPage, &mut [u32; 8]) {
352 let avic_page = unsafe { &mut *self.state.avic_pages[GuestVtl::Vtl0].get() };
355 let proxy_irr_vtl0 = unsafe { &mut (*self.run.get()).proxy_irr_exit };
357
358 (avic_page, proxy_irr_vtl0)
359 }
360}