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 cache_lines_flush_page(GHCB_GVA.into_bits());
552
553 pvalidate(page_number, GHCB_GVA.into_bits(), false, false).expect("memory unaccept");
555 let resp = Ghcb::ghcb_call(GhcbCall {
557 info: GhcbInfo::PAGE_STATE_CHANGE,
558 extra_data: x86defs::snp::GHCB_DATA_PAGE_STATE_SHARED,
559 page_number,
560 });
561 assert!(resp.into_bits() == GhcbInfo::PAGE_STATE_UPDATED.0);
562
563 page_table[PT_INDEX] = pte_for_pfn(page_number, false);
565 flush_tlb();
566 cache_lines_flush_page(GHCB_GVA.into_bits());
568
569 ghcb_access::zero_page();
572 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
573
574 let resp = Self::ghcb_call(GhcbCall {
577 extra_data: 0,
578 page_number: ghcb_access::page_number(),
579 info: GhcbInfo::REGISTER_REQUEST,
580 });
581 assert!(
582 resp.info() == GhcbInfo::REGISTER_RESPONSE.0
583 && resp.extra_data() == 0
584 && resp.pfn() == ghcb_access::page_number(),
585 "GhcbInfo::REGISTER_RESPONSE returned msr value {resp:x?}"
586 );
587
588 let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1);
590 assert!(Self::set_msr(
591 hvdef::HV_X64_MSR_GUEST_OS_ID,
592 guest_os_id.into()
593 ));
594 assert!(
596 Self::get_msr(hvdef::HV_X64_MSR_GUEST_OS_ID).expect("GHCB: Failed to set guest OS ID")
597 == guest_os_id.into()
598 );
599 Self::set_register(HvX64RegisterName::GuestOsId, guest_os_id.into_bits().into())
600 .expect("failed to set guest OS ID");
601
602 GHCB_PREVIOUS.replace(unsafe { read_msr(X86X_AMD_MSR_GHCB) });
604 }
605
606 pub fn uninitialize() {
607 let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new();
609 Self::set_register(HvX64RegisterName::GuestOsId, guest_os_id.into_bits().into())
610 .expect("failed to set guest OS ID");
611 assert!(Self::set_msr(
612 hvdef::HV_X64_MSR_GUEST_OS_ID,
613 guest_os_id.into()
614 ));
615 assert!(
617 Self::get_msr(hvdef::HV_X64_MSR_GUEST_OS_ID).expect("GHCB: Failed to set guest OS ID")
618 == guest_os_id.into()
619 );
620
621 let resp = Self::ghcb_call(GhcbCall {
634 extra_data: 0,
635 page_number: 0,
636 info: GhcbInfo::REGISTER_REQUEST,
637 });
638 assert!(
639 resp.info() == GhcbInfo::REGISTER_RESPONSE.0
640 && resp.extra_data() == 0
641 && resp.pfn() == 0,
642 "GhcbInfo::REGISTER_RESPONSE returned msr value {resp:x?}"
643 );
644
645 cache_lines_flush_page(GHCB_GVA.into_bits());
650
651 let page_table_pfn = (PAGE_TABLE.get() as u64) >> X64_PAGE_SHIFT;
654 let page_table = page_table(page_table_pfn);
655 let page_number = ghcb_access::page_number();
656
657 page_table[PT_INDEX] |= X64_PTE_CONFIDENTIAL;
658 flush_tlb();
659
660 let resp = Ghcb::ghcb_call(GhcbCall {
662 info: GhcbInfo::PAGE_STATE_CHANGE,
663 extra_data: x86defs::snp::GHCB_DATA_PAGE_STATE_PRIVATE,
664 page_number,
665 });
666 assert!(resp.into_bits() == GhcbInfo::PAGE_STATE_UPDATED.0);
667
668 pvalidate(page_number, GHCB_GVA.into_bits(), false, true).expect("memory accept");
670
671 flush_tlb();
672
673 ghcb_access::zero_page();
674
675 unsafe { write_msr(X86X_AMD_MSR_GHCB, GHCB_PREVIOUS.get()) };
677 }
678
679 fn io_port_exit(port: u16, access_size: IoAccessSize, is_read: bool, data: Option<u32>) {
680 ghcb_access::set_usage(GhcbUsage::BASE);
681 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
682 ghcb_access::clear_bitmaps();
683
684 let io_exit_info = SevIoAccessInfo::new()
685 .with_port(port)
686 .with_read_access(is_read);
687 let io_exit_info = match access_size {
688 IoAccessSize::Byte => io_exit_info.with_access_size8(true),
689 IoAccessSize::Word => io_exit_info.with_access_size16(true),
690 IoAccessSize::Dword => io_exit_info.with_access_size32(true),
691 };
692
693 ghcb_access::set_sw_exit_code(SevExitCode::IOIO.0);
694 ghcb_access::set_sw_exit_info1(io_exit_info.into_bits().into());
695 ghcb_access::set_sw_exit_info2(0);
696
697 if let Some(data) = data {
698 ghcb_access::set_rax(data as u64);
699 }
700
701 Self::ghcb_call(GhcbCall {
702 info: GhcbInfo::NORMAL,
703 extra_data: 0,
704 page_number: ghcb_access::page_number(),
705 });
706 ghcb_access::set_usage(GhcbUsage::INVALID);
707 }
708
709 #[must_use]
710 fn read_io_port(port: u16, access_size: IoAccessSize) -> Option<u32> {
711 Self::io_port_exit(port, access_size, true, None);
712
713 if ghcb_access::sw_exit_info1() != 0 {
714 None
715 } else {
716 Some(ghcb_access::rax() as u32)
717 }
718 }
719
720 #[must_use]
721 fn write_io_port(port: u16, access_size: IoAccessSize, data: u32) -> bool {
722 Self::io_port_exit(port, access_size, false, Some(data));
723
724 ghcb_access::sw_exit_info1() == 0
725 }
726
727 #[must_use]
728 pub fn set_msr(msr_index: u32, value: u64) -> bool {
729 ghcb_access::set_usage(GhcbUsage::BASE);
730 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
731 ghcb_access::clear_bitmaps();
732
733 ghcb_access::set_sw_exit_code(SevExitCode::MSR.0);
734 ghcb_access::set_sw_exit_info1(1);
735 ghcb_access::set_sw_exit_info2(0);
736
737 ghcb_access::set_rcx(msr_index as u64);
738 ghcb_access::set_rax(value as u32 as u64);
739 ghcb_access::set_rdx((value >> 32) as u32 as u64);
740
741 Self::ghcb_call(GhcbCall {
742 info: GhcbInfo::NORMAL,
743 extra_data: 0,
744 page_number: ghcb_access::page_number(),
745 });
746 ghcb_access::set_usage(GhcbUsage::INVALID);
747
748 ghcb_access::sw_exit_info1() == 0
749 }
750
751 #[must_use]
752 pub fn get_msr(msr_index: u32) -> Option<u64> {
753 ghcb_access::set_usage(GhcbUsage::BASE);
754 ghcb_access::set_protocol_version(GhcbProtocolVersion::V2);
755 ghcb_access::clear_bitmaps();
756
757 ghcb_access::set_sw_exit_code(SevExitCode::MSR.0);
758 ghcb_access::set_sw_exit_info1(0);
759 ghcb_access::set_sw_exit_info2(0);
760
761 ghcb_access::set_rcx(msr_index as u64);
762
763 Self::ghcb_call(GhcbCall {
764 info: GhcbInfo::NORMAL,
765 extra_data: 0,
766 page_number: ghcb_access::page_number(),
767 });
768 ghcb_access::set_usage(GhcbUsage::INVALID);
769
770 if ghcb_access::sw_exit_info1() != 0 {
771 None
772 } else {
773 Some(ghcb_access::rax() | (ghcb_access::rdx() << 32))
774 }
775 }
776
777 pub fn set_register(
778 name: HvX64RegisterName,
779 value: HvRegisterValue,
780 ) -> Result<(), hvdef::HvError> {
781 let header = hvdef::hypercall::GetSetVpRegisters {
782 partition_id: hvdef::HV_PARTITION_ID_SELF,
783 vp_index: hvdef::HV_VP_INDEX_SELF,
784 target_vtl: HvInputVtl::CURRENT_VTL,
785 rsvd: [0; 3],
786 };
787 let reg_assoc = hvdef::hypercall::HvRegisterAssoc {
788 name: name.into(),
789 pad: Default::default(),
790 value,
791 };
792 let control = hvdef::hypercall::Control::new()
793 .with_code(hvdef::HypercallCode::HvCallSetVpRegisters.0)
794 .with_rep_count(1);
795
796 ghcb_access::set_usage(GhcbUsage::HYPERCALL);
797 ghcb_access::set_hypercall_data(header.as_bytes(), 0);
798 ghcb_access::set_hypercall_data(reg_assoc.as_bytes(), size_of_val(&header));
799 ghcb_access::set_hypercall_input(control.into_bits());
800 ghcb_access::set_hypercall_output_gpa(0);
801
802 Self::ghcb_call(GhcbCall {
803 info: GhcbInfo::NORMAL,
804 extra_data: 0,
805 page_number: ghcb_access::page_number(),
806 });
807 ghcb_access::set_usage(GhcbUsage::INVALID);
808
809 HypercallOutput::from_bits(ghcb_access::hypercall_output()).result()
810 }
811}
812
813fn pvalidate(
815 page_number: u64,
816 va: u64,
817 large_page: bool,
818 validate: bool,
819) -> Result<AcceptGpaStatus, AcceptGpaError> {
820 if large_page {
821 assert!(va.is_multiple_of(x86defs::X64_LARGE_PAGE_SIZE));
822 } else {
823 assert!(va.is_multiple_of(hvdef::HV_PAGE_SIZE))
824 }
825
826 let validate_page = validate as u32;
827 let page_size = large_page as u32;
828 let mut error_code: u32;
829 let mut carry_flag: u32 = 0;
830
831 unsafe {
833 asm!(r#"
834 pvalidate
835 jnc 2f
836 inc {carry_flag:e}
837 2:
838 "#,
839 in("rax") va,
840 in("ecx") page_size,
841 in("edx") validate_page,
842 lateout("eax") error_code,
843 carry_flag = inout(reg) carry_flag);
844 }
845
846 const SEV_SUCCESS: u32 = 0;
847 const SEV_FAIL_SIZEMISMATCH: u32 = 6;
848
849 match (error_code, carry_flag) {
850 (SEV_SUCCESS, 0) => Ok(AcceptGpaStatus::Success),
851 (SEV_FAIL_SIZEMISMATCH, _) => Ok(AcceptGpaStatus::Retry),
852 _ => Err(AcceptGpaError::MemorySecurityViolation {
853 error_code,
854 carry_flag,
855 page_number,
856 large_page,
857 validate,
858 }),
859 }
860}
861
862pub fn set_page_acceptance(
865 local_map: &mut LocalMap<'_>,
866 range: MemoryRange,
867 validate: bool,
868) -> Result<(), AcceptGpaError> {
869 let pages_per_large_page = x86defs::X64_LARGE_PAGE_SIZE / X64_PAGE_SIZE;
870 let mut page_count = range.page_count_4k();
871 let mut page_base = range.start_4k_gpn();
872
873 while page_count != 0 {
874 let mapping = local_map.map_pages(
878 MemoryRange::from_4k_gpn_range(page_base..page_base + 1),
879 true,
880 );
881 if page_base.is_multiple_of(pages_per_large_page) && page_count >= pages_per_large_page {
882 let res = pvalidate(page_base, mapping.data.as_ptr() as u64, true, validate)?;
883 match res {
884 AcceptGpaStatus::Success => {
885 page_count -= pages_per_large_page;
886 page_base += pages_per_large_page;
887 continue;
888 }
889 AcceptGpaStatus::Retry => (),
890 }
891 }
892
893 let res = pvalidate(page_base, mapping.data.as_ptr() as u64, false, validate)?;
895 match res {
896 AcceptGpaStatus::Success => {
897 page_count -= 1;
898 page_base += 1;
899 }
900 AcceptGpaStatus::Retry => {
901 return Err(AcceptGpaError::Unknown);
903 }
904 }
905 }
906
907 Ok(())
908}
909
910#[cfg(feature = "cvm_boot_log")]
912pub struct SnpIoAccess;
913
914#[cfg(feature = "cvm_boot_log")]
915impl minimal_rt::arch::IoAccess for SnpIoAccess {
916 unsafe fn inb(&self, port: u16) -> u8 {
917 Ghcb::read_io_port(port, IoAccessSize::Byte).unwrap_or(!0) as u8
919 }
920
921 unsafe fn outb(&self, port: u16, data: u8) {
922 let _ = Ghcb::write_io_port(port, IoAccessSize::Byte, data as u32);
924 }
925}