Skip to content

Commit

Permalink
Merge branch 'exception' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Sabanic-P committed Dec 19, 2024
2 parents 14389b0 + 7216a81 commit 31ca1b5
Show file tree
Hide file tree
Showing 8 changed files with 569 additions and 39 deletions.
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

0 comments on commit 31ca1b5

Please sign in to comment.