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 thiserror::Error;
28use x86defs::snp::SevRmpAdjust;
29use x86defs::snp::SevVmsa;
30
31pub struct Snp<'a> {
33 vmsa: VtlArray<&'a UnsafeCell<SevVmsa>, 2>,
34}
35
36#[derive(Debug, Error)]
38#[expect(missing_docs)]
39pub enum SnpError {
40 #[error("operating system error")]
41 Os(#[source] nix::Error),
42 #[error("isa error {0:?}")]
43 Isa(u32),
44}
45
46#[derive(Debug, Error)]
48#[expect(missing_docs)]
49pub enum SnpPageError {
50 #[error("pvalidate failed")]
51 Pvalidate(#[source] SnpError),
52 #[error("rmpadjust failed")]
53 Rmpadjust(#[source] SnpError),
54 #[error("rmpquery failed")]
55 Rmpquery(#[source] SnpError),
56}
57
58impl MshvVtl {
59 pub fn pvalidate_pages(
63 &self,
64 range: MemoryRange,
65 validate: bool,
66 terminate_on_failure: bool,
67 ) -> Result<(), SnpPageError> {
68 tracing::debug!(%range, validate, terminate_on_failure, "pvalidate");
69 let ret = unsafe {
73 hcl_pvalidate_pages(
74 self.file.as_raw_fd(),
75 &mshv_pvalidate {
76 start_pfn: range.start() / HV_PAGE_SIZE,
77 page_count: (range.end() - range.start()) / HV_PAGE_SIZE,
78 validate: validate as u8,
79 terminate_on_failure: terminate_on_failure as u8,
80 ram: 0,
81 padding: [0; 1],
82 },
83 )
84 .map_err(SnpError::Os)
85 .map_err(SnpPageError::Pvalidate)?
86 };
87
88 if ret != 0 {
89 return Err(SnpPageError::Pvalidate(SnpError::Isa(ret as u32)));
90 }
91
92 Ok(())
93 }
94
95 pub fn rmpadjust_pages(
99 &self,
100 range: MemoryRange,
101 value: SevRmpAdjust,
102 terminate_on_failure: bool,
103 ) -> Result<(), SnpPageError> {
104 let ret = unsafe {
107 hcl_rmpadjust_pages(
108 self.file.as_raw_fd(),
109 &mshv_rmpadjust {
110 start_pfn: range.start() / HV_PAGE_SIZE,
111 page_count: (range.end() - range.start()) / HV_PAGE_SIZE,
112 value: value.into(),
113 terminate_on_failure: terminate_on_failure as u8,
114 ram: 0,
115 padding: Default::default(),
116 },
117 )
118 .map_err(SnpError::Os)
119 .map_err(SnpPageError::Rmpadjust)?
120 };
121
122 if ret != 0 {
123 return Err(SnpPageError::Rmpadjust(SnpError::Isa(ret as u32)));
124 }
125
126 Ok(())
127 }
128
129 pub fn rmpquery_page(&self, gpa: u64, vtl: GuestVtl) -> Result<SevRmpAdjust, SnpPageError> {
132 let page_count = 1u64;
133 let mut flags = [u64::from(SevRmpAdjust::new().with_target_vmpl(match vtl {
134 GuestVtl::Vtl0 => 2,
135 GuestVtl::Vtl1 => 1,
136 })); 1];
137
138 let mut page_size = [0; 1];
139 let mut pages_processed = 0u64;
140
141 debug_assert!(flags.len() == page_count as usize);
142 debug_assert!(page_size.len() == page_count as usize);
143
144 let query = mshv_rmpquery {
145 start_pfn: gpa / HV_PAGE_SIZE,
146 page_count,
147 terminate_on_failure: 0,
148 ram: 0,
149 padding: Default::default(),
150 flags: flags.as_mut_ptr(),
151 page_size: page_size.as_mut_ptr(),
152 pages_processed: &mut pages_processed,
153 };
154
155 unsafe {
157 hcl_rmpquery_pages(self.file.as_raw_fd(), &query)
158 .map_err(SnpError::Os)
159 .map_err(SnpPageError::Rmpquery)?;
160 }
161
162 assert!(pages_processed <= page_count);
163
164 Ok(SevRmpAdjust::from(flags[0]))
165 }
166}
167
168impl<'a> super::private::BackingPrivate<'a> for Snp<'a> {
169 fn new(vp: &'a HclVp, sidecar: Option<&SidecarVp<'_>>, _hcl: &Hcl) -> Result<Self, NoRunner> {
170 assert!(sidecar.is_none());
171 let super::BackingState::Snp { vmsa } = &vp.backing else {
172 return Err(NoRunner::MismatchedIsolation);
173 };
174
175 Ok(Self {
176 vmsa: vmsa.each_ref().map(|mp| mp.as_ref()),
177 })
178 }
179
180 fn try_set_reg(
181 _runner: &mut ProcessorRunner<'a, Self>,
182 _vtl: GuestVtl,
183 _name: HvRegisterName,
184 _value: HvRegisterValue,
185 ) -> bool {
186 false
187 }
188
189 fn must_flush_regs_on(_runner: &ProcessorRunner<'a, Self>, _name: HvRegisterName) -> bool {
190 false
191 }
192
193 fn try_get_reg(
194 _runner: &ProcessorRunner<'a, Self>,
195 _vtl: GuestVtl,
196 _name: HvRegisterName,
197 ) -> Option<HvRegisterValue> {
198 None
199 }
200
201 fn flush_register_page(_runner: &mut ProcessorRunner<'a, Self>) {}
202}
203
204impl<'a> ProcessorRunner<'a, Snp<'a>> {
205 pub fn vmsa(&self, vtl: GuestVtl) -> VmsaWrapper<'_, &SevVmsa> {
207 let vmsa = unsafe { &*self.state.vmsa[vtl].get() };
210
211 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
212 }
213
214 pub fn vmsa_mut(&mut self, vtl: GuestVtl) -> VmsaWrapper<'_, &mut SevVmsa> {
216 let vmsa = unsafe { &mut *self.state.vmsa[vtl].get() };
219
220 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
221 }
222
223 pub fn vmsas_mut(&mut self) -> [VmsaWrapper<'_, &mut SevVmsa>; 2] {
225 self.state
226 .vmsa
227 .each_mut()
228 .map(|vmsa| {
229 let vmsa = unsafe { &mut *vmsa.get() };
232
233 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
234 })
235 .into_inner()
236 }
237}