igvmfilegen/vp_context_builder/
snp.rs1use crate::vp_context_builder::VpContextBuilder;
7use crate::vp_context_builder::VpContextPageState;
8use crate::vp_context_builder::VpContextState;
9use hvdef::Vtl;
10use igvm_defs::PAGE_SIZE_4K;
11use loader::importer::BootPageAcceptance;
12use loader::importer::SegmentRegister;
13use loader::importer::TableRegister;
14use loader::importer::X86Register;
15use loader::paravisor::HCL_SECURE_VTL;
16use std::fmt::Debug;
17use x86defs::X64_EFER_SVME;
18use x86defs::snp::SevSelector;
19use x86defs::snp::SevVmsa;
20use zerocopy::FromZeros;
21use zerocopy::IntoBytes;
22
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25pub enum InjectionType {
26 Normal,
28 Restricted,
30}
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34pub enum SecureAvic {
35 Enabled,
37 Disabled,
39}
40
41#[derive(Debug, Copy, Clone)]
42enum SnpContextKind {
43 Standard,
44 LinuxDirect { c_bit_mask: u64 },
45}
46
47fn table_register(reg: TableRegister) -> SevSelector {
48 SevSelector {
49 limit: reg.limit.into(),
50 base: reg.base,
51 ..FromZeros::new_zeroed()
52 }
53}
54
55fn segment_register(reg: SegmentRegister) -> SevSelector {
56 SevSelector {
57 limit: reg.limit,
58 base: reg.base,
59 selector: reg.selector,
60 attrib: (reg.attributes & 0xff) | ((reg.attributes >> 4) & 0xf00),
61 }
62}
63
64#[derive(Debug)]
66pub struct SnpHardwareContext {
67 accept_lower_1mb: bool,
70 page_number: Option<u64>,
72 vmsa: SevVmsa,
74 kind: SnpContextKind,
75}
76
77impl SnpHardwareContext {
78 pub fn new(
91 vtl: Vtl,
92 enlightened_uefi: bool,
93 shared_gpa_boundary: u64,
94 injection_type: InjectionType,
95 secure_avic: SecureAvic,
96 ) -> Self {
97 let mut vmsa: SevVmsa = FromZeros::new_zeroed();
98
99 vmsa.efer = X64_EFER_SVME;
101
102 vmsa.sev_features.set_snp(true);
104 vmsa.sev_features.set_vtom(true);
105 vmsa.virtual_tom = shared_gpa_boundary;
106 vmsa.sev_features.set_debug_swap(true);
107
108 if enlightened_uefi {
109 assert_eq!(injection_type, InjectionType::Restricted);
112 vmsa.sev_features.set_restrict_injection(true);
113 } else {
114 if vtl < HCL_SECURE_VTL {
120 vmsa.sev_features
121 .set_alternate_injection(injection_type == InjectionType::Restricted);
122 if injection_type == InjectionType::Normal {
123 vmsa.sev_features
124 .set_secure_avic(secure_avic == SecureAvic::Enabled);
125 vmsa.sev_features
126 .set_guest_intercept_control(secure_avic == SecureAvic::Enabled);
127 }
128 } else {
129 vmsa.sev_features
130 .set_restrict_injection(injection_type == InjectionType::Restricted);
131 vmsa.sev_features.set_snp_btb_isolation(true);
132 vmsa.sev_features.set_ibpb_on_entry(true);
133 vmsa.sev_features.set_prevent_host_ibs(true);
134 vmsa.sev_features.set_vmsa_reg_prot(true);
135 vmsa.sev_features.set_vtom(false);
136 vmsa.sev_features
137 .set_secure_avic(secure_avic == SecureAvic::Enabled);
138 vmsa.virtual_tom = 0;
139 }
140 }
141
142 vmsa.xcr0 = 0x1; SnpHardwareContext {
147 accept_lower_1mb: enlightened_uefi,
148 page_number: None,
149 vmsa,
150 kind: SnpContextKind::Standard,
151 }
152 }
153
154 pub fn new_linux_direct(c_bit_mask: u64, injection_type: InjectionType) -> Self {
156 let mut vmsa: SevVmsa = FromZeros::new_zeroed();
157 vmsa.efer = X64_EFER_SVME;
158 vmsa.sev_features.set_snp(true);
159 vmsa.sev_features
160 .set_restrict_injection(injection_type == InjectionType::Restricted);
161 vmsa.xcr0 = x86defs::xsave::XFEATURE_X87;
162 vmsa.rflags = u64::from(x86defs::RFlags::at_reset());
163 vmsa.dr6 = 0xffff_0ff0;
164 vmsa.dr7 = 0x400;
165 vmsa.tr = segment_register(SegmentRegister {
166 base: 0,
167 limit: 0xffff,
168 selector: 0,
169 attributes: 0x8b,
170 });
171 vmsa.ldtr = segment_register(SegmentRegister {
172 base: 0,
173 limit: 0xffff,
174 selector: 0,
175 attributes: 0x82,
176 });
177 vmsa.idtr = table_register(TableRegister {
178 base: 0,
179 limit: 0xffff,
180 });
181 vmsa.x87_fcw = x86defs::xsave::INIT_FCW;
182 vmsa.mxcsr = x86defs::xsave::DEFAULT_MXCSR;
183
184 Self {
185 accept_lower_1mb: false,
186 page_number: None,
187 vmsa,
188 kind: SnpContextKind::LinuxDirect { c_bit_mask },
189 }
190 }
191
192 #[cfg(test)]
194 pub fn vmsa(&self) -> &SevVmsa {
195 &self.vmsa
196 }
197}
198
199impl VpContextBuilder for SnpHardwareContext {
200 type Register = X86Register;
201
202 fn import_vp_register(&mut self, register: X86Register) {
203 match register {
204 X86Register::Gdtr(reg) => self.vmsa.gdtr = table_register(reg),
205 X86Register::Idtr(reg) => match self.kind {
206 SnpContextKind::Standard => panic!("Idtr not allowed for SNP"),
207 SnpContextKind::LinuxDirect { .. } => self.vmsa.idtr = table_register(reg),
208 },
209 X86Register::Ds(reg) => self.vmsa.ds = segment_register(reg),
210 X86Register::Es(reg) => self.vmsa.es = segment_register(reg),
211 X86Register::Fs(reg) => self.vmsa.fs = segment_register(reg),
212 X86Register::Gs(reg) => self.vmsa.gs = segment_register(reg),
213 X86Register::Ss(reg) => self.vmsa.ss = segment_register(reg),
214 X86Register::Cs(reg) => self.vmsa.cs = segment_register(reg),
215 X86Register::Tr(reg) => self.vmsa.tr = segment_register(reg),
216 X86Register::Cr0(reg) => {
217 self.vmsa.cr0 = match self.kind {
218 SnpContextKind::Standard => reg,
219 SnpContextKind::LinuxDirect { .. } => reg | x86defs::X64_CR0_ET,
220 }
221 }
222 X86Register::Cr3(reg) => {
223 self.vmsa.cr3 = match self.kind {
224 SnpContextKind::Standard => reg,
225 SnpContextKind::LinuxDirect { c_bit_mask } => reg | c_bit_mask,
226 }
227 }
228 X86Register::Cr4(reg) => {
229 self.vmsa.cr4 = match self.kind {
230 SnpContextKind::Standard => reg,
231 SnpContextKind::LinuxDirect { .. } => reg | x86defs::X64_CR4_MCE,
232 }
233 }
234 X86Register::Efer(reg) => {
235 self.vmsa.efer = reg | X64_EFER_SVME;
237 }
238 X86Register::Pat(reg) => self.vmsa.pat = reg,
239 X86Register::Rbp(reg) => self.vmsa.rbp = reg,
240 X86Register::Rip(reg) => self.vmsa.rip = reg,
241 X86Register::Rsi(reg) => self.vmsa.rsi = reg,
242 X86Register::Rsp(reg) => match self.kind {
243 SnpContextKind::Standard => panic!("rsp not allowed for SNP"),
244 SnpContextKind::LinuxDirect { .. } => self.vmsa.rsp = reg,
245 },
246 X86Register::R8(reg) => self.vmsa.r8 = reg,
247 X86Register::R9(reg) => self.vmsa.r9 = reg,
248 X86Register::R10(reg) => self.vmsa.r10 = reg,
249 X86Register::R11(reg) => self.vmsa.r11 = reg,
250 X86Register::R12(reg) => self.vmsa.r12 = reg,
251 X86Register::Rflags(reg) => match self.kind {
252 SnpContextKind::Standard => panic!("rflags not allowed for SNP"),
253 SnpContextKind::LinuxDirect { .. } => self.vmsa.rflags = reg,
254 },
255
256 X86Register::MtrrDefType(_)
257 | X86Register::MtrrPhysBase0(_)
258 | X86Register::MtrrPhysMask0(_)
259 | X86Register::MtrrPhysBase1(_)
260 | X86Register::MtrrPhysMask1(_)
261 | X86Register::MtrrPhysBase2(_)
262 | X86Register::MtrrPhysMask2(_)
263 | X86Register::MtrrPhysBase3(_)
264 | X86Register::MtrrPhysMask3(_)
265 | X86Register::MtrrPhysBase4(_)
266 | X86Register::MtrrPhysMask4(_)
267 | X86Register::MtrrFix64k00000(_)
268 | X86Register::MtrrFix16k80000(_)
269 | X86Register::MtrrFix4kE0000(_)
270 | X86Register::MtrrFix4kE8000(_)
271 | X86Register::MtrrFix4kF0000(_)
272 | X86Register::MtrrFix4kF8000(_) => {
273 tracing::warn!(?register, "Ignoring MTRR register for SNP.")
274 }
275 }
276 }
277
278 fn set_vp_context_memory(&mut self, page_base: u64) {
279 assert!(self.page_number.is_none(), "only allowed to set vmsa once");
280 self.page_number = Some(page_base);
281 }
282
283 fn finalize(&mut self, state: &mut Vec<VpContextState>) {
284 let Some(page_number) = self.page_number else {
285 return;
286 };
287
288 if self.accept_lower_1mb {
293 let mut trampoline_page = vec![0u8; PAGE_SIZE_4K as usize];
294
295 trampoline_page[..8].copy_from_slice(self.vmsa.rip.as_bytes());
299
300 let break_offset = size_of::<u64>();
303 trampoline_page[break_offset] = 0xCC;
304
305 let mut byte_offset = break_offset + 1;
307 self.vmsa.rip = byte_offset as u64;
308
309 let copy_instr =
310 |trampoline_page: &mut Vec<u8>, byte_offset, instruction: &[u8]| -> usize {
311 trampoline_page[byte_offset..byte_offset + instruction.len()]
312 .copy_from_slice(instruction);
313 byte_offset + instruction.len()
314 };
315
316 byte_offset = copy_instr(
318 &mut trampoline_page,
319 byte_offset,
320 &[0xBE, 0x00, 0x10, 0x00, 0x00],
321 );
322
323 byte_offset = copy_instr(
325 &mut trampoline_page,
326 byte_offset,
327 &[0xBB, 0x00, 0x00, 0x10, 0x00],
328 );
329
330 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x33, 0xC9]);
332
333 byte_offset = copy_instr(
335 &mut trampoline_page,
336 byte_offset,
337 &[0xBA, 0x01, 0x00, 0x00, 0x00],
338 );
339
340 let jump_offset = byte_offset;
342
343 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x8B, 0xC6]);
345
346 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0xF2, 0x0F, 0x01, 0xFF]);
348
349 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x72]);
351 byte_offset += 1;
352 trampoline_page[byte_offset - 1] = (break_offset as u8).wrapping_sub(byte_offset as u8);
353
354 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x48, 0x85, 0xC0]);
356
357 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x75]);
359 byte_offset += 1;
360 trampoline_page[byte_offset - 1] = (break_offset as u8).wrapping_sub(byte_offset as u8);
361
362 byte_offset = copy_instr(
364 &mut trampoline_page,
365 byte_offset,
366 &[0x81, 0xC6, 0x00, 0x10, 0x00, 0x00],
367 );
368
369 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x3B, 0xF3]);
371
372 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0x72]);
374 byte_offset += 1;
375 trampoline_page[byte_offset - 1] = (jump_offset as u8).wrapping_sub(byte_offset as u8);
376
377 byte_offset = copy_instr(&mut trampoline_page, byte_offset, &[0xFF, 0x25]);
379 let relative_offset: u32 = 0u32.wrapping_sub(byte_offset as u32 + 4);
380 trampoline_page[byte_offset..byte_offset + 4]
381 .copy_from_slice(relative_offset.as_bytes());
382
383 state.push(VpContextState::Page(VpContextPageState {
384 page_base: 0,
385 page_count: 1,
386 acceptance: BootPageAcceptance::Exclusive,
387 data: trampoline_page,
388 }));
389 }
390
391 state.push(VpContextState::Page(VpContextPageState {
392 page_base: page_number,
393 page_count: 1,
394 acceptance: BootPageAcceptance::VpContext,
395 data: self.vmsa.as_bytes().to_vec(),
396 }));
397 }
398}