Skip to content

Commit

Permalink
Start creating a trait for the HAL
Browse files Browse the repository at this point in the history
The main difficulty stemmed from the fact that we don't want the
platform to leak into the `PortFactory` or `Port` types, as we need to
have the serial port as a static and at that point we don't know what
the right `Platform` is yet. The answer is... function pointers, as they
have a known size and signature.

This just covers CPUID + MMIO + ports, but this should suffice as an
example. I will move the rest of the operations (MSRs, attestation etc)
to the trait in a separate CR as those changes are more
straightforward.

Bug: 350496083
Change-Id: Ide1e100147a9463c44b664edc9856d4e78151e00
  • Loading branch information
andrisaar committed Aug 10, 2024
1 parent 4242902 commit b038659
Show file tree
Hide file tree
Showing 20 changed files with 394 additions and 323 deletions.
54 changes: 25 additions & 29 deletions stage0/src/apic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@
// limitations under the License.
//

use x86_64::PhysAddr;

use crate::{
hal,
msr::{
ApicBase, ApicBaseFlags, ApicErrorFlags, DestinationMode, DestinationShorthand, Level,
MessageType, SpuriousInterruptFlags, TriggerMode, X2ApicErrorStatusRegister,
X2ApicIdRegister, X2ApicInterruptCommandRegister, X2ApicSpuriousInterruptRegister,
X2ApicVersionRegister,
},
use x86_64::{structures::paging::Size4KiB, PhysAddr};

use crate::msr::{
ApicBase, ApicBaseFlags, ApicErrorFlags, DestinationMode, DestinationShorthand, Level,
MessageType, SpuriousInterruptFlags, TriggerMode, X2ApicErrorStatusRegister, X2ApicIdRegister,
X2ApicInterruptCommandRegister, X2ApicSpuriousInterruptRegister, X2ApicVersionRegister,
};

/// Interrupt Command.
Expand Down Expand Up @@ -83,7 +79,7 @@ trait SpuriousInterrupts {
}

mod xapic {
use x86_64::PhysAddr;
use x86_64::{structures::paging::Size4KiB, PhysAddr};

use super::{ApicErrorFlags, SpuriousInterruptFlags};
use crate::hal;
Expand All @@ -96,11 +92,11 @@ mod xapic {
const INTERRUPT_COMMAND_REGISTER_LOW_OFFSET: usize = 0x300 / core::mem::size_of::<u32>();
const INTERRUPT_COMMAND_REGISTER_HIGH_OFFSET: usize = 0x310 / core::mem::size_of::<u32>();

pub(crate) struct Xapic {
mmio: crate::hal::Mmio,
pub(crate) struct Xapic<M: hal::Mmio<Size4KiB>> {
mmio: M,
}

impl Xapic {
impl<M: hal::Mmio<Size4KiB>> Xapic<M> {
fn read(&self, register: usize) -> u32 {
self.mmio.read_u32(register)
}
Expand All @@ -109,7 +105,7 @@ mod xapic {
}
}

impl super::ApicId for Xapic {
impl<M: hal::Mmio<Size4KiB>> super::ApicId for Xapic<M> {
/// Read the Local APIC ID register.
///
/// See Section 16.3.3 in the AMD64 Architecture Programmer's Manual,
Expand All @@ -119,7 +115,7 @@ mod xapic {
}
}

impl super::InterprocessorInterrupt for Xapic {
impl<M: hal::Mmio<Size4KiB>> super::InterprocessorInterrupt for Xapic<M> {
fn send(
&mut self,
destination: u32,
Expand Down Expand Up @@ -149,7 +145,7 @@ mod xapic {
}
}

impl super::ErrorStatus for Xapic {
impl<M: hal::Mmio<Size4KiB>> super::ErrorStatus for Xapic<M> {
fn read(&self) -> ApicErrorFlags {
ApicErrorFlags::from_bits_truncate(self.read(ERROR_STATUS_REGISTER_OFFSET))
}
Expand All @@ -160,7 +156,7 @@ mod xapic {
}
}

impl super::ApicVersion for Xapic {
impl<M: hal::Mmio<Size4KiB>> super::ApicVersion for Xapic<M> {
fn read(&self) -> (bool, u8, u8) {
let val = self.read(APIC_VERSION_REGISTER_OFFSET);

Expand All @@ -172,7 +168,7 @@ mod xapic {
}
}

impl super::SpuriousInterrupts for Xapic {
impl<M: hal::Mmio<Size4KiB>> super::SpuriousInterrupts for Xapic<M> {
fn read(&self) -> (SpuriousInterruptFlags, u8) {
let val = self.read(SPURIOUS_INTERRUPT_REGISTER_OFFSET);

Expand All @@ -187,8 +183,8 @@ mod xapic {

/// Safety: caller needs to guarantee that `apic_base` points to the APIC
/// MMIO memory.
pub(crate) unsafe fn init(apic_base: PhysAddr) -> Xapic {
Xapic { mmio: hal::Mmio::new(apic_base) }
pub(crate) unsafe fn init<P: crate::Platform>(apic_base: PhysAddr) -> Xapic<P::Mmio<Size4KiB>> {
Xapic { mmio: P::mmio::<Size4KiB>(apic_base) }
}
}

Expand Down Expand Up @@ -264,8 +260,8 @@ mod x2apic {
}
}

enum Apic {
Xapic(xapic::Xapic),
enum Apic<M: crate::hal::Mmio<Size4KiB>> {
Xapic(xapic::Xapic<M>),
X2apic(
X2ApicInterruptCommandRegister,
X2ApicErrorStatusRegister,
Expand All @@ -277,14 +273,14 @@ enum Apic {
/// Wrapper for the local APIC.
///
/// Currenty only supports x2APIC mode.
pub struct Lapic {
pub struct Lapic<M: crate::hal::Mmio<Size4KiB>> {
apic_id: u32,
interface: Apic,
interface: Apic<M>,
}

impl Lapic {
pub fn enable() -> Result<Self, &'static str> {
let x2apic = hal::cpuid(0x0000_0001).ecx & (1 << 21) > 0;
impl<M: crate::hal::Mmio<Size4KiB>> Lapic<M> {
pub fn enable<P: crate::Platform<Mmio<Size4KiB> = M>>() -> Result<Self, &'static str> {
let x2apic = P::cpuid(0x0000_0001).ecx & (1 << 21) > 0;
// See Section 16.9 in the AMD64 Architecture Programmer's Manual, Volume 2 for
// explanation of the initialization procedure.
let (aba, mut flags) = ApicBase::read();
Expand Down Expand Up @@ -312,7 +308,7 @@ impl Lapic {
} else {
log::info!("Using xAPIC for AP initialization.");
// Safety: we trust the address provided by the ApicBase MSR.
let apic = unsafe { xapic::init(aba) };
let apic = unsafe { xapic::init::<P>(aba) };
Lapic { apic_id: apic.apic_id(), interface: Apic::Xapic(apic) }
};
// Version should be between [0x10...0x20).
Expand Down
12 changes: 8 additions & 4 deletions stage0/src/cmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
// limitations under the License.
//

use oak_sev_guest::io::{PortReader, PortWriter};
use oak_sev_guest::io::{IoPortFactory, PortReader, PortWriter};

use crate::hal::Port;
use crate::hal::{Port, PortFactory};
const CMOS_INDEX_PORT: u16 = 0x0070;
const CMOS_DATA_PORT: u16 = 0x0071;

Expand Down Expand Up @@ -45,8 +45,12 @@ impl Cmos {
/// readers/writers to the CMOS ports (0x70, 0x71) and that CMOS is
/// actually available on those ports, otherwise the behaviour is
/// undefined.
pub unsafe fn new() -> Self {
Self { index_port: Port::new(CMOS_INDEX_PORT), data_port: Port::new(CMOS_DATA_PORT) }
pub unsafe fn new<P: crate::Platform>() -> Self {
let factory = PortFactory::new::<P>();
Self {
index_port: factory.new_reader(CMOS_INDEX_PORT),
data_port: factory.new_reader(CMOS_DATA_PORT),
}
}

/// Returns the low RAM size (memory under the 4 GiB mark)
Expand Down
20 changes: 13 additions & 7 deletions stage0/src/fw_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ use core::{cmp::min, ffi::CStr};

use bitflags::bitflags;
use oak_linux_boot_params::{BootE820Entry, E820EntryType};
use oak_sev_guest::io::{PortReader, PortWriter};
use oak_sev_guest::io::{IoPortFactory, PortReader, PortWriter};
use x86_64::{
structures::paging::{PageSize, Size2MiB, Size4KiB},
PhysAddr, VirtAddr,
};
use zerocopy::{AsBytes, FromBytes, FromZeroes};

use crate::{allocator::Shared, hal::Port, BootAllocator};
use crate::{
allocator::Shared,
hal::{Port, PortFactory},
BootAllocator,
};

// See https://www.qemu.org/docs/master/specs/fw_cfg.html for documentation about the various data structures and constants.
const FWCFG_PORT_SELECTOR: u16 = 0x510;
Expand Down Expand Up @@ -160,14 +164,16 @@ impl FwCfg {
///
/// The caller has to guarantee that at least doing the probe will not cause
/// any adverse effects.
pub unsafe fn new(alloc: &'static BootAllocator) -> Result<Self, &'static str> {
pub unsafe fn new<P: crate::Platform>(
alloc: &'static BootAllocator,
) -> Result<Self, &'static str> {
let mut fwcfg = Self {
selector: Port::new(FWCFG_PORT_SELECTOR),
data: Port::new(FWCFG_PORT_DATA),
dma_high: Port::new(FWCFG_PORT_DMA),
selector: PortFactory::new::<P>().new_writer(FWCFG_PORT_SELECTOR),
data: PortFactory::new::<P>().new_reader(FWCFG_PORT_DATA),
dma_high: PortFactory::new::<P>().new_writer(FWCFG_PORT_DMA),
// The DMA address must be big-endian encoded, so the low address is 4 bytes further
// than the high address.
dma_low: Port::new(FWCFG_PORT_DMA + 4),
dma_low: PortFactory::new::<P>().new_writer(FWCFG_PORT_DMA + 4),
dma_buf: Shared::new_in(DmaBuffer::default(), alloc),
dma_access: Shared::new_in(FwCfgDmaAccess::default(), alloc),
dma_enabled: false,
Expand Down
22 changes: 0 additions & 22 deletions stage0/src/hal/base/cpuid.rs

This file was deleted.

16 changes: 13 additions & 3 deletions stage0/src/hal/base/mmio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//

use alloc::alloc::{alloc, dealloc};
use core::alloc::Layout;
use core::{alloc::Layout, mem::size_of};

use x86_64::{
instructions::tlb::flush_all,
Expand Down Expand Up @@ -58,16 +58,26 @@ impl<S: PageSize> Mmio<S> {

Mmio { base_address, layout, mmio_memory, old_pte, phantom: core::marker::PhantomData }
}
}

pub fn read_u32(&self, offset: usize) -> u32 {
impl<S: PageSize> crate::hal::Mmio<S> for Mmio<S> {
fn read_u32(&self, offset: usize) -> u32 {
let offset = offset * size_of::<u32>();
if offset >= S::SIZE as usize {
panic!("invalid MMIO access for read: offset would read beyond memory boundary");
}
// Safety:
// - offset is aligned to u32
// - we've checked it's within the page size
// - when calling new() we were promised the memory is valid
unsafe { self.mmio_memory.as_ptr::<u32>().add(offset).read_volatile() }
}

pub unsafe fn write_u32(&mut self, offset: usize, value: u32) {
unsafe fn write_u32(&mut self, offset: usize, value: u32) {
let offset = offset * size_of::<u32>();
if offset >= S::SIZE as usize {
panic!("invalid MMIO access for write: offset would write beyond memory boundary");
}
// Safety:
// - offset is aligned to u32
// - we've checked it's within the page size
Expand Down
50 changes: 48 additions & 2 deletions stage0/src/hal/base/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,58 @@
// limitations under the License.
//

mod cpuid;
mod mmio;

pub use cpuid::*;
use core::arch::x86_64::{CpuidResult, __cpuid};

pub use mmio::*;
pub use oak_stage0_dice::{
mock_attestation_report as get_attestation, mock_derived_key as get_derived_key,
};
pub use x86_64::registers::model_specific::Msr;
use x86_64::structures::{
paging::PageSize,
port::{PortRead, PortWrite},
};

pub struct Base {}

impl crate::Platform for Base {
type Mmio<S: PageSize> = mmio::Mmio<S>;

fn cpuid(leaf: u32) -> CpuidResult {
// Safety: all CPUs we care about are modern enough to support CPUID.
unsafe { __cpuid(leaf) }
}

unsafe fn mmio<S: PageSize>(base_address: x86_64::PhysAddr) -> Self::Mmio<S> {
mmio::Mmio::new(base_address)
}

unsafe fn read_u8_from_port(port: u16) -> Result<u8, &'static str> {
Ok(u8::read_from_port(port))
}

unsafe fn write_u8_to_port(port: u16, value: u8) -> Result<(), &'static str> {
u8::write_to_port(port, value);
Ok(())
}

unsafe fn read_u16_from_port(port: u16) -> Result<u16, &'static str> {
Ok(u16::read_from_port(port))
}

unsafe fn write_u16_to_port(port: u16, value: u16) -> Result<(), &'static str> {
u16::write_to_port(port, value);
Ok(())
}

unsafe fn read_u32_from_port(port: u16) -> Result<u32, &'static str> {
Ok(u32::read_from_port(port))
}

unsafe fn write_u32_to_port(port: u16, value: u32) -> Result<(), &'static str> {
u32::write_to_port(port, value);
Ok(())
}
}
Loading

0 comments on commit b038659

Please sign in to comment.