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