Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trustlet: Install exception handler #8

Merged
merged 5 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions kernel/src/cpu/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,26 @@ impl GDTEntry {
}

pub const fn code_64_kernel() -> Self {
Self(0x00af9a000000ffffu64)
Self(0x00af9b000000ffffu64)
}

pub const fn data_64_kernel() -> Self {
Self(0x00cf92000000ffffu64)
Self(0x00cf93000000ffffu64)
}

pub const fn code_64_user() -> Self {
Self(0x00affb000000ffffu64)
}

pub const fn data_64_user() -> Self {
Self(0x00cff2000000ffffu64)
Self(0x00cff3000000ffffu64)
}
}

const GDT_SIZE: u16 = 8;

#[derive(Copy, Clone, Debug)]
#[repr(align(4096))]
pub struct GDT {
entries: [GDTEntry; GDT_SIZE as usize],
}
Expand Down Expand Up @@ -115,7 +116,7 @@ impl GDT {
}
}

unsafe fn set_tss_entry(&mut self, desc0: GDTEntry, desc1: GDTEntry) {
pub unsafe fn set_tss_entry(&mut self, desc0: GDTEntry, desc1: GDTEntry) {
let idx = (SVSM_TSS / 8) as usize;

let tss_entries = &self.entries[idx..idx + 1].as_mut_ptr();
Expand Down
1 change: 1 addition & 0 deletions kernel/src/cpu/idt/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ struct IdtDesc {
}

#[derive(Copy, Clone, Debug)]
#[repr(align(4096))]
pub struct IDT {
entries: [IdtEntry; IDT_ENTRIES],
}
Expand Down
6 changes: 5 additions & 1 deletion kernel/src/cpu/tss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub const _IST_INVALID: usize = 0;
pub const IST_DF: usize = 1;

#[derive(Debug, Default, Clone, Copy)]
#[repr(C, packed)]
#[repr(C, packed(4))]
pub struct X86Tss {
reserved1: u32,
pub stacks: [VirtAddr; 3],
Expand Down Expand Up @@ -59,4 +59,8 @@ impl X86Tss {

(GDTEntry::from_raw(desc0), GDTEntry::from_raw(desc1))
}

pub fn base(&self) -> u64 {
self as *const X86Tss as u64
}
}
2 changes: 1 addition & 1 deletion kernel/src/process_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ pub fn monitor_init(){
}
set_ecryption_mask_address_size();
let _ = additional_monitor_memory_init();
PROCESS_STORE.init(10);
PROCESS_STORE.init(128);
let _ = MONITOR_INIT_STATE.reinit(&MONITOR_INIT_STATE_TRUE);
}
249 changes: 247 additions & 2 deletions kernel/src/process_manager/process.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
extern crate alloc;

use core::arch::global_asm;
use core::cell::UnsafeCell;
use alloc::vec::Vec;
use cpuarch::vmsa::VMSASegment;
use igvm_defs::PAGE_SIZE_4K;
use crate::locking::{RWLock, ReadLockGuard, WriteLockGuard};
use crate::address::PhysAddr;
use crate::cpu::percpu::this_cpu_shared;
use crate::cpu::percpu::this_cpu_unsafe;
use crate::cpu::tss::{X86Tss,TSS_LIMIT};
use crate::cpu::gdt::GDT;
use crate::cpu::idt::common::{IdtEntry, DF_VECTOR, IDT, PF_VECTOR, GP_VECTOR};
use crate::cpu::control_regs::read_cr3;
use crate::mm::PAGE_SIZE;
use crate::mm::pagetable::PageTableRef;
use crate::mm::SVSM_PERCPU_VMSA_BASE;
use crate::process_manager::process_memory::allocate_page;
use crate::process_manager::allocation::AllocationRange;
use crate::process_manager::process_paging::ProcessPageTableRef;
use crate::process_manager::process_paging::ProcessPageFlags;
use crate::protocols::errors::SvsmReqError;
use crate::protocols::RequestParams;
use crate::sev::RMPFlags;
Expand All @@ -28,6 +36,7 @@ use crate::vaddr_as_u64_slice;
use cpuarch::vmsa::VMSA;
use core::mem::replace;

use super::process_paging::{TP_STACK_START_VADDR,TP_KERN_STACK_START_VADDR};
use super::memory_channels::MemoryChannel;
use crate::attestation::monitor::{ProcessMeasurements, measure};

Expand Down Expand Up @@ -398,6 +407,104 @@ impl Default for ProcessContext {
}
}

// FIXME: Allocarte the GDT, IDT, and TSS in a dedicated page
/// GDT for Trustlets (shared between all Trustlets)
static GDT_TRUSTLET: RWLock<GDT> = RWLock::new(GDT::new());

fn gdt_trustlet() -> ReadLockGuard<'static, GDT> {
GDT_TRUSTLET.lock_read()
}

fn gdt_trustlet_mut() -> WriteLockGuard<'static, GDT> {
GDT_TRUSTLET.lock_write()
}

/// IDT for Trustlets (shared between all Trustlets)
static IDT_TRUSTLET: RWLock<IDT> = RWLock::new(IDT::new());

fn idt_trustlet() -> ReadLockGuard<'static, IDT> {
IDT_TRUSTLET.lock_read()
}

fn idt_trustlet_mut() -> WriteLockGuard<'static, IDT> {
IDT_TRUSTLET.lock_write()
}

/// TSS for Trustlets
// FIXME: the current implementation use the same TSS for all Trustlets. This works because
// the only one Trustlet runs at a time.
static TSS_TRUSTLET: RWLock<X86Tss> = RWLock::new(X86Tss::new());

fn tss_trustlet() -> ReadLockGuard<'static, X86Tss> {
TSS_TRUSTLET.lock_read()
}

fn tss_trustlet_mut() -> WriteLockGuard<'static, X86Tss> {
TSS_TRUSTLET.lock_write()
}

global_asm!(
r#"
.align 4096
.code64
.section .text
.global asm_entry_trustlet_pf
asm_entry_trustlet_pf:
# #PF pushes the error code on the stack
pushq %rax
pushq %rbx
movq 16(%rsp), %rbx # load error code
movq $0x4EFFFFFF, %rax # monitor call number
cpuid # call the monitor

movq %cr2, %rax # load faulting address
invlpg (%rax) # invalidate the page

popq %rbx
popq %rax
addq $8, %rsp # remove error code

iretq

.global asm_entry_trustlet_df
asm_entry_trustlet_df:
# #DF pushes the error code on the stack
pushq %rax
pushq %rbx
pushq %rcx
movq 24(%rsp), %rbx # load error code
movq $0x4EFFFFFE, %rax # monitor call number
sgdt gdt_desc(%rip) # store current GDT
lea gdt_desc(%rip), %rcx
cpuid
/* no return */

# debug
.section .data
.global gdt_desc
gdt_desc:
.word gdt_entry_end - gdt_entry - 1 # 2 bytes for the GDT limit
.quad gdt_entry # 8 bytes for the GDT base address
.align 256
gdt_entry:
.quad 0x0000000000000000 # null
.quad 0x00af9b000000ffff # code_64_kernel
.quad 0x00cf93000000ffff # data_64_kernel
.quad 0x00affb000000ffff # code_64_user
.quad 0x00cff3000000ffff # data_64_user
.quad 0x0000000000000000 # null
.quad 0x0000000000000000 # tss
.quad 0xAAAAAAAAAAAAAAAA # tss
gdt_entry_end:
"#,
options(att_syntax)
);

extern "C" {
fn asm_entry_trustlet_pf();
fn asm_entry_trustlet_df();
static gdt_desc: u8;
}

impl ProcessContext {

Expand Down Expand Up @@ -437,9 +544,147 @@ impl ProcessContext {
vmsa.efer = vmsa.efer | 1u64 << 12;
vmsa.rip = base.entry_point.into();
vmsa.sev_features = old_vmsa_ptr.sev_features | 4; // 4 is for #VC Reflect
vmsa.rflags &= !(1u64 << 9); // Clear IF;
// New Stack
vmsa.rbp = u64::from(0x8000000000u64)+8*4096-1;
vmsa.rsp = u64::from(0x8000000000u64)+8*4096-1;
vmsa.rbp = u64::from(TP_STACK_START_VADDR)+8*4096-1;
vmsa.rsp = u64::from(TP_STACK_START_VADDR)+8*4096-1;

// ---
// Setup exception handlers

let mut svsm_page_table_ref = ProcessPageTableRef::default();
svsm_page_table_ref.set_external_table(read_cr3().into());

// disable SMAP/SMEP to execute the handler in ring0
// FIXME: propery setup the page flags for the handler
vmsa.cr4 = vmsa.cr4 & !(1u64 << 20 | 1u64 << 21);

log::info!("asm_entry_trustlet_pf: {:x}", asm_entry_trustlet_pf as u64);
log::info!("asm_entry_trustlet_df: {:x}", asm_entry_trustlet_df as u64);
log::info!("gdt_desc: {:x}", unsafe { &gdt_desc as *const u8 as u64 });

// setup IDT
// 1. setup IDT entry for #PF, #DF
idt_trustlet_mut().set_entry(PF_VECTOR, IdtEntry::trap_entry(asm_entry_trustlet_pf));
idt_trustlet_mut().set_entry(DF_VECTOR, IdtEntry::entry(asm_entry_trustlet_df));
//idt_trustlet_mut().set_entry(GP_VECTOR, IdtEntry::trap_entry(asm_entry_trustlet_df));
// 2. rmpadjust for IDT and handlers
let (idt_base, limit) = idt_trustlet().base_limit();
rmp_adjust(idt_base.into(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();
rmp_adjust((asm_entry_trustlet_pf as u64).into(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();
rmp_adjust((unsafe { &gdt_desc as *const u8 as u64 }).into(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();
// 3. map IDT and handlers to trustlet's page table
let idt_phys = svsm_page_table_ref.virt_to_phys(idt_base.into());
let handler_phys = svsm_page_table_ref.virt_to_phys((asm_entry_trustlet_pf as u64).into());
let gdt_desc_phys = svsm_page_table_ref.virt_to_phys((unsafe { &gdt_desc as *const u8 as u64 }).into());
assert!(idt_phys != PhysAddr::null());
assert!(handler_phys != PhysAddr::null());
assert!(gdt_desc_phys != PhysAddr::null());
// FIXME: this assertion is to check the virtual address is available, but this page could
// be also used by GDT/TSS and in that case the assertion will fail. Currently IDT and TSS
// is aligend to 4KB boundary to about this issue. Fix this by properly allocating and
// managing the page for IDT, TSS and GDT.
assert!(page_table_ref.virt_to_phys(idt_base.into()) == PhysAddr::null());
assert!(page_table_ref.virt_to_phys((asm_entry_trustlet_pf as u64).into()) == PhysAddr::null());
assert!(page_table_ref.virt_to_phys((unsafe { &gdt_desc as *const u8 as u64 }).into()) == PhysAddr::null());
page_table_ref.map_4k_page(idt_base.into(), idt_phys, ProcessPageFlags::exec());
page_table_ref.map_4k_page((asm_entry_trustlet_pf as u64).into(), handler_phys, ProcessPageFlags::exec());
page_table_ref.map_4k_page((unsafe { &gdt_desc as *const u8 as u64 }).into(), gdt_desc_phys, ProcessPageFlags::data());
// 4. setup IDT segment in VMSA
let vmsa_idt = VMSASegment {
selector: 0,
flags: 0x0,
base: idt_base,
limit: limit-1,
};
vmsa.idt = vmsa_idt;

// setup TSS
let mut tss = tss_trustlet_mut();
let tss_base = tss.base();
let num_page = 1;
// 1. setup kernel stack address
tss.stacks[0] = (TP_KERN_STACK_START_VADDR + 4096*num_page-16).into();
// 2. map the stack address to trustlet's page table
page_table_ref.add_stack(TP_KERN_STACK_START_VADDR.into(), num_page);
// 3. map the TSS to trustlet's page table
let tss_phys = svsm_page_table_ref.virt_to_phys(tss_base.into());
assert!(tss_phys != PhysAddr::null());
assert!(page_table_ref.virt_to_phys(tss_base.into()) == PhysAddr::null());
page_table_ref.map_4k_page(tss_base.into(), tss_phys, ProcessPageFlags::data());
// 4. rmpadjust for TSS
rmp_adjust(tss_base.into(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();
let vmsa_tss = VMSASegment {
selector: 6*8,
flags: 0x89, // TSS
base: tss_base,
limit: TSS_LIMIT as u32-1,
};
vmsa.tr = vmsa_tss;

// setup GDT
// GDT entreies:
// 0. null
// 1. code_64_kernel
// 2. data_64_kernel
// 3. code_64_user
// 4. data_64_user
// 5. null
// 6-7. TSS
let (desc0, desc1) = tss.to_gdt_entry();
unsafe{
// this sets the entry 6 for TSS
gdt_trustlet_mut().set_tss_entry(desc0, desc1);
}
let (base_gdt, limit) = gdt_trustlet().base_limit();
log::info!("GDT base: {:x}, limit: {:x}", base_gdt, limit);
// 1. rmpadjust for GDT
rmp_adjust(base_gdt.into(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();
// 2. map GDT to trustlet's page table
let gdt_phys = svsm_page_table_ref.virt_to_phys(base_gdt.into());
assert!(gdt_phys != PhysAddr::null());
// FIXME: currently use the same virtual address as the SVSM for the trustlet
assert!(page_table_ref.virt_to_phys(base_gdt.into()) == PhysAddr::null());
page_table_ref.map_4k_page(base_gdt.into(), gdt_phys, ProcessPageFlags::data());
// 3. setup GDT segment in VMSA
let vmsa_gdt = VMSASegment {
selector: 0,
flags: 0,
base: base_gdt,
limit: limit,
};
vmsa.gdt = vmsa_gdt;

let cs = VMSASegment {
selector: 3*8 | 0x3,
flags: 0xAFB, // user, code, 4KB granularity, 64-bit
base: 0,
limit: 0xFFFF_FFFF,
};
let ds = VMSASegment {
selector: 4*8 | 0x3,
flags: 0xCF3, // user, data, 4KB granularity, 64-bit
base: 0,
limit: 0xFFFF_FFFF,
};

vmsa.cs = cs;
vmsa.ds = ds;
vmsa.es = ds;
vmsa.fs = ds;
vmsa.ss = ds;

let efer = vmsa.efer;
let cr4 = vmsa.cr4;
let rflags = vmsa.rflags;
log::info!("vmsa EFER: {:?}", efer);
log::info!("vmsa cr4: {:?}", cr4);
log::info!("vmsa CS: {:?}", vmsa.cs);
log::info!("vmsa SS: {:?}", vmsa.ss);
log::info!("vmsa DS: {:?}", vmsa.ds);
log::info!("vmsa rflags: {:?}", rflags);

// ------ end of exception handlers setup

//Check VMSA
let svme_mask: u64 = 1u64 << 12;
Expand Down
Loading
Loading