1use super::SegmentRegister;
12use super::TableRegister;
13use super::X86InitialRegs;
14use super::vp;
15use thiserror::Error;
16use x86defs::SegmentAttributes;
17use x86defs::snp::SevSelector;
18use x86defs::snp::SevVmsa;
19use zerocopy::FromZeros;
20
21const _: () = assert!(size_of::<SevVmsa>() <= hvdef::HV_PAGE_SIZE as usize);
22
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25pub struct SnpVmsaState {
26 pub registers: vp::Registers,
28 pub pat: u64,
30 pub xcr0: u64,
32 pub restricted_injection: bool,
34}
35
36#[derive(Debug, Error)]
38pub enum SnpVmsaError {
39 #[error("unsupported SNP VMSA state")]
41 UnsupportedState,
42}
43
44#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
46pub struct SnpVmsaConfig {
47 pub restricted_injection: bool,
49}
50
51pub fn snp_id_block(id_block: &crate::SnpIdBlock, policy: u64) -> x86defs::snp::SnpPspIdBlock {
53 x86defs::snp::SnpPspIdBlock {
54 ld: id_block.launch_digest,
55 family_id: id_block.family_id,
56 image_id: id_block.image_id,
57 version: id_block.version,
58 guest_svn: id_block.guest_svn,
59 policy,
60 }
61}
62
63pub fn snp_id_auth(id_block: &crate::SnpIdBlock) -> Box<x86defs::snp::SnpPspIdAuth> {
65 let mut auth = Box::new(x86defs::snp::SnpPspIdAuth::new_zeroed());
66 auth.id_key_algorithm = id_block.id_key_algorithm;
67 auth.author_key_algorithm = id_block.author_key_algorithm;
68 auth.id_block_signature.r = id_block.id_key_signature.r;
69 auth.id_block_signature.s = id_block.id_key_signature.s;
70 auth.id_key.curve = id_block.id_public_key.curve;
71 auth.id_key.qx = id_block.id_public_key.qx;
72 auth.id_key.qy = id_block.id_public_key.qy;
73 auth.id_key_signature.r = id_block.author_key_signature.r;
74 auth.id_key_signature.s = id_block.author_key_signature.s;
75 auth.author_key.curve = id_block.author_public_key.curve;
76 auth.author_key.qx = id_block.author_public_key.qx;
77 auth.author_key.qy = id_block.author_public_key.qy;
78 auth
79}
80
81pub fn vmsa_from_initial_regs(initial: &X86InitialRegs, config: SnpVmsaConfig) -> SevVmsa {
83 vmsa_from_state(&SnpVmsaState {
84 registers: initial.registers,
85 pat: initial.pat.value,
86 xcr0: x86defs::xsave::XFEATURE_X87,
87 restricted_injection: config.restricted_injection,
88 })
89}
90
91pub fn state_from_vmsa(vmsa: &SevVmsa) -> Result<SnpVmsaState, SnpVmsaError> {
93 let state = SnpVmsaState {
94 registers: vp::Registers {
95 rax: vmsa.rax,
96 rcx: vmsa.rcx,
97 rdx: vmsa.rdx,
98 rbx: vmsa.rbx,
99 rbp: vmsa.rbp,
100 rsp: vmsa.rsp,
101 rsi: vmsa.rsi,
102 rdi: vmsa.rdi,
103 r8: vmsa.r8,
104 r9: vmsa.r9,
105 r10: vmsa.r10,
106 r11: vmsa.r11,
107 r12: vmsa.r12,
108 r13: vmsa.r13,
109 r14: vmsa.r14,
110 r15: vmsa.r15,
111 rip: vmsa.rip,
112 rflags: vmsa.rflags,
113 cs: segment_from_vmsa(vmsa.cs),
114 ds: segment_from_vmsa(vmsa.ds),
115 es: segment_from_vmsa(vmsa.es),
116 fs: segment_from_vmsa(vmsa.fs),
117 gs: segment_from_vmsa(vmsa.gs),
118 ss: segment_from_vmsa(vmsa.ss),
119 tr: segment_from_vmsa(vmsa.tr),
120 ldtr: segment_from_vmsa(vmsa.ldtr),
121 gdtr: table_from_vmsa(vmsa.gdtr),
122 idtr: table_from_vmsa(vmsa.idtr),
123 cr0: vmsa.cr0,
124 cr2: vmsa.cr2,
125 cr3: vmsa.cr3,
126 cr4: vmsa.cr4,
127 cr8: 0,
128 efer: vmsa.efer & !x86defs::X64_EFER_SVME,
129 },
130 pat: vmsa.pat,
131 xcr0: vmsa.xcr0,
132 restricted_injection: vmsa.sev_features.restrict_injection(),
133 };
134
135 if &vmsa_from_state(&state) != vmsa {
136 return Err(SnpVmsaError::UnsupportedState);
137 }
138
139 Ok(state)
140}
141
142fn vmsa_from_state(state: &SnpVmsaState) -> SevVmsa {
143 let registers = &state.registers;
144 let mut vmsa = SevVmsa::new_zeroed();
145
146 vmsa.es = segment_to_vmsa(registers.es);
147 vmsa.cs = segment_to_vmsa(registers.cs);
148 vmsa.ss = segment_to_vmsa(registers.ss);
149 vmsa.ds = segment_to_vmsa(registers.ds);
150 vmsa.fs = segment_to_vmsa(registers.fs);
151 vmsa.gs = segment_to_vmsa(registers.gs);
152 vmsa.gdtr = table_to_vmsa(registers.gdtr);
153 vmsa.ldtr = segment_to_vmsa(registers.ldtr);
154 vmsa.idtr = table_to_vmsa(registers.idtr);
155 vmsa.tr = segment_to_vmsa(registers.tr);
156 vmsa.cpl = SegmentAttributes::from(registers.cs.attributes).descriptor_privilege_level();
157 vmsa.efer = registers.efer | x86defs::X64_EFER_SVME;
158 vmsa.cr4 = registers.cr4;
159 vmsa.cr3 = registers.cr3;
160 vmsa.cr0 = registers.cr0;
161 vmsa.rflags = registers.rflags;
162 vmsa.rip = registers.rip;
163 vmsa.rsp = registers.rsp;
164 vmsa.rax = registers.rax;
165 vmsa.cr2 = registers.cr2;
166 vmsa.pat = state.pat;
167 vmsa.rcx = registers.rcx;
168 vmsa.rdx = registers.rdx;
169 vmsa.rbx = registers.rbx;
170 vmsa.rbp = registers.rbp;
171 vmsa.rsi = registers.rsi;
172 vmsa.rdi = registers.rdi;
173 vmsa.r8 = registers.r8;
174 vmsa.r9 = registers.r9;
175 vmsa.r10 = registers.r10;
176 vmsa.r11 = registers.r11;
177 vmsa.r12 = registers.r12;
178 vmsa.r13 = registers.r13;
179 vmsa.r14 = registers.r14;
180 vmsa.r15 = registers.r15;
181 vmsa.sev_features.set_snp(true);
182 vmsa.sev_features
183 .set_restrict_injection(state.restricted_injection);
184 vmsa.xcr0 = state.xcr0;
185
186 vmsa
187}
188
189fn segment_to_vmsa(register: SegmentRegister) -> SevSelector {
190 SevSelector {
191 selector: register.selector,
192 attrib: (register.attributes & 0xff) | ((register.attributes >> 4) & 0xf00),
193 limit: register.limit,
194 base: register.base,
195 }
196}
197
198fn segment_from_vmsa(selector: SevSelector) -> SegmentRegister {
199 SegmentRegister {
200 selector: selector.selector,
201 attributes: (selector.attrib & 0xff) | ((selector.attrib & 0xf00) << 4),
202 limit: selector.limit,
203 base: selector.base,
204 }
205}
206
207fn table_to_vmsa(register: TableRegister) -> SevSelector {
208 SevSelector {
209 selector: 0,
210 attrib: 0,
211 limit: register.limit as u32,
212 base: register.base,
213 }
214}
215
216fn table_from_vmsa(selector: SevSelector) -> TableRegister {
217 TableRegister {
218 limit: selector.limit as u16,
219 base: selector.base,
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 fn test_id_block() -> crate::SnpIdBlock {
228 crate::SnpIdBlock {
229 author_key_enabled: 1,
230 launch_digest: [0x11; 48],
231 family_id: [0x22; 16],
232 image_id: [0x33; 16],
233 version: 1,
234 guest_svn: 7,
235 id_key_algorithm: 0x01020304,
236 author_key_algorithm: 0x05060708,
237 id_key_signature: x86defs::snp::SnpIdBlockSignature {
238 r: [0x44; 72],
239 s: [0x55; 72],
240 },
241 id_public_key: x86defs::snp::SnpIdBlockPublicKey {
242 curve: 2,
243 qx: [0x66; 72],
244 qy: [0x77; 72],
245 },
246 author_key_signature: x86defs::snp::SnpIdBlockSignature {
247 r: [0x88; 72],
248 s: [0x99; 72],
249 },
250 author_public_key: x86defs::snp::SnpIdBlockPublicKey {
251 curve: 3,
252 qx: [0xaa; 72],
253 qy: [0xbb; 72],
254 },
255 }
256 }
257
258 #[test]
259 fn snp_id_block_preserves_fields_and_policy() {
260 let source = test_id_block();
261 let id_block = snp_id_block(&source, 0x1234);
262
263 assert_eq!(id_block.ld, source.launch_digest);
264 assert_eq!(id_block.family_id, source.family_id);
265 assert_eq!(id_block.image_id, source.image_id);
266 assert_eq!(id_block.version, source.version);
267 assert_eq!(id_block.guest_svn, source.guest_svn);
268 assert_eq!(id_block.policy, 0x1234);
269 }
270
271 #[test]
272 fn snp_id_auth_serializes_algorithms() {
273 let id_block = test_id_block();
274 let id_auth = snp_id_auth(&id_block);
275
276 assert_eq!(id_auth.id_key_algorithm, id_block.id_key_algorithm);
277 assert_eq!(id_auth.author_key_algorithm, id_block.author_key_algorithm);
278 }
279
280 #[test]
281 fn snp_id_auth_serializes_signatures_and_keys() {
282 let id_block = test_id_block();
283 let id_auth = snp_id_auth(&id_block);
284
285 assert_eq!(id_auth.id_block_signature.r, id_block.id_key_signature.r);
286 assert_eq!(id_auth.id_block_signature.s, id_block.id_key_signature.s);
287 assert_eq!(id_auth.id_key.curve, id_block.id_public_key.curve);
288 assert_eq!(id_auth.id_key.qx, id_block.id_public_key.qx);
289 assert_eq!(id_auth.id_key.qy, id_block.id_public_key.qy);
290 assert_eq!(id_auth.id_key_signature.r, id_block.author_key_signature.r);
291 assert_eq!(id_auth.id_key_signature.s, id_block.author_key_signature.s);
292 assert_eq!(id_auth.author_key.curve, id_block.author_public_key.curve);
293 assert_eq!(id_auth.author_key.qx, id_block.author_public_key.qx);
294 assert_eq!(id_auth.author_key.qy, id_block.author_public_key.qy);
295 }
296
297 #[test]
298 fn snp_id_auth_zero_pads_reserved_bytes() {
299 let id_auth = snp_id_auth(&test_id_block());
300
301 assert!(id_auth.reserved0.iter().all(|&byte| byte == 0));
302 assert!(
303 id_auth
304 .id_block_signature
305 .reserved
306 .iter()
307 .all(|&byte| byte == 0)
308 );
309 assert!(id_auth.id_key.reserved.iter().all(|&byte| byte == 0));
310 assert!(id_auth.reserved1.iter().all(|&byte| byte == 0));
311 assert!(
312 id_auth
313 .id_key_signature
314 .reserved
315 .iter()
316 .all(|&byte| byte == 0)
317 );
318 assert!(id_auth.author_key.reserved.iter().all(|&byte| byte == 0));
319 assert!(id_auth.reserved2.iter().all(|&byte| byte == 0));
320 }
321
322 #[test]
323 fn direct_boot_vmsa_round_trips() {
324 let initial = X86InitialRegs {
325 registers: vp::Registers {
326 rax: 1,
327 rdx: 2,
328 rip: 0x100000,
329 rflags: 2,
330 cs: SegmentRegister {
331 selector: 0x10,
332 attributes: 0xa09b,
333 limit: u32::MAX,
334 base: 0,
335 },
336 efer: x86defs::X64_EFER_LME | x86defs::X64_EFER_LMA,
337 ..Default::default()
338 },
339 mtrrs: Default::default(),
340 pat: vp::Pat {
341 value: 0x7040600070406,
342 },
343 };
344
345 let vmsa = vmsa_from_initial_regs(&initial, SnpVmsaConfig::default());
346 let state = state_from_vmsa(&vmsa).unwrap();
347
348 assert_eq!(state.registers, initial.registers);
349 assert_eq!(state.pat, initial.pat.value);
350 assert_eq!(state.xcr0, x86defs::xsave::XFEATURE_X87);
351 assert!(vmsa.sev_features.snp());
352 assert_ne!(vmsa.efer & x86defs::X64_EFER_SVME, 0);
353 }
354
355 #[test]
356 fn rejects_unsupported_state() {
357 let initial = X86InitialRegs {
358 registers: Default::default(),
359 mtrrs: Default::default(),
360 pat: Default::default(),
361 };
362 let mut vmsa = vmsa_from_initial_regs(&initial, SnpVmsaConfig::default());
363 vmsa.virtual_tom = 0x1000;
364
365 assert!(matches!(
366 state_from_vmsa(&vmsa),
367 Err(SnpVmsaError::UnsupportedState)
368 ));
369 }
370
371 #[test]
372 fn enables_restricted_injection() {
373 let initial = X86InitialRegs {
374 registers: Default::default(),
375 mtrrs: Default::default(),
376 pat: Default::default(),
377 };
378
379 let vmsa = vmsa_from_initial_regs(
380 &initial,
381 SnpVmsaConfig {
382 restricted_injection: true,
383 },
384 );
385
386 assert!(vmsa.sev_features.restrict_injection());
387 assert!(state_from_vmsa(&vmsa).unwrap().restricted_injection);
388 }
389}