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(
65 &self,
66 range: MemoryRange,
67 validate: bool,
68 terminate_on_failure: bool,
69 ) -> Result<(), SnpPageError> {
70 tracing::debug!(%range, validate, terminate_on_failure, "pvalidate");
71 let ret = unsafe {
75 hcl_pvalidate_pages(
76 self.file.as_raw_fd(),
77 &mshv_pvalidate {
78 start_pfn: range.start() / HV_PAGE_SIZE,
79 page_count: (range.end() - range.start()) / HV_PAGE_SIZE,
80 validate: validate as u8,
81 terminate_on_failure: terminate_on_failure as u8,
82 ram: 0,
83 padding: [0; 1],
84 },
85 )
86 .map_err(SnpError::Os)
87 .map_err(SnpPageError::Pvalidate)?
88 };
89
90 if ret != 0 {
91 return Err(SnpPageError::Pvalidate(SnpError::Isa(ret as u32)));
92 }
93
94 Ok(())
95 }
96
97 pub fn rmpadjust_pages(
103 &self,
104 range: MemoryRange,
105 value: SevRmpAdjust,
106 terminate_on_failure: bool,
107 ) -> Result<(), SnpPageError> {
108 if value.vmsa() {
109 return Ok(());
111 }
112
113 #[expect(clippy::undocumented_unsafe_blocks)] let ret = unsafe {
115 hcl_rmpadjust_pages(
116 self.file.as_raw_fd(),
117 &mshv_rmpadjust {
118 start_pfn: range.start() / HV_PAGE_SIZE,
119 page_count: (range.end() - range.start()) / HV_PAGE_SIZE,
120 value: value.into(),
121 terminate_on_failure: terminate_on_failure as u8,
122 ram: 0,
123 padding: Default::default(),
124 },
125 )
126 .map_err(SnpError::Os)
127 .map_err(SnpPageError::Rmpadjust)?
128 };
129
130 if ret != 0 {
131 return Err(SnpPageError::Rmpadjust(SnpError::Isa(ret as u32)));
132 }
133
134 Ok(())
135 }
136
137 pub fn rmpquery_page(&self, gpa: u64, vtl: GuestVtl) -> Result<SevRmpAdjust, SnpPageError> {
140 let page_count = 1u64;
141 let mut flags = [u64::from(SevRmpAdjust::new().with_target_vmpl(match vtl {
142 GuestVtl::Vtl0 => 2,
143 GuestVtl::Vtl1 => 1,
144 })); 1];
145
146 let mut page_size = [0; 1];
147 let mut pages_processed = 0u64;
148
149 debug_assert!(flags.len() == page_count as usize);
150 debug_assert!(page_size.len() == page_count as usize);
151
152 let query = mshv_rmpquery {
153 start_pfn: gpa / HV_PAGE_SIZE,
154 page_count,
155 terminate_on_failure: 0,
156 ram: 0,
157 padding: Default::default(),
158 flags: flags.as_mut_ptr(),
159 page_size: page_size.as_mut_ptr(),
160 pages_processed: &mut pages_processed,
161 };
162
163 unsafe {
165 hcl_rmpquery_pages(self.file.as_raw_fd(), &query)
166 .map_err(SnpError::Os)
167 .map_err(SnpPageError::Rmpquery)?;
168 }
169
170 assert!(pages_processed <= page_count);
171
172 Ok(SevRmpAdjust::from(flags[0]))
173 }
174}
175
176impl<'a> super::private::BackingPrivate<'a> for Snp<'a> {
177 fn new(vp: &'a HclVp, sidecar: Option<&SidecarVp<'_>>, _hcl: &Hcl) -> Result<Self, NoRunner> {
178 assert!(sidecar.is_none());
179 let super::BackingState::Snp { vmsa } = &vp.backing else {
180 return Err(NoRunner::MismatchedIsolation);
181 };
182
183 Ok(Self {
184 vmsa: vmsa.each_ref().map(|mp| mp.as_ref()),
185 })
186 }
187
188 fn try_set_reg(
189 _runner: &mut ProcessorRunner<'a, Self>,
190 _vtl: GuestVtl,
191 _name: HvRegisterName,
192 _value: HvRegisterValue,
193 ) -> Result<bool, super::Error> {
194 Ok(false)
195 }
196
197 fn must_flush_regs_on(_runner: &ProcessorRunner<'a, Self>, _name: HvRegisterName) -> bool {
198 false
199 }
200
201 fn try_get_reg(
202 _runner: &ProcessorRunner<'a, Self>,
203 _vtl: GuestVtl,
204 _name: HvRegisterName,
205 ) -> Result<Option<HvRegisterValue>, super::Error> {
206 Ok(None)
207 }
208
209 fn flush_register_page(_runner: &mut ProcessorRunner<'a, Self>) {}
210}
211
212impl<'a> ProcessorRunner<'a, Snp<'a>> {
213 pub fn vmsa(&self, vtl: GuestVtl) -> VmsaWrapper<'_, &SevVmsa> {
215 let vmsa = unsafe { &*self.state.vmsa[vtl].get() };
218
219 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
220 }
221
222 pub fn vmsa_mut(&mut self, vtl: GuestVtl) -> VmsaWrapper<'_, &mut SevVmsa> {
224 let vmsa = unsafe { &mut *self.state.vmsa[vtl].get() };
227
228 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
229 }
230
231 pub fn vmsas_mut(&mut self) -> [VmsaWrapper<'_, &mut SevVmsa>; 2] {
233 self.state
234 .vmsa
235 .each_mut()
236 .map(|vmsa| {
237 let vmsa = unsafe { &mut *vmsa.get() };
240
241 VmsaWrapper::new(vmsa, &self.hcl.snp_register_bitmap)
242 })
243 .into_inner()
244 }
245}