Skip to content

Commit

Permalink
Merge pull request #314 from joergroedel/user-mode
Browse files Browse the repository at this point in the history
Support execution in user-mode
  • Loading branch information
joergroedel authored Apr 25, 2024
2 parents b5f4796 + 5145e2d commit 3e2a303
Show file tree
Hide file tree
Showing 33 changed files with 966 additions and 404 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ members = [
"elf",
# Microsoft TPM library
"libmstpm",
# syscall interface definitions
"syscall",
]


Expand All @@ -23,6 +25,7 @@ test = { path = "test" }
svsm = { path = "kernel" }
elf = { path = "elf" }
libmstpm = { path = "libmstpm" }
syscall = { path = "syscall" }

# crates.io
aes-gcm = { version = "0.10.3", default-features = false }
Expand Down
4 changes: 4 additions & 0 deletions elf/src/load_segments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern crate alloc;
use super::types::*;
use super::Elf64AddrRange;
use super::Elf64File;
use super::Elf64FileRange;
use super::Elf64PhdrFlags;
use super::ElfError;
use alloc::vec::Vec;
Expand Down Expand Up @@ -146,6 +147,8 @@ pub struct Elf64ImageLoadVaddrAllocInfo {
pub struct Elf64ImageLoadSegment<'a> {
/// The virtual address (vaddr) range covering by this segment
pub vaddr_range: Elf64AddrRange,
/// The range in the ELF file covering this segment
pub file_range: Elf64FileRange,
/// The contents of the segment in the ELF file
pub file_contents: &'a [u8],
/// Flags associated with this segment
Expand Down Expand Up @@ -193,6 +196,7 @@ impl<'a> Iterator for Elf64ImageLoadSegmentIterator<'a> {

Some(Elf64ImageLoadSegment {
vaddr_range,
file_range,
file_contents,
flags: phdr.p_flags,
})
Expand Down
1 change: 1 addition & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ doctest = true
bootlib.workspace = true
cpuarch.workspace = true
elf.workspace = true
syscall.workspace = true

aes-gcm = { workspace = true, features = ["aes", "alloc"] }
bitflags.workspace = true
Expand Down
25 changes: 25 additions & 0 deletions kernel/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::types::{PAGE_SHIFT, PAGE_SIZE};
use core::fmt;
use core::ops;

use core::slice;

// The backing type to represent an address;
type InnerAddr = usize;

Expand Down Expand Up @@ -210,6 +212,11 @@ impl VirtAddr {
Self(sign_extend(addr))
}

/// Returns the index into page-table pages of given levels.
pub const fn to_pgtbl_idx<const L: usize>(&self) -> usize {
(self.0 >> (12 + L * 9)) & 0x1ffusize
}

#[inline]
pub fn as_ptr<T>(&self) -> *const T {
self.0 as *const T
Expand Down Expand Up @@ -251,6 +258,24 @@ impl VirtAddr {
pub const fn const_add(&self, offset: usize) -> Self {
VirtAddr::new(self.0 + offset)
}

/// Converts the `VirtAddr` to a slice of a given type
///
/// # Arguments:
///
/// * `len` - Number of elements of type `T` in the slice
///
/// # Returns
///
/// Slice with `len` elements of type `T`
///
/// # Safety
///
/// All Safety requirements from [`core::slice::from_raw_parts`] for the
/// data pointed to by the `VirtAddr` apply here as well.
pub unsafe fn to_slice<T>(&self, len: usize) -> &[T] {
slice::from_raw_parts::<T>(self.as_ptr::<T>(), len)
}
}

impl fmt::Display for VirtAddr {
Expand Down
12 changes: 10 additions & 2 deletions kernel/src/cpu/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ impl GDTEntry {
pub const fn data_64_kernel() -> Self {
Self(0x00cf92000000ffffu64)
}

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

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

const GDT_SIZE: u16 = 8;
Expand All @@ -57,8 +65,8 @@ impl GDT {
GDTEntry::null(),
GDTEntry::code_64_kernel(),
GDTEntry::data_64_kernel(),
GDTEntry::null(),
GDTEntry::null(),
GDTEntry::code_64_user(),
GDTEntry::data_64_user(),
GDTEntry::null(),
GDTEntry::null(),
GDTEntry::null(),
Expand Down
48 changes: 42 additions & 6 deletions kernel/src/cpu/idt/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ pub struct X86ExceptionContext {
pub frame: X86InterruptFrame,
}

pub fn user_mode(ctxt: &X86ExceptionContext) -> bool {
(ctxt.frame.cs & 3) == 3
}

#[derive(Copy, Clone, Default, Debug)]
#[repr(C, packed)]
pub struct IdtEntry {
Expand All @@ -62,22 +66,39 @@ const IDT_TARGET_MASK_1_SHIFT: u64 = 0;
const IDT_TARGET_MASK_2_SHIFT: u64 = 48 - 16;
const IDT_TARGET_MASK_3_SHIFT: u64 = 32;

const IDT_TYPE_MASK: u64 = 0xeu64 << 40; // Only interrupt gates for now
const IDT_TYPE_MASK: u8 = 0x0f;
const IDT_TYPE_SHIFT: u64 = 40;
const IDT_TYPE_CALL: u8 = 0x0c;
const IDT_TYPE_INT: u8 = 0x0e;
const IDT_TYPE_TRAP: u8 = 0x0f;

fn idt_type_mask(t: u8) -> u64 {
((t & IDT_TYPE_MASK) as u64) << IDT_TYPE_SHIFT
}

const IDT_DPL_MASK: u8 = 0x03;
const IDT_DPL_SHIFT: u64 = 45;

fn idt_dpl_mask(dpl: u8) -> u64 {
((dpl & IDT_DPL_MASK) as u64) << IDT_DPL_SHIFT
}

const IDT_PRESENT_MASK: u64 = 0x1u64 << 47;
const IDT_CS_SHIFT: u64 = 16;

const IDT_IST_MASK: u64 = 0x7;
const IDT_IST_SHIFT: u64 = 32;

impl IdtEntry {
fn create(target: VirtAddr, cs: u16, ist: u8) -> Self {
fn create(target: VirtAddr, cs: u16, desc_type: u8, dpl: u8, ist: u8) -> Self {
let vaddr = target.bits() as u64;
let cs_mask = (cs as u64) << IDT_CS_SHIFT;
let ist_mask = ((ist as u64) & IDT_IST_MASK) << IDT_IST_SHIFT;
let low = (vaddr & IDT_TARGET_MASK_1) << IDT_TARGET_MASK_1_SHIFT
| (vaddr & IDT_TARGET_MASK_2) << IDT_TARGET_MASK_2_SHIFT
| IDT_TYPE_MASK
| idt_type_mask(desc_type)
| IDT_PRESENT_MASK
| idt_dpl_mask(dpl)
| cs_mask
| ist_mask;
let high = (vaddr & IDT_TARGET_MASK_3) >> IDT_TARGET_MASK_3_SHIFT;
Expand All @@ -86,17 +107,32 @@ impl IdtEntry {
}

pub fn raw_entry(target: VirtAddr) -> Self {
IdtEntry::create(target, SVSM_CS, 0)
IdtEntry::create(target, SVSM_CS, IDT_TYPE_INT, 0, 0)
}

pub fn entry(handler: unsafe extern "C" fn()) -> Self {
let target = VirtAddr::from(handler as *const ());
IdtEntry::create(target, SVSM_CS, 0)
IdtEntry::create(target, SVSM_CS, IDT_TYPE_INT, 0, 0)
}

pub fn user_entry(handler: unsafe extern "C" fn()) -> Self {
let target = VirtAddr::from(handler as *const ());
IdtEntry::create(target, SVSM_CS, IDT_TYPE_INT, 3, 0)
}

pub fn ist_entry(handler: unsafe extern "C" fn(), ist: u8) -> Self {
let target = VirtAddr::from(handler as *const ());
IdtEntry::create(target, SVSM_CS, ist)
IdtEntry::create(target, SVSM_CS, IDT_TYPE_INT, 0, ist)
}

pub fn trap_entry(handler: unsafe extern "C" fn()) -> Self {
let target = VirtAddr::from(handler as *const ());
IdtEntry::create(target, SVSM_CS, IDT_TYPE_TRAP, 0, 0)
}

pub fn call_entry(handler: unsafe extern "C" fn()) -> Self {
let target = VirtAddr::from(handler as *const ());
IdtEntry::create(target, SVSM_CS, IDT_TYPE_CALL, 3, 0)
}

pub const fn no_handler() -> Self {
Expand Down
12 changes: 12 additions & 0 deletions kernel/src/cpu/idt/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,19 @@ asm_entry_\name:
jmp default_return
.endm

.globl default_return
default_return:
testb $3, 18*8(%rsp) // Check CS in exception frame
jnz return_user
pop_regs
default_iret:
iretq

return_user:
// Put user-mode specific return code here
pop_regs
jmp default_iret

// #DE Divide-by-Zero-Error Exception (Vector 0)
default_entry_no_ist name=de handler=panic error_code=0 vector=0

Expand Down Expand Up @@ -136,3 +145,6 @@ default_entry_no_ist name=vc handler=vmm_communication error_code=1 vector=29

// #SX Security Exception (Vector 30)
default_entry_no_ist name=sx handler=panic error_code=1 vector=30

// INT 0x80 system call handler
default_entry_no_ist name=int80 handler=system_call error_code=0 vector=0x80
Loading

0 comments on commit 3e2a303

Please sign in to comment.