1use super::address_space::LocalMap;
7use core::arch::asm;
8use memory_range::MemoryRange;
9use minimal_rt::arch::msr::read_msr;
10use minimal_rt::arch::msr::write_msr;
11use x86defs::X64_PAGE_SIZE;
12use x86defs::X86X_AMD_MSR_GHCB;
13use x86defs::snp::GhcbInfo;
14use x86defs::snp::GhcbMsr;
15
16#[cfg(feature = "cvm_boot_log")]
17use {
18 super::address_space::PAGE_TABLE_ENTRY_COUNT, super::address_space::X64_PAGE_SHIFT,
19 super::address_space::X64_PTE_ACCESSED, super::address_space::X64_PTE_PRESENT,
20 super::address_space::X64_PTE_READ_WRITE,
21 crate::arch::x86_64::address_space::X64_PTE_CONFIDENTIAL,
22 crate::single_threaded::SingleThreaded, bitfield_struct::bitfield, core::cell::Cell,
23 core::cell::UnsafeCell, core::sync::atomic::Ordering, core::sync::atomic::compiler_fence,
24 core::sync::atomic::fence, hvdef::HvRegisterValue, hvdef::HvX64RegisterName,
25 hvdef::hypercall::HvInputVtl, hvdef::hypercall::HypercallOutput,
26 x86defs::snp::GhcbProtocolVersion, x86defs::snp::GhcbUsage, x86defs::snp::SevExitCode,
27 x86defs::snp::SevIoAccessInfo, zerocopy::IntoBytes,
28};
29
30#[cfg(feature = "cvm_boot_log")]
31static GHCB_PREVIOUS: SingleThreaded<Cell<u64>> = SingleThreaded(Cell::new(0));
32
33pub struct Ghcb;
34
35#[derive(Debug)]
36pub enum AcceptGpaStatus {
37 Success,
38 Retry,
39}
40
41#[expect(dead_code)] #[derive(Debug)]
43pub enum AcceptGpaError {
44 MemorySecurityViolation {
45 error_code: u32,
46 carry_flag: u32,
47 page_number: u64,
48 large_page: bool,
49 validate: bool,
50 },
51 Unknown,
52}
53
54struct GhcbCall {
55 extra_data: u64,
56 page_number: u64,
57 info: GhcbInfo,
58}
59
60#[cfg(feature = "cvm_boot_log")]
64mod ghcb_page_mapping {
65 use super::*;
66
67 #[bitfield(u64)]
71 pub struct VirtAddr4Level {
72 #[bits(12)]
74 offset: usize,
75 #[bits(9)]
77 pt_index: usize,
78 #[bits(9)]
80 pd_index: usize,
81 #[bits(9)]
83 pdp_index: usize,
84 #[bits(9)]
86 pml4_index: usize,
87 #[bits(16)]
89 reserved: usize,
90 }
91
92 impl VirtAddr4Level {
93 pub const fn canonicalize(&self) -> VirtAddr4Level {
94 Self::from_bits((self.into_bits().wrapping_shl(16) as i64).wrapping_shr(16) as u64)
97 }
98 }
99
100 #[repr(C, align(4096))]
102 pub struct PageTable {
103 pub entries: [u64; PAGE_TABLE_ENTRY_COUNT],
104 }
105
106 pub static PDP_TABLE: SingleThreaded<UnsafeCell<PageTable>> =
111 SingleThreaded(UnsafeCell::new(PageTable {
112 entries: [0; PAGE_TABLE_ENTRY_COUNT],
113 }));
114
115 pub static PD_TABLE: SingleThreaded<UnsafeCell<PageTable>> =
117 SingleThreaded(UnsafeCell::new(PageTable {
118 entries: [0; PAGE_TABLE_ENTRY_COUNT],
119 }));
120
121 pub static PAGE_TABLE: SingleThreaded<UnsafeCell<PageTable>> =
123 SingleThreaded(UnsafeCell::new(PageTable {
124 entries: [0; PAGE_TABLE_ENTRY_COUNT],
125 }));
126
127 pub const PML4_INDEX: usize = 0x1d0; pub const PDP_INDEX: usize = 0;
129 pub const PD_INDEX: usize = 0;
130 pub const PT_INDEX: usize = 0;
131 pub const GHCB_GVA: VirtAddr4Level = VirtAddr4Level::new()
132 .with_pt_index(PT_INDEX)
133 .with_pd_index(PD_INDEX)
134 .with_pdp_index(PDP_INDEX)
135 .with_pml4_index(PML4_INDEX)
136 .canonicalize();
137
138 pub fn get_cr3() -> u64 {
139 let mut cr3: u64;
140
141 unsafe {
143 asm!("mov {0}, cr3", out(reg) cr3, options(nostack));
144 }
145 cr3
146 }
147
148 pub fn cache_lines_flush_page(addr: u64) {
149 const FLUSH_SIZE: u64 = 64; let start = addr & !(X64_PAGE_SIZE - 1);
151 let end = start + X64_PAGE_SIZE;
152
153 fence(Ordering::SeqCst);
155
156 for addr in (start..end).step_by(FLUSH_SIZE as usize) {
157 unsafe {
159 asm!("clflush [{0}]", in(reg) addr, options(nostack));
160 }
161 }
162 }
163
164 pub fn flush_tlb() {
165 fence(Ordering::SeqCst);
166 unsafe {
169 asm!("mov cr3, {0}", in(reg) get_cr3(), options(nostack));
170 }
171 compiler_fence(Ordering::SeqCst);
172 }
173
174 pub fn page_table(pfn: u64) -> &'static mut [u64] {
175 unsafe {
177 core::slice::from_raw_parts_mut(
178 (pfn << X64_PAGE_SHIFT) as *mut u64,
179 PAGE_TABLE_ENTRY_COUNT,
180 )
181 }
182 }
183
184 pub fn pte_for_pfn(pfn: u64, confidential: bool) -> u64 {
185 let common =
186 X64_PTE_PRESENT | X64_PTE_ACCESSED | X64_PTE_READ_WRITE | (pfn << X64_PAGE_SHIFT);
187 if confidential {
188 common | X64_PTE_CONFIDENTIAL
189 } else {
190 common
191 }
192 }
193} #[cfg(feature = "cvm_boot_log")]
196use ghcb_page_mapping::*;
197
198#[cfg(feature = "cvm_boot_log")]
203mod ghcb_access {
204 use super::GHCB_GVA;
205 use crate::PageAlign;
206 use crate::arch::x86_64::address_space::X64_PAGE_SHIFT;
207 use crate::zeroed;
208 use core::mem::offset_of;
209 use core::sync::atomic::AtomicU8;
210 use core::sync::atomic::AtomicU16;
211 use core::sync::atomic::AtomicU32;
212 use core::sync::atomic::AtomicU64;
213 use core::sync::atomic::Ordering;
214 use x86defs::snp::GHCB_PAGE_HV_HYPERCALL_DATA_SIZE;
215 use x86defs::snp::GhcbPage;
216 use x86defs::snp::GhcbPageHvHypercall;
217 use x86defs::snp::GhcbProtocolVersion;
218 use x86defs::snp::GhcbSaveArea;
219 use x86defs::snp::GhcbUsage;
220
221 static GHCB: PageAlign<[u8; size_of::<GhcbPage>()]> = zeroed();
228
229 pub fn page_number() -> u64 {
230 let gva = GHCB.0.as_ptr() as u64;
232 gva >> X64_PAGE_SHIFT
233 }
234
235 unsafe fn ghcb_data<T>() -> &'static mut [T] {
240 unsafe {
244 core::slice::from_raw_parts_mut(
245 GHCB_GVA.into_bits() as *mut T,
246 size_of::<GhcbPage>() / size_of::<T>(),
247 )
248 }
249 }
250
251 macro_rules! ghcb_field_set {
258 ($field:ident, $type:ty, $val:expr) => {{
259 let ghcb_data = unsafe { ghcb_data::<$type>() };
261 let pos = offset_of!(GhcbPage, $field) / size_of::<$type>();
262 ghcb_data[pos].store($val, Ordering::SeqCst);
263 }};
264 }
265
266 macro_rules! ghcb_save_field_set {
267 ($field:ident, $type:ty, $func:ident, $val:expr) => {{
268 let ghcb_data = unsafe { ghcb_data::<$type>() };
270 let pos = offset_of!(GhcbSaveArea, $field) / size_of::<$type>();
272 ghcb_data[pos].$func($val, Ordering::SeqCst);
273 }};
274 }
275
276 macro_rules! ghcb_save_field_get {
278 ($field:ident, $type:ty) => {{
279 let ghcb_data = unsafe { ghcb_data::<$type>() };
281 let pos = offset_of!(GhcbSaveArea, $field) / size_of::<$type>();
283 ghcb_data[pos].load(Ordering::SeqCst)
284 }};
285 }
286
287 pub fn zero_page() {
288 unsafe { ghcb_data::<AtomicU64>() }
290 .iter()
291 .for_each(|x| x.store(0, Ordering::SeqCst));
292 }
293
294 pub fn clear_bitmaps() {
295 ghcb_save_field_set!(valid_bitmap0, AtomicU64, store, 0);
296 ghcb_save_field_set!(valid_bitmap1, AtomicU64, store, 0);
297 }
298
299 macro_rules! ghcb_save_set_valid_bitmap0 {
300 ($save_field:ident) => {{
301 let mask = 1u64 << (offset_of!(GhcbSaveArea, $save_field) / 8);
302 ghcb_save_field_set!(valid_bitmap0, AtomicU64, fetch_or, mask);
303 }};
304 }
305
306 macro_rules! ghcb_save_set_valid_bitmap1 {
307 ($save_field:ident) => {{
308 let mask = 1u64 << (offset_of!(GhcbSaveArea, $save_field) / 8 - 64);
309 ghcb_save_field_set!(valid_bitmap1, AtomicU64, fetch_or, mask);
310 }};
311 }
312
313 macro_rules! ghcb_save_assert_valid_bitmap0 {
315 ($save_field:ident) => {{
316 let mask = 1u64 << (offset_of!(GhcbSaveArea, $save_field) / 8);
317 assert_eq!(ghcb_save_field_get!(valid_bitmap0, AtomicU64) & mask, mask);
318 }};
319 }
320
321 macro_rules! ghcb_save_assert_valid_bitmap1 {
323 ($save_field:ident) => {{
324 let mask = 1u64 << (offset_of!(GhcbSaveArea, $save_field) / 8 - 64);
325 assert_eq!(ghcb_save_field_get!(valid_bitmap1, AtomicU64) & mask, mask);
326 }};
327 }
328
329 pub fn set_usage(usage: GhcbUsage) {
330 ghcb_field_set!(ghcb_usage, AtomicU32, usage.into_bits());
331 }
332
333 pub fn set_protocol_version(version: GhcbProtocolVersion) {
334 ghcb_field_set!(protocol_version, AtomicU16, version.into_bits());
335 }
336
337 pub fn set_sw_exit_code(code: u64) {
338 ghcb_save_field_set!(sw_exit_code, AtomicU64, store, code);
339 ghcb_save_set_valid_bitmap1!(sw_exit_code);
340 }
341
342 pub fn set_sw_exit_info1(info: u64) {
343 ghcb_save_field_set!(sw_exit_info1, AtomicU64, store, info);
344 ghcb_save_set_valid_bitmap1!(sw_exit_info1);
345 }
346
347 pub fn sw_exit_info1() -> u64 {
348 ghcb_save_assert_valid_bitmap1!(sw_exit_info1);
349 ghcb_save_field_get!(sw_exit_info1, AtomicU64)
350 }
351
352 pub fn set_sw_exit_info2(info: u64) {
353 ghcb_save_field_set!(sw_exit_info2, AtomicU64, store, info);
354 ghcb_save_set_valid_bitmap1!(sw_exit_info2);
355 }
356
357 pub fn set_rax(rax: u64) {
358 ghcb_save_field_set!(rax, AtomicU64, store, rax);
359 ghcb_save_set_valid_bitmap0!(rax);
360 }
361
362 pub fn rax() -> u64 {
363 ghcb_save_assert_valid_bitmap0!(rax);
364 ghcb_save_field_get!(rax, AtomicU64)
365 }
366
367 pub fn set_rcx(rcx: u64) {
368 ghcb_save_field_set!(rcx, AtomicU64, store, rcx);
369 ghcb_save_set_valid_bitmap1!(rcx);
370 }
371
372 pub fn set_rdx(rdx: u64) {
373 ghcb_save_field_set!(rdx, AtomicU64, store, rdx);
374 ghcb_save_set_valid_bitmap1!(rdx);
375 }
376
377 pub fn rdx() -> u64 {
378 ghcb_save_assert_valid_bitmap1!(rdx);
379 ghcb_save_field_get!(rdx, AtomicU64)
380 }
381
382 unsafe fn ghcb_hv_hypercall<T>() -> &'static mut [T] {
389 unsafe {
393 core::slice::from_raw_parts_mut(
394 GHCB_GVA.into_bits() as *mut T,
395 size_of::<GhcbPageHvHypercall>() / size_of::<T>(),
396 )
397 }
398 }
399
400 macro_rules! ghcb_hv_hypercall_field_set {
401 ($field:ident, $type:ty, $val:expr) => {{
402 let ghcb_data = unsafe { ghcb_hv_hypercall::<$type>() };
404 let pos = offset_of!(GhcbPageHvHypercall, $field) / size_of::<$type>();
405 ghcb_data[pos].store($val, Ordering::SeqCst);
406 }};
407 }
408
409 macro_rules! ghcb_hv_hypercall_field_get {
410 ($field:ident, $type:ty) => {{
411 let ghcb_data = unsafe { ghcb_hv_hypercall::<$type>() };
413 let pos = offset_of!(GhcbPageHvHypercall, $field) / size_of::<$type>();
414 ghcb_data[pos].load(Ordering::SeqCst)
415 }};
416 }
417
418 pub fn set_hypercall_data(data: &[u8], start: usize) {
419 let ghcb_data = unsafe { ghcb_hv_hypercall::<AtomicU8>() };
421 assert!(start <= GHCB_PAGE_HV_HYPERCALL_DATA_SIZE);
422 assert!(data.len() <= GHCB_PAGE_HV_HYPERCALL_DATA_SIZE - start);
423
424 ghcb_data[start..start + data.len()]
425 .iter()
426 .zip(data.iter())
427 .for_each(|(x, y)| x.store(*y, Ordering::SeqCst));
428 }
429
430 pub fn set_hypercall_input(input: u64) {
431 ghcb_hv_hypercall_field_set!(io, AtomicU64, input);
432 }
433
434 pub fn hypercall_output() -> u64 {
435 ghcb_hv_hypercall_field_get!(io, AtomicU64)
436 }
437
438 pub fn set_hypercall_output_gpa(gpa: u64) {
439 ghcb_hv_hypercall_field_set!(output_gpa, AtomicU64, gpa);
440 }
441}
442
443#[cfg(feature = "cvm_boot_log")]
444#[expect(dead_code)]
445enum IoAccessSize {
446 Byte = 1,
447 Word = 2,
448 Dword = 4,
449}
450
451impl Ghcb {
452 #[inline(always)]
453 fn vmg_exit() {
454 unsafe {
457 asm!("rep vmmcall", options(nostack));
458 }
459 }
460
461 fn ghcb_call(call_data: GhcbCall) -> GhcbMsr {
463 let GhcbCall {
464 info,
465 extra_data,
466 page_number,
467 } = call_data;
468 let ghcb_control = GhcbMsr::new()
469 .with_pfn(page_number)
470 .with_info(info.0)
471 .with_extra_data(extra_data);
472
473 GhcbMsr::from_bits(
474 unsafe {
476 write_msr(X86X_AMD_MSR_GHCB, ghcb_control.into_bits());
477 Self::vmg_exit();
478 read_msr(X86X_AMD_MSR_GHCB)
479 },
480 )
481 }
482
483 pub fn change_page_visibility(range: MemoryRange, host_visible: bool) {
484 for page_number in range.start_4k_gpn()..range.end_4k_gpn() {
485 let extra_data = if host_visible {
486 x86defs::snp::GHCB_DATA_PAGE_STATE_SHARED
487 } else {
488 x86defs::snp::GHCB_DATA_PAGE_STATE_PRIVATE
489 };
490
491 let resp = Self::ghcb_call(GhcbCall {
492 info: GhcbInfo::PAGE_STATE_CHANGE,
493 extra_data,
494 page_number,
495 });
496
497 assert!(
501 resp.into_bits() == GhcbInfo::PAGE_STATE_UPDATED.0,
502 "GhcbInfo::PAGE_STATE_UPDATED returned msr value {resp:x?}"
503 );
504 }
505 }
506}
507
508#[cfg(feature = "cvm_boot_log")]
511impl Ghcb {
512 pub fn initialize() {
513 assert_eq!((PAGE_TABLE.get() as u64) & (X64_PAGE_SIZE - 1), 0);
515 assert_eq!((PD_TABLE.get() as u64) & (X64_PAGE_SIZE - 1), 0);
516 assert_eq!((PDP_TABLE.get() as u64) & (X64_PAGE_SIZE - 1), 0);
517
518 let page_root = get_cr3() & !(X64_PAGE_SIZE - 1);
521 let pml4table = page_table(page_root >> X64_PAGE_SHIFT);
522 assert!(pml4table[PML4_INDEX] & X64_PTE_PRESENT == 0);
523
524 let pdp_table_pfn = (PDP_TABLE.get() as u64) >> X64_PAGE_SHIFT;
526 let pd_table_pfn = (PD_TABLE.get() as u64) >> X64_PAGE_SHIFT;
527 let page_table_pfn = (PAGE_TABLE.get() as u64) >> X64_PAGE_SHIFT;
528 let page_number = ghcb_access::page_number();
529
530 let pdp_table = page_table(pdp_table_pfn);
531 let pd_table = page_table(pd_table_pfn);
532 let page_table = page_table(page_table_pfn);
533
534 pml4table[PML4_INDEX] = pte_for_pfn(pdp_table_pfn, true);
535 pdp_table[PDP_INDEX] = pte_for_pfn(pd_table_pfn, true);
536 pd_table[PD_INDEX] = pte_for_pfn(page_table_pfn, true);
537 page_table[PT_INDEX] = pte_for_pfn(page_number, true);
538
539 flush_tlb();
540 cache_lines_flush_page(GHCB_GVA.into_bits());
542
543 pvalidate(page_number, GHCB_GVA.into_bits(), false, false).expect("memory unaccept");
545 let resp = Ghcb::ghcb_call(GhcbCall {
547 info: GhcbInfo::PAGE_STATE_CHANGE,
548 extra_data: x86defs::snp::GHCB_DATA_PAGE_STATE_SHARED,
549 page_number,
550 });
551 assert!(resp.into_bits() == GhcbInfo::PAGE_STATE_UPDATED.0);
552
553 page_table[PT_INDEX] = pte_for_pfn(page_number, false);
555 flush_tlb();
556 cache_lines_flush_page(GHCB_GVA.into_bits());
558
559 ghcb_access::zero_page();
562 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
563
564 let resp = Self::ghcb_call(GhcbCall {
567 extra_data: 0,
568 page_number: ghcb_access::page_number(),
569 info: GhcbInfo::REGISTER_REQUEST,
570 });
571 assert!(
572 resp.info() == GhcbInfo::REGISTER_RESPONSE.0
573 && resp.extra_data() == 0
574 && resp.pfn() == ghcb_access::page_number(),
575 "GhcbInfo::REGISTER_RESPONSE returned msr value {resp:x?}"
576 );
577
578 let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1);
580 assert!(Self::set_msr(
581 hvdef::HV_X64_MSR_GUEST_OS_ID,
582 guest_os_id.into()
583 ));
584 assert!(
586 Self::get_msr(hvdef::HV_X64_MSR_GUEST_OS_ID).expect("GHCB: Failed to set guest OS ID")
587 == guest_os_id.into()
588 );
589 Self::set_register(HvX64RegisterName::GuestOsId, guest_os_id.into_bits().into())
590 .expect("failed to set guest OS ID");
591
592 GHCB_PREVIOUS.replace(unsafe { read_msr(X86X_AMD_MSR_GHCB) });
594 }
595
596 pub fn uninitialize() {
597 let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new();
599 Self::set_register(HvX64RegisterName::GuestOsId, guest_os_id.into_bits().into())
600 .expect("failed to set guest OS ID");
601 assert!(Self::set_msr(
602 hvdef::HV_X64_MSR_GUEST_OS_ID,
603 guest_os_id.into()
604 ));
605 assert!(
607 Self::get_msr(hvdef::HV_X64_MSR_GUEST_OS_ID).expect("GHCB: Failed to set guest OS ID")
608 == guest_os_id.into()
609 );
610
611 let resp = Self::ghcb_call(GhcbCall {
624 extra_data: 0,
625 page_number: 0,
626 info: GhcbInfo::REGISTER_REQUEST,
627 });
628 assert!(
629 resp.info() == GhcbInfo::REGISTER_RESPONSE.0
630 && resp.extra_data() == 0
631 && resp.pfn() == 0,
632 "GhcbInfo::REGISTER_RESPONSE returned msr value {resp:x?}"
633 );
634
635 cache_lines_flush_page(GHCB_GVA.into_bits());
640
641 let page_table_pfn = (PAGE_TABLE.get() as u64) >> X64_PAGE_SHIFT;
644 let page_table = page_table(page_table_pfn);
645 let page_number = ghcb_access::page_number();
646
647 page_table[PT_INDEX] |= X64_PTE_CONFIDENTIAL;
648 flush_tlb();
649
650 let resp = Ghcb::ghcb_call(GhcbCall {
652 info: GhcbInfo::PAGE_STATE_CHANGE,
653 extra_data: x86defs::snp::GHCB_DATA_PAGE_STATE_PRIVATE,
654 page_number,
655 });
656 assert!(resp.into_bits() == GhcbInfo::PAGE_STATE_UPDATED.0);
657
658 pvalidate(page_number, GHCB_GVA.into_bits(), false, true).expect("memory accept");
660
661 flush_tlb();
662
663 ghcb_access::zero_page();
664
665 unsafe { write_msr(X86X_AMD_MSR_GHCB, GHCB_PREVIOUS.get()) };
667 }
668
669 fn io_port_exit(port: u16, access_size: IoAccessSize, is_read: bool, data: Option<u32>) {
670 ghcb_access::set_usage(GhcbUsage::BASE);
671 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
672 ghcb_access::clear_bitmaps();
673
674 let io_exit_info = SevIoAccessInfo::new()
675 .with_port(port)
676 .with_read_access(is_read);
677 let io_exit_info = match access_size {
678 IoAccessSize::Byte => io_exit_info.with_access_size8(true),
679 IoAccessSize::Word => io_exit_info.with_access_size16(true),
680 IoAccessSize::Dword => io_exit_info.with_access_size32(true),
681 };
682
683 ghcb_access::set_sw_exit_code(SevExitCode::IOIO.0);
684 ghcb_access::set_sw_exit_info1(io_exit_info.into_bits().into());
685 ghcb_access::set_sw_exit_info2(0);
686
687 if let Some(data) = data {
688 ghcb_access::set_rax(data as u64);
689 }
690
691 Self::ghcb_call(GhcbCall {
692 info: GhcbInfo::NORMAL,
693 extra_data: 0,
694 page_number: ghcb_access::page_number(),
695 });
696 ghcb_access::set_usage(GhcbUsage::INVALID);
697 }
698
699 #[must_use]
700 fn read_io_port(port: u16, access_size: IoAccessSize) -> Option<u32> {
701 Self::io_port_exit(port, access_size, true, None);
702
703 if ghcb_access::sw_exit_info1() != 0 {
704 None
705 } else {
706 Some(ghcb_access::rax() as u32)
707 }
708 }
709
710 #[must_use]
711 fn write_io_port(port: u16, access_size: IoAccessSize, data: u32) -> bool {
712 Self::io_port_exit(port, access_size, false, Some(data));
713
714 ghcb_access::sw_exit_info1() == 0
715 }
716
717 #[must_use]
718 pub fn set_msr(msr_index: u32, value: u64) -> bool {
719 ghcb_access::set_usage(GhcbUsage::BASE);
720 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
721 ghcb_access::clear_bitmaps();
722
723 ghcb_access::set_sw_exit_code(SevExitCode::MSR.0);
724 ghcb_access::set_sw_exit_info1(1);
725 ghcb_access::set_sw_exit_info2(0);
726
727 ghcb_access::set_rcx(msr_index as u64);
728 ghcb_access::set_rax(value as u32 as u64);
729 ghcb_access::set_rdx((value >> 32) as u32 as u64);
730
731 Self::ghcb_call(GhcbCall {
732 info: GhcbInfo::NORMAL,
733 extra_data: 0,
734 page_number: ghcb_access::page_number(),
735 });
736 ghcb_access::set_usage(GhcbUsage::INVALID);
737
738 ghcb_access::sw_exit_info1() == 0
739 }
740
741 #[must_use]
742 pub fn get_msr(msr_index: u32) -> Option<u64> {
743 ghcb_access::set_usage(GhcbUsage::BASE);
744 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
745 ghcb_access::clear_bitmaps();
746
747 ghcb_access::set_sw_exit_code(SevExitCode::MSR.0);
748 ghcb_access::set_sw_exit_info1(0);
749 ghcb_access::set_sw_exit_info2(0);
750
751 ghcb_access::set_rcx(msr_index as u64);
752
753 Self::ghcb_call(GhcbCall {
754 info: GhcbInfo::NORMAL,
755 extra_data: 0,
756 page_number: ghcb_access::page_number(),
757 });
758 ghcb_access::set_usage(GhcbUsage::INVALID);
759
760 if ghcb_access::sw_exit_info1() != 0 {
761 None
762 } else {
763 Some(ghcb_access::rax() | (ghcb_access::rdx() << 32))
764 }
765 }
766
767 pub fn set_register(
768 name: HvX64RegisterName,
769 value: HvRegisterValue,
770 ) -> Result<(), hvdef::HvError> {
771 let header = hvdef::hypercall::GetSetVpRegisters {
772 partition_id: hvdef::HV_PARTITION_ID_SELF,
773 vp_index: hvdef::HV_VP_INDEX_SELF,
774 target_vtl: HvInputVtl::CURRENT_VTL,
775 rsvd: [0; 3],
776 };
777 let reg_assoc = hvdef::hypercall::HvRegisterAssoc {
778 name: name.into(),
779 pad: Default::default(),
780 value,
781 };
782 let control = hvdef::hypercall::Control::new()
783 .with_code(hvdef::HypercallCode::HvCallSetVpRegisters.0)
784 .with_rep_count(1);
785
786 ghcb_access::set_usage(GhcbUsage::HYPERCALL);
787 ghcb_access::set_hypercall_data(header.as_bytes(), 0);
788 ghcb_access::set_hypercall_data(reg_assoc.as_bytes(), size_of_val(&header));
789 ghcb_access::set_hypercall_input(control.into_bits());
790 ghcb_access::set_hypercall_output_gpa(0);
791
792 Self::ghcb_call(GhcbCall {
793 info: GhcbInfo::NORMAL,
794 extra_data: 0,
795 page_number: ghcb_access::page_number(),
796 });
797 ghcb_access::set_usage(GhcbUsage::INVALID);
798
799 HypercallOutput::from_bits(ghcb_access::hypercall_output()).result()
800 }
801}
802
803fn pvalidate(
805 page_number: u64,
806 va: u64,
807 large_page: bool,
808 validate: bool,
809) -> Result<AcceptGpaStatus, AcceptGpaError> {
810 if large_page {
811 assert!(va.is_multiple_of(x86defs::X64_LARGE_PAGE_SIZE));
812 } else {
813 assert!(va.is_multiple_of(hvdef::HV_PAGE_SIZE))
814 }
815
816 let validate_page = validate as u32;
817 let page_size = large_page as u32;
818 let mut error_code: u32;
819 let mut carry_flag: u32 = 0;
820
821 unsafe {
823 asm!(r#"
824 pvalidate
825 jnc 2f
826 inc {carry_flag:e}
827 2:
828 "#,
829 in("rax") va,
830 in("ecx") page_size,
831 in("edx") validate_page,
832 lateout("eax") error_code,
833 carry_flag = inout(reg) carry_flag);
834 }
835
836 const SEV_SUCCESS: u32 = 0;
837 const SEV_FAIL_SIZEMISMATCH: u32 = 6;
838
839 match (error_code, carry_flag) {
840 (SEV_SUCCESS, 0) => Ok(AcceptGpaStatus::Success),
841 (SEV_FAIL_SIZEMISMATCH, _) => Ok(AcceptGpaStatus::Retry),
842 _ => Err(AcceptGpaError::MemorySecurityViolation {
843 error_code,
844 carry_flag,
845 page_number,
846 large_page,
847 validate,
848 }),
849 }
850}
851
852pub fn set_page_acceptance(
855 local_map: &mut LocalMap<'_>,
856 range: MemoryRange,
857 validate: bool,
858) -> Result<(), AcceptGpaError> {
859 let pages_per_large_page = x86defs::X64_LARGE_PAGE_SIZE / X64_PAGE_SIZE;
860 let mut page_count = range.page_count_4k();
861 let mut page_base = range.start_4k_gpn();
862
863 while page_count != 0 {
864 let mapping = local_map.map_pages(
868 MemoryRange::from_4k_gpn_range(page_base..page_base + 1),
869 true,
870 );
871 if page_base.is_multiple_of(pages_per_large_page) && page_count >= pages_per_large_page {
872 let res = pvalidate(page_base, mapping.data.as_ptr() as u64, true, validate)?;
873 match res {
874 AcceptGpaStatus::Success => {
875 page_count -= pages_per_large_page;
876 page_base += pages_per_large_page;
877 continue;
878 }
879 AcceptGpaStatus::Retry => (),
880 }
881 }
882
883 let res = pvalidate(page_base, mapping.data.as_ptr() as u64, false, validate)?;
885 match res {
886 AcceptGpaStatus::Success => {
887 page_count -= 1;
888 page_base += 1;
889 }
890 AcceptGpaStatus::Retry => {
891 return Err(AcceptGpaError::Unknown);
893 }
894 }
895 }
896
897 Ok(())
898}
899
900#[cfg(feature = "cvm_boot_log")]
902pub struct SnpIoAccess;
903
904#[cfg(feature = "cvm_boot_log")]
905impl minimal_rt::arch::IoAccess for SnpIoAccess {
906 unsafe fn inb(&self, port: u16) -> u8 {
907 Ghcb::read_io_port(port, IoAccessSize::Byte).unwrap_or(!0) as u8
909 }
910
911 unsafe fn outb(&self, port: u16, data: u8) {
912 let _ = Ghcb::write_io_port(port, IoAccessSize::Byte, data as u32);
914 }
915}