Skip to content

Commit

Permalink
Merge pull request #98 from 00xc/sev/pvalidate
Browse files Browse the repository at this point in the history
types: explicitly type page size parameters
  • Loading branch information
joergroedel authored Sep 25, 2023
2 parents 1494d2c + 9f2e794 commit 739fa7f
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 57 deletions.
19 changes: 14 additions & 5 deletions src/fw_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::error::SvsmError;
use crate::mm::PerCPUPageMappingGuard;
use crate::mm::SIZE_1G;
use crate::sev::ghcb::PageStateChangeOp;
use crate::sev::{pvalidate, rmp_adjust, RMPFlags};
use crate::types::PAGE_SIZE;
use crate::sev::{pvalidate, rmp_adjust, PvalidateOp, RMPFlags};
use crate::types::{PageSize, PAGE_SIZE};
use crate::utils::{overlap, zero_mem_region};
use alloc::vec::Vec;

Expand Down Expand Up @@ -399,7 +399,12 @@ fn validate_fw_mem_region(region: SevPreValidMem) -> Result<(), SvsmError> {

this_cpu_mut()
.ghcb()
.page_state_change(pstart, pend, false, PageStateChangeOp::PscPrivate)
.page_state_change(
pstart,
pend,
PageSize::Regular,
PageStateChangeOp::PscPrivate,
)
.expect("GHCB PSC call failed to validate firmware memory");

for paddr in (pstart.bits()..pend.bits())
Expand All @@ -409,10 +414,14 @@ fn validate_fw_mem_region(region: SevPreValidMem) -> Result<(), SvsmError> {
let guard = PerCPUPageMappingGuard::create_4k(paddr)?;
let vaddr = guard.virt_addr();

pvalidate(vaddr, false, true)?;
pvalidate(vaddr, PageSize::Regular, PvalidateOp::Valid)?;

// Make page accessible to guest VMPL
rmp_adjust(vaddr, RMPFlags::GUEST_VMPL | RMPFlags::RWX, false)?;
rmp_adjust(
vaddr,
RMPFlags::GUEST_VMPL | RMPFlags::RWX,
PageSize::Regular,
)?;

zero_mem_region(vaddr, vaddr + PAGE_SIZE);
}
Expand Down
17 changes: 10 additions & 7 deletions src/protocols/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use crate::protocols::errors::SvsmReqError;
use crate::protocols::RequestParams;
use crate::sev::utils::{
pvalidate, rmp_clear_guest_vmsa, rmp_grant_guest_access, rmp_revoke_guest_access,
rmp_set_guest_vmsa, RMPFlags, SevSnpError,
rmp_set_guest_vmsa, PvalidateOp, RMPFlags, SevSnpError,
};
use crate::sev::vmsa::VMSA;
use crate::types::{PAGE_SIZE, PAGE_SIZE_2M};
use crate::types::{PageSize, PAGE_SIZE, PAGE_SIZE_2M};
use crate::utils::zero_mem_region;

const SVSM_REQ_CORE_REMAP_CA: u32 = 0;
Expand Down Expand Up @@ -198,12 +198,15 @@ fn core_configure_vtom(params: &mut RequestParams) -> Result<(), SvsmReqError> {

fn core_pvalidate_one(entry: u64, flush: &mut bool) -> Result<(), SvsmReqError> {
let (page_size_bytes, valign, huge) = match entry & 3 {
0 => (PAGE_SIZE, VIRT_ALIGN_4K, false),
1 => (PAGE_SIZE_2M, VIRT_ALIGN_2M, true),
0 => (PAGE_SIZE, VIRT_ALIGN_4K, PageSize::Regular),
1 => (PAGE_SIZE_2M, VIRT_ALIGN_2M, PageSize::Huge),
_ => return Err(SvsmReqError::invalid_parameter()),
};

let valid = (entry & 4) == 4;
let valid = match (entry & 4) == 4 {
true => PvalidateOp::Valid,
false => PvalidateOp::Invalid,
};
let ign_cf = (entry & 8) == 8;

let paddr = PhysAddr::from(entry).page_align();
Expand All @@ -220,7 +223,7 @@ fn core_pvalidate_one(entry: u64, flush: &mut bool) -> Result<(), SvsmReqError>
let guard = PerCPUPageMappingGuard::create(paddr, paddr + page_size_bytes, valign)?;
let vaddr = guard.virt_addr();

if !valid {
if valid == PvalidateOp::Invalid {
*flush |= true;
rmp_revoke_guest_access(vaddr, huge)?;
}
Expand All @@ -230,7 +233,7 @@ fn core_pvalidate_one(entry: u64, flush: &mut bool) -> Result<(), SvsmReqError>
_ => Err(err),
})?;

if valid {
if valid == PvalidateOp::Valid {
// Zero out a page when it is validated and before giving other VMPLs
// access to it. This is necessary to prevent a possible HV attack:
//
Expand Down
36 changes: 23 additions & 13 deletions src/sev/ghcb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ use crate::mm::validate::{
use crate::mm::virt_to_phys;
use crate::sev::sev_snp_enabled;
use crate::sev::utils::raw_vmgexit;
use crate::types::{PAGE_SIZE, PAGE_SIZE_2M};
use crate::types::{PageSize, PAGE_SIZE_2M};
use core::cell::RefCell;
use core::{mem, ptr};

use super::msr_protocol::{
invalidate_page_msr, register_ghcb_gpa_msr, request_termination_msr, validate_page_msr,
};
use super::pvalidate;
use super::{pvalidate, PvalidateOp};

// TODO: Fix this when Rust gets decent compile time struct offset support
const OFF_CPL: u16 = 0xcb;
Expand Down Expand Up @@ -143,7 +143,7 @@ impl GHCB {

if sev_snp_enabled() {
// Make page invalid
pvalidate(vaddr, false, false)?;
pvalidate(vaddr, PageSize::Regular, PvalidateOp::Invalid)?;

// Let the Hypervisor take the page back
invalidate_page_msr(paddr)?;
Expand Down Expand Up @@ -184,7 +184,7 @@ impl GHCB {
validate_page_msr(paddr)?;

// Make page guest-valid
pvalidate(vaddr, false, true)?;
pvalidate(vaddr, PageSize::Regular, PvalidateOp::Valid)?;

// Needs guarding for Stage2 GHCB
if valid_bitmap_valid_addr(paddr) {
Expand Down Expand Up @@ -375,12 +375,18 @@ impl GHCB {
Ok(())
}

pub fn psc_entry(&self, paddr: PhysAddr, op_mask: u64, current_page: u64, huge: bool) -> u64 {
assert!(!huge || paddr.is_aligned(PAGE_SIZE_2M));
pub fn psc_entry(
&self,
paddr: PhysAddr,
op_mask: u64,
current_page: u64,
size: PageSize,
) -> u64 {
assert!(size == PageSize::Regular || paddr.is_aligned(PAGE_SIZE_2M));

let mut entry: u64 =
((paddr.bits() as u64) & PSC_GFN_MASK) | op_mask | (current_page & 0xfffu64);
if huge {
if size == PageSize::Huge {
entry |= PSC_FLAG_HUGE;
}

Expand All @@ -391,7 +397,7 @@ impl GHCB {
&mut self,
start: PhysAddr,
end: PhysAddr,
huge: bool,
size: PageSize,
op: PageStateChangeOp,
) -> Result<(), SvsmError> {
// Maximum entries (8 bytes each_ minus 8 bytes for header
Expand All @@ -408,12 +414,16 @@ impl GHCB {
self.clear();

while paddr < end {
let huge = huge && paddr.is_aligned(PAGE_SIZE_2M) && paddr + PAGE_SIZE_2M <= end;
let pgsize: usize = match huge {
true => PAGE_SIZE_2M,
false => PAGE_SIZE,
let size = if size == PageSize::Huge
&& paddr.is_aligned(PAGE_SIZE_2M)
&& paddr + PAGE_SIZE_2M <= end
{
PageSize::Huge
} else {
PageSize::Regular
};
let entry = self.psc_entry(paddr, op_mask, 0, huge);
let pgsize = usize::from(size);
let entry = self.psc_entry(paddr, op_mask, 0, size);
let offset: isize = (entries as isize) * 8 + 8;
self.write_buffer(&entry, offset)?;
entries += 1;
Expand Down
2 changes: 1 addition & 1 deletion src/sev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ pub mod utils;
pub use status::sev_status_init;
pub use status::sev_status_verify;
pub use status::{sev_es_enabled, sev_snp_enabled};
pub use utils::{pvalidate, pvalidate_range, SevSnpError};
pub use utils::{pvalidate, pvalidate_range, PvalidateOp, SevSnpError};
pub use utils::{rmp_adjust, RMPFlags};
58 changes: 40 additions & 18 deletions src/sev/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use crate::address::{Address, VirtAddr};
use crate::error::SvsmError;
use crate::types::{GUEST_VMPL, PAGE_SIZE, PAGE_SIZE_2M};
use crate::types::{PageSize, GUEST_VMPL, PAGE_SIZE, PAGE_SIZE_2M};
use core::arch::asm;
use core::fmt;

Expand Down Expand Up @@ -50,43 +50,58 @@ impl fmt::Display for SevSnpError {
}
}

fn pvalidate_range_4k(start: VirtAddr, end: VirtAddr, valid: bool) -> Result<(), SvsmError> {
fn pvalidate_range_4k(start: VirtAddr, end: VirtAddr, valid: PvalidateOp) -> Result<(), SvsmError> {
for addr in (start.bits()..end.bits())
.step_by(PAGE_SIZE)
.map(VirtAddr::from)
{
pvalidate(addr, false, valid)?;
pvalidate(addr, PageSize::Regular, valid)?;
}

Ok(())
}

pub fn pvalidate_range(start: VirtAddr, end: VirtAddr, valid: bool) -> Result<(), SvsmError> {
pub fn pvalidate_range(
start: VirtAddr,
end: VirtAddr,
valid: PvalidateOp,
) -> Result<(), SvsmError> {
let mut addr = start;

while addr < end {
if addr.is_aligned(PAGE_SIZE_2M) && addr + PAGE_SIZE_2M <= end {
// Try to validate as a huge page.
// If we fail, try to fall back to regular-sized pages.
pvalidate(addr, true, valid).or_else(|err| match err {
pvalidate(addr, PageSize::Huge, valid).or_else(|err| match err {
SvsmError::SevSnp(SevSnpError::FAIL_SIZEMISMATCH(_)) => {
pvalidate_range_4k(addr, addr + PAGE_SIZE_2M, valid)
}
_ => Err(err),
})?;
addr = addr + PAGE_SIZE_2M;
} else {
pvalidate(addr, false, valid)?;
pvalidate(addr, PageSize::Regular, valid)?;
addr = addr + PAGE_SIZE;
}
}

Ok(())
}

pub fn pvalidate(vaddr: VirtAddr, huge_page: bool, valid: bool) -> Result<(), SvsmError> {
/// The desired state of the page passed to PVALIDATE.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u64)]
pub enum PvalidateOp {
Invalid = 0,
Valid = 1,
}

pub fn pvalidate(vaddr: VirtAddr, size: PageSize, valid: PvalidateOp) -> Result<(), SvsmError> {
let rax = vaddr.bits();
let rcx = huge_page as u64;
let rcx: u64 = match size {
PageSize::Regular => 0,
PageSize::Huge => 1,
};
let rdx = valid as u64;
let ret: u64;
let cf: u64;
Expand Down Expand Up @@ -143,8 +158,11 @@ bitflags::bitflags! {
}
}

pub fn rmp_adjust(addr: VirtAddr, flags: RMPFlags, huge: bool) -> Result<(), SvsmError> {
let rcx: usize = if huge { 1 } else { 0 };
pub fn rmp_adjust(addr: VirtAddr, flags: RMPFlags, size: PageSize) -> Result<(), SvsmError> {
let rcx: u64 = match size {
PageSize::Regular => 0,
PageSize::Huge => 1,
};
let rax: u64 = addr.bits() as u64;
let rdx: u64 = flags.bits();
let mut ret: u64;
Expand Down Expand Up @@ -182,24 +200,28 @@ pub fn rmp_adjust(addr: VirtAddr, flags: RMPFlags, huge: bool) -> Result<(), Svs
}
}

pub fn rmp_revoke_guest_access(vaddr: VirtAddr, huge: bool) -> Result<(), SvsmError> {
pub fn rmp_revoke_guest_access(vaddr: VirtAddr, size: PageSize) -> Result<(), SvsmError> {
for vmpl in RMPFlags::GUEST_VMPL.bits()..=RMPFlags::VMPL3.bits() {
let vmpl = RMPFlags::from_bits_truncate(vmpl);
rmp_adjust(vaddr, vmpl | RMPFlags::NONE, huge)?;
rmp_adjust(vaddr, vmpl | RMPFlags::NONE, size)?;
}
Ok(())
}

pub fn rmp_grant_guest_access(vaddr: VirtAddr, huge: bool) -> Result<(), SvsmError> {
rmp_adjust(vaddr, RMPFlags::GUEST_VMPL | RMPFlags::RWX, huge)
pub fn rmp_grant_guest_access(vaddr: VirtAddr, size: PageSize) -> Result<(), SvsmError> {
rmp_adjust(vaddr, RMPFlags::GUEST_VMPL | RMPFlags::RWX, size)
}

pub fn rmp_set_guest_vmsa(vaddr: VirtAddr) -> Result<(), SvsmError> {
rmp_revoke_guest_access(vaddr, false)?;
rmp_adjust(vaddr, RMPFlags::GUEST_VMPL | RMPFlags::VMSA, false)
rmp_revoke_guest_access(vaddr, PageSize::Regular)?;
rmp_adjust(
vaddr,
RMPFlags::GUEST_VMPL | RMPFlags::VMSA,
PageSize::Regular,
)
}

pub fn rmp_clear_guest_vmsa(vaddr: VirtAddr) -> Result<(), SvsmError> {
rmp_revoke_guest_access(vaddr, false)?;
rmp_grant_guest_access(vaddr, false)
rmp_revoke_guest_access(vaddr, PageSize::Regular)?;
rmp_grant_guest_access(vaddr, PageSize::Regular)
}
7 changes: 4 additions & 3 deletions src/sev/vmsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::utils::{rmp_adjust, RMPFlags};
use crate::address::{Address, VirtAddr};
use crate::error::SvsmError;
use crate::mm::alloc::{allocate_pages, free_page};
use crate::types::{PAGE_SIZE, PAGE_SIZE_2M};
use crate::types::{PageSize, PAGE_SIZE, PAGE_SIZE_2M};
use crate::utils::zero_mem_region;

pub const VMPL_MAX: usize = 4;
Expand Down Expand Up @@ -318,14 +318,15 @@ pub fn allocate_new_vmsa(vmpl: RMPFlags) -> Result<VirtAddr, SvsmError> {

zero_mem_region(vmsa_page, vmsa_page + PAGE_SIZE);

if let Err(e) = rmp_adjust(vmsa_page, RMPFlags::VMSA | vmpl, false) {
if let Err(e) = rmp_adjust(vmsa_page, RMPFlags::VMSA | vmpl, PageSize::Regular) {
free_page(vmsa_page);
return Err(e);
}
Ok(vmsa_page)
}

pub fn free_vmsa(vaddr: VirtAddr) {
rmp_adjust(vaddr, RMPFlags::RWX | RMPFlags::VMPL0, false).expect("Failed to free VMSA page");
rmp_adjust(vaddr, RMPFlags::RWX | RMPFlags::VMPL0, PageSize::Regular)
.expect("Failed to free VMSA page");
free_page(vaddr);
}
14 changes: 10 additions & 4 deletions src/stage2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ use svsm::mm::validate::{
use svsm::serial::{SerialPort, SERIAL_PORT};
use svsm::sev::ghcb::PageStateChangeOp;
use svsm::sev::msr_protocol::verify_ghcb_version;
use svsm::sev::{pvalidate_range, sev_status_init, sev_status_verify};
use svsm::sev::{pvalidate_range, sev_status_init, sev_status_verify, PvalidateOp};
use svsm::svsm_console::SVSMIOPort;
use svsm::types::PAGE_SIZE;
use svsm::types::{PageSize, PAGE_SIZE};
use svsm::utils::halt;

extern "C" {
Expand Down Expand Up @@ -119,9 +119,15 @@ fn map_and_validate(vaddr: VirtAddr, paddr: PhysAddr, len: usize) {

this_cpu_mut()
.ghcb()
.page_state_change(paddr, paddr + len, true, PageStateChangeOp::PscPrivate)
.page_state_change(
paddr,
paddr + len,
PageSize::Huge,
PageStateChangeOp::PscPrivate,
)
.expect("GHCB::PAGE_STATE_CHANGE call failed for kernel region");
pvalidate_range(vaddr, vaddr + len, true).expect("PVALIDATE kernel region failed");
pvalidate_range(vaddr, vaddr + len, PvalidateOp::Valid)
.expect("PVALIDATE kernel region failed");
valid_bitmap_set_valid_range(paddr, paddr + len);
}

Expand Down
8 changes: 6 additions & 2 deletions src/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use svsm::sev::sev_status_init;
use svsm::sev::utils::{rmp_adjust, RMPFlags};
use svsm::svsm_console::SVSMIOPort;
use svsm::svsm_paging::{init_page_table, invalidate_stage2};
use svsm::types::{GUEST_VMPL, PAGE_SIZE};
use svsm::types::{PageSize, GUEST_VMPL, PAGE_SIZE};
use svsm::utils::{halt, immut_after_init::ImmutAfterInitCell, zero_mem_region};

use svsm::mm::validate::{init_valid_bitmap_ptr, migrate_valid_bitmap};
Expand Down Expand Up @@ -267,7 +267,11 @@ fn validate_flash() -> Result<(), SvsmError> {
{
let guard = PerCPUPageMappingGuard::create_4k(paddr)?;
let vaddr = guard.virt_addr();
if let Err(e) = rmp_adjust(vaddr, RMPFlags::GUEST_VMPL | RMPFlags::RWX, false) {
if let Err(e) = rmp_adjust(
vaddr,
RMPFlags::GUEST_VMPL | RMPFlags::RWX,
PageSize::Regular,
) {
log::info!("rmpadjust failed for addr {:#018x}", vaddr);
return Err(e);
}
Expand Down
Loading

0 comments on commit 739fa7f

Please sign in to comment.