diff --git a/internal/shim-sev/src/attestation.rs b/internal/shim-sev/src/attestation.rs new file mode 100644 index 00000000..46157227 --- /dev/null +++ b/internal/shim-sev/src/attestation.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! SEV attestation handling + +use crate::hostlib::{BootInfo, SevSecret, SEV_SECRET_MAX_SIZE}; +use crate::C_BIT_MASK; +use core::hint::unreachable_unchecked; +use core::sync::atomic::Ordering; +use spinning::RwLock; + +/// A copy of the injected SEV secret. +#[derive(Copy, Clone, Debug)] +pub struct SevSecretCopy { + /// the secret byte blob + data: [u8; SEV_SECRET_MAX_SIZE], +} + +/// The secret injected by the Hypervisor +pub static SEV_SECRET: RwLock = RwLock::::const_new( + spinning::RawRwLock::const_new(), + SevSecretCopy { + data: [0u8; SEV_SECRET_MAX_SIZE], + }, +); + +impl SevSecretCopy { + #[allow(clippy::integer_arithmetic)] + unsafe fn cbor_len(data: *const u8) -> Option { + let prefix = data.read(); + + // only accept CBOR BYTES type + if (prefix >> 5) != 2 { + return None; + } + + // mask the minor + match prefix & 0b00011111 { + x @ 0..=23 => Some(1 + x as usize), + 24 => Some(1 + 1 + data.add(1).read() as usize), + 25 => { + let data = data.add(1) as *const [u8; 2]; + Some(1 + 2 + u16::from_be_bytes(data.read()) as usize) + } + 26 => { + let data = data.add(1) as *const [u8; 4]; + Some(1 + 4 + u32::from_be_bytes(data.read()) as usize) + } + 27 => { + let data = data.add(1) as *const [u8; 8]; + Some(1 + 8 + u64::from_be_bytes(data.read()) as usize) + } + 28 => None, + 29 => None, + 30 => None, + 31 => None, + 32..=255 => unreachable_unchecked(), + } + } + + /// get the length of the secret + pub fn try_len(&self) -> Option { + unsafe { SevSecretCopy::cbor_len(self.data.as_ptr()) } + } + + /// Backup the secret injected by the Hypervisor + /// + /// # Safety + /// The caller has to ensure `boot_info` is pointing + /// to the initial BootInfo passed by the Hypervisor. + pub unsafe fn copy_from_bootinfo(&mut self, boot_info: *const BootInfo) { + if C_BIT_MASK.load(Ordering::Relaxed) == 0 { + return; + } + + let secret_ptr = SevSecret::get_secret_ptr(boot_info); + + let secret_len = match SevSecretCopy::cbor_len(secret_ptr as *const u8) { + None => return, + Some(len) => len, + }; + + if secret_len > SEV_SECRET_MAX_SIZE { + return; + } + + core::ptr::copy_nonoverlapping::( + (*secret_ptr).data.as_ptr() as _, + self.data.as_mut_ptr(), + secret_len, + ); + } + + /// Get a slice of the secret + pub fn try_as_slice(&self) -> Option<&[u8]> { + self.try_len().map(|len| &self.data[..len]) + } +} diff --git a/internal/shim-sev/src/hostlib.rs b/internal/shim-sev/src/hostlib.rs index bd90d26e..08fbce09 100644 --- a/internal/shim-sev/src/hostlib.rs +++ b/internal/shim-sev/src/hostlib.rs @@ -35,6 +35,7 @@ /// FIXME: might change to another mechanism in the future pub const SYSCALL_TRIGGER_PORT: u16 = 0xFF; +use core::mem::{align_of, size_of, MaybeUninit}; use lset::{Line, Span}; use nbytes::bytes; use primordial::Page; @@ -65,6 +66,29 @@ fn above(rel: impl Into>, size: usize, align: usize) -> Option, +} + +impl SevSecret { + /// Get the pointer to the SEV secret relative to the BootInfo pointer + #[allow(dead_code)] + pub fn get_secret_ptr(boot_info: *const BootInfo) -> *const SevSecret { + unsafe { + let secret_ptr = (boot_info as *const u8).add(size_of::()); + secret_ptr.add(secret_ptr.align_offset(align_of::())) as *const SevSecret + } + } +} + /// Basic information for the shim and the loader #[repr(C)] #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] diff --git a/internal/shim-sev/src/main.rs b/internal/shim-sev/src/main.rs index 7e59f4ce..55d0441d 100644 --- a/internal/shim-sev/src/main.rs +++ b/internal/shim-sev/src/main.rs @@ -15,33 +15,36 @@ extern crate compiler_builtins; extern crate rcrt1; +#[macro_use] +pub mod print; + pub mod addr; pub mod asm; +pub mod attestation; pub mod frame_allocator; pub mod gdt; pub mod hostcall; /// Shared components for the shim and the loader pub mod hostlib; pub mod no_std; +pub mod pagetables; pub mod paging; pub mod payload; -pub mod shim_stack; -#[macro_use] -pub mod print; -pub mod pagetables; pub mod random; +pub mod shim_stack; pub mod syscall; pub mod usermode; use crate::addr::{ShimVirtAddr, SHIM_VIRT_OFFSET}; +use crate::attestation::SEV_SECRET; use crate::hostcall::HOST_CALL; +use crate::hostlib::BootInfo; use crate::pagetables::switch_sallyport_to_unencrypted; use crate::paging::SHIM_PAGETABLE; use crate::payload::PAYLOAD_VIRT_ADDR; use core::convert::TryFrom; use core::mem::size_of; use core::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -pub use hostlib::BootInfo; use primordial::Address; use sallyport::Block; use spinning::RwLock; @@ -113,7 +116,9 @@ macro_rules! entry_point { ); // make a local copy of boot_info, before the shared page gets overwritten - BOOT_INFO.write().replace(boot_info.read_volatile()); + BOOT_INFO.write().replace(boot_info.read()); + + SEV_SECRET.write().copy_from_bootinfo(boot_info); switch_sallyport_to_unencrypted(c_bit_mask); diff --git a/internal/shim-sev/src/syscall.rs b/internal/shim-sev/src/syscall.rs index 0a61d28e..736c68f9 100644 --- a/internal/shim-sev/src/syscall.rs +++ b/internal/shim-sev/src/syscall.rs @@ -4,6 +4,7 @@ use crate::addr::{HostVirtAddr, ShimPhysUnencryptedAddr}; use crate::asm::_enarx_asm_triple_fault; +use crate::attestation::SEV_SECRET; use crate::eprintln; use crate::frame_allocator::FRAME_ALLOCATOR; use crate::hostcall::{self, HostCall, HOST_CALL}; @@ -16,7 +17,7 @@ use primordial::{Address, Register}; use sallyport::{Cursor, Request}; use spinning::MutexGuard; use syscall::{SyscallHandler, ARCH_GET_FS, ARCH_GET_GS, ARCH_SET_FS, ARCH_SET_GS, SEV_TECH}; -use untrusted::{AddressValidator, UntrustedRef, UntrustedRefMut, Validate}; +use untrusted::{AddressValidator, UntrustedRef, UntrustedRefMut, Validate, ValidateSlice}; use x86_64::registers::{rdfsbase, rdgsbase, wrfsbase, wrgsbase}; use x86_64::structures::paging::{Page, PageTableFlags, Size4KiB}; use x86_64::{align_up, VirtAddr}; @@ -205,11 +206,28 @@ impl SyscallHandler for Handler { &mut self, _nonce: UntrustedRef, _nonce_len: libc::size_t, - _buf: UntrustedRefMut, - _buf_len: libc::size_t, + buf: UntrustedRefMut, + buf_len: libc::size_t, ) -> sallyport::Result { - self.trace("get_att", 0); - Ok([0.into(), SEV_TECH.into()]) + self.trace("get_attestation", 4); + + let secret = SEV_SECRET.read(); + + match secret.try_len() { + Some(mut result_len) => { + if buf_len != 0 { + result_len = result_len.min(buf_len); + let buf = buf.validate_slice(buf_len, self).ok_or(libc::EFAULT)?; + + buf[..result_len].copy_from_slice( + &(SEV_SECRET.read()).try_as_slice().unwrap()[..result_len], + ); + } + + Ok([result_len.into(), SEV_TECH.into()]) + } + None => Err(libc::ENOSYS), + } } fn exit(&mut self, status: i32) -> ! { diff --git a/src/backend/kvm/mod.rs b/src/backend/kvm/mod.rs index 61088601..3b41db8c 100644 --- a/src/backend/kvm/mod.rs +++ b/src/backend/kvm/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 mod builder; -mod shim; +pub mod shim; mod vm; pub const SHIM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bin/shim-sev")); diff --git a/src/backend/kvm/vm/builder.rs b/src/backend/kvm/vm/builder.rs index 88d723f4..3f01b04f 100644 --- a/src/backend/kvm/vm/builder.rs +++ b/src/backend/kvm/vm/builder.rs @@ -53,7 +53,12 @@ pub trait Hook { Ok(()) } - fn code_loaded(&mut self, _vm: &mut VmFd, _addr_space: &[u8]) -> Result<()> { + fn code_loaded( + &mut self, + _vm: &mut VmFd, + _saddr_space: &[u8], + _syscall_blocks: VirtAddr, + ) -> Result<()> { Ok(()) } @@ -101,7 +106,7 @@ impl Builder { let code_offset = boot_info.code.start; Self::load_component(addr, &mut self.code, code_offset); - self.hook.code_loaded(&mut fd, &map)?; + self.hook.code_loaded(&mut fd, &map, arch.syscall_blocks)?; let vm = Vm { kvm, diff --git a/src/backend/sev/builder.rs b/src/backend/sev/builder.rs index cb6dd2c8..844efab8 100644 --- a/src/backend/sev/builder.rs +++ b/src/backend/sev/builder.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::backend::kvm; +use crate::backend::kvm::shim::{BootInfo, SevSecret}; use sev::firmware::Firmware; use sev::launch::{Launcher, Secret}; @@ -25,7 +26,12 @@ impl Sev { } impl kvm::Hook for Sev { - fn code_loaded(&mut self, vm: &mut VmFd, addr_space: &[u8]) -> Result<()> { + fn code_loaded( + &mut self, + vm: &mut VmFd, + addr_space: &[u8], + syscall_blocks: VirtAddr, + ) -> Result<()> { let mut sev = Firmware::open()?; let build = sev.platform_status().unwrap().build; @@ -69,7 +75,7 @@ impl kvm::Hook for Sev { session: serde_flavor::from_reader(start_packet.session.as_slice())?, }; - let (launcher, measurement) = { + let (mut launcher, measurement) = { let launcher = Launcher::new(vm, &mut sev)?; let mut launcher = launcher.start(start)?; launcher.update_data(addr_space)?; @@ -93,9 +99,11 @@ impl kvm::Hook for Sev { }; if !secret.is_empty() { - let _secret: Secret = serde_flavor::from_reader(secret.as_slice())?; - // TODO: https://github.com/enarx/enarx-keepldr/issues/159 - // Inject the secret! + let secret: Secret = serde_flavor::from_reader(secret.as_slice())?; + + let secret_ptr = SevSecret::get_secret_ptr(syscall_blocks.as_ptr::()); + + launcher.inject(secret, secret_ptr as _)?; } let finish_packet = Message::Finish(Finish);