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

Implement mprotect and page table copy #4

Merged
merged 4 commits into from
Dec 10, 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
19 changes: 16 additions & 3 deletions kernel/src/process_manager/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::address::PhysAddr;
use crate::cpu::percpu::this_cpu_shared;
use crate::cpu::percpu::this_cpu_unsafe;
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;
Expand Down Expand Up @@ -193,7 +194,7 @@ impl TrustedProcess {
if data != 0 {
let (function_code, function_code_range) = ProcessPageTableRef::copy_data_from_guest(data, size, pgt);
let size = (4096 - (size & 0xFFF)) + size;
trustlet.base.page_table_ref.add_function(function_code, size);
trustlet.context.page_table_ref.add_function(function_code, size);
function_code_range.delete();
}
trustlet
Expand Down Expand Up @@ -298,7 +299,6 @@ pub fn attest_trusted_process(_params: &mut RequestParams) -> Result<(), SvsmReq
}

pub fn check_page_table(pgd_addr: u64, test_location: u64) {

log::info!("Using Address: {:#x}", pgd_addr);
let mut page_table_ref = ProcessPageTableRef::default();
page_table_ref.set_external_table(pgd_addr);
Expand Down Expand Up @@ -373,6 +373,7 @@ pub struct ProcessContext {
pub channel: MemoryChannel,
pub sev_features: u64,
pub measurements: ProcessMeasurements,
pub page_table_ref: ProcessPageTableRef,
}

impl Default for ProcessContext {
Expand All @@ -383,15 +384,26 @@ impl Default for ProcessContext {
channel: MemoryChannel::default(),
sev_features: 0,
measurements: ProcessMeasurements::default(),
page_table_ref: ProcessPageTableRef::default(),
}
}
}


impl ProcessContext {

/// This function is called to create a Trustlet from a Zygote
pub fn init(&mut self, base: ProcessBaseContext, measurements: ProcessMeasurements) {

// Setup a new page table for the Process
// FIXME: this performs full deep copy of memory and page table from the base
// TODO: implement proper CoW
let mut new_page_table_ref = ProcessPageTableRef::default();
new_page_table_ref.init_vmpl1();
new_page_table_ref.copy_from(&base.page_table_ref);
let page_table_ref = new_page_table_ref;
//let page_table_ref = base.page_table_ref;

//Creating new VMSA for the Process
let new_vmsa_page = allocate_page();
let new_vmsa_mapping = PerCPUPageMappingGuard::create_4k(new_vmsa_page).unwrap();
Expand All @@ -412,7 +424,7 @@ impl ProcessContext {
//New VMSA Setup
vmsa.vmpl = 1; // Trustlets always run in VMPL1
vmsa.cpl = 3; // Ring 3
vmsa.cr3 = u64::from(base.page_table_ref.process_page_table);
vmsa.cr3 = u64::from(page_table_ref.process_page_table);
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
Expand Down Expand Up @@ -443,6 +455,7 @@ impl ProcessContext {
self.sev_features = vmsa.sev_features;
self.base = base;
self.measurements = measurements;
self.page_table_ref = page_table_ref;
}

pub fn add_function(&mut self, function: VirtAddr, size: u64) {
Expand Down
148 changes: 141 additions & 7 deletions kernel/src/process_manager/process_paging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,22 @@ use core::ffi::CStr;
use super::memory_helper::{ZERO_PAGE};

// TP: Trusted Process
const TP_STACK_START_VADDR: u64 = 0x80_0000_0000;
const TP_MANIFEST_START_VADDR: u64 = 0x100_0000_0000;
const TP_LIBOS_START_VADDR: u64 = 0x180_0000_0000;
pub const TP_STACK_START_VADDR: u64 = 0x80_0000_0000;
pub const TP_MANIFEST_START_VADDR: u64 = 0x100_0000_0000;
pub const TP_LIBOS_START_VADDR: u64 = 0x180_0000_0000;

// Gramine PAL protection flags (pal_prot_flags_t)
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GraminePalProtFlags: u64 {
const READ = 0x1;
const WRITE = 0x2;
const EXEC = 0x4;
const WRITECOPY = 0x8;
const MASK = 0xF;
}
}

// Flags for the Page Table
// In general all Trusted Processes need to
Expand All @@ -37,8 +50,7 @@ bitflags! {
const HUGE_PAGE = 1 << 7;
const GLOBAL = 1 << 8;

//const NO_EXECUTE = 1 << 63;
const NO_EXECUTE = 0;
const NO_EXECUTE = 1 << 63;
}
}

Expand Down Expand Up @@ -159,7 +171,7 @@ impl ProcessPageTableRef {
}
}

fn init_vmpl1(&mut self){
pub fn init_vmpl1(&mut self){
self.process_page_table = allocate_page();
let (mapping, table) = paddr_as_u64_slice!(self.process_page_table);
for i in 0..512 {
Expand Down Expand Up @@ -406,7 +418,6 @@ impl ProcessPageTableRef {
pub fn virt_to_phys(&self, vaddr: VirtAddr) -> PhysAddr {
let (_pgd_mapping, pgd_table) = paddr_as_table!(self.process_page_table);
let mut current_mapping = self.page_walk(&pgd_table, self.process_page_table, vaddr);
//log::info!("Current Mapping {:?}", current_mapping);
match current_mapping {
ProcessTableLevelMapping::PTE(addr, index) => {
let (_mapping, table) = paddr_as_u64_slice!(addr);
Expand All @@ -417,6 +428,61 @@ impl ProcessPageTableRef {

}

pub fn change_attr(&self, vaddr: VirtAddr, readable: bool, writable: bool,
executable: bool, writecopy: bool) {
// Change page table attributes
// NOTE: this function assumes that we operate on the trusted process's page table
// specifically, this function is called to handle Gramine's PAL mprotect request

let (_pgd_mapping, pgd_table) = paddr_as_table!(self.process_page_table);
let current_mapping = self.page_walk(&pgd_table, self.process_page_table, vaddr);

match current_mapping {
ProcessTableLevelMapping::PTE(addr, index) => {
let (_mapping, table) = paddr_as_u64_slice!(addr);
if readable {
// XXX: for now all pages are readable, do nothing
}
if writable || writecopy {
// XXX: see below the reason for setting writable if writecopy here
table[index] |= ProcessPageFlags::WRITABLE.bits();
} else {
table[index] &= !ProcessPageFlags::WRITABLE.bits();
}
if executable {
table[index] &= !ProcessPageFlags::NO_EXECUTE.bits();
} else {
table[index] |= ProcessPageFlags::NO_EXECUTE.bits();
}
if writecopy {
// FIXME: currently we skip this as we only support single process for now & don't have #PF handler
// TODO: implement proper CoW

// the following copies the page immidiately at this handler
// confiremd to work, but for single process program it's not necessary (I think)
// so skip this to prefer performance & smaller memory footprint
/*
let phys_mask = 0xFFFF_FFFF_F000;
let entry_attr = table[index] & !phys_mask;
let entry_phys = PhysAddr::from(table[index] & phys_mask);
let new_page = allocate_page();
let (_src_mapping, src_data) = paddr_as_slice!(entry_phys, u64);
let (new_page_mapping, new_page_mapped) = paddr_as_slice!(new_page);
rmp_adjust(new_page_mapping.virt_addr(), RMPFlags::VMPL1 | RMPFlags::RWX , PageSize::Regular).unwrap();
for i in 0..512 {
new_page_mapped[i] = src_data[i];
}
let new_entry = new_page.bits() as u64 | entry_attr | ProcessPageFlags::WRITABLE.bits();
table[index] = new_entry;
*/
}
}
_ => {
// page non-present, skip (XXX: should we handle this?)
}
}
}


/// Takes the page table of the guest OS and copies the
/// specified starteding from addr and edning at addr + size * pagesize
Expand Down Expand Up @@ -493,4 +559,72 @@ impl ProcessPageTableRef {
}
}
}

fn _copy_page_table(&self, src: PhysAddr, dst: PhysAddr, level: u64) {
// Copy the page table and its memory recursively

assert!(level <= 4 && level >= 1);

let (_src_table_mapping, src_table) = paddr_as_table!(src);
let (_dst_table_mapping, dst_table) = paddr_as_table!(dst);

for i in 0..512 {
let src_entry = src_table[i].0.bits();
// FIXME: use proper mask for the physical address
let phys_mask = 0xFFFF_FFFF_F000;
let src_entry_attr = src_entry & !phys_mask;
let src_entry_phys = PhysAddr::from(src_entry & phys_mask);
let is_present = (src_entry_attr & ProcessPageFlags::PRESENT.bits() as usize) != 0;
let is_huge_page = (src_entry_attr & ProcessPageFlags::HUGE_PAGE.bits() as usize) != 0;

if !is_present {
// XXX: we don't copy un-present pages, is this OK?
continue;
}

let new_page_phys = if is_huge_page {
unimplemented!();
} else {
allocate_page()
};
assert!(new_page_phys != PhysAddr::null());

// copy the entry
dst_table[i] = ProcessPageTableEntry(PhysAddr::from(new_page_phys.bits() | src_entry_attr));

if level > 1 && !is_huge_page {
let (_new_mapping, _) = paddr_as_table!(new_page_phys);
rmp_adjust(_new_mapping.virt_addr(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();

// copy the next level
self._copy_page_table(src_entry_phys, new_page_phys, level - 1);
} else {
// this is the last level, copy the data into the new page
let (_src_mapping, src_data) = paddr_as_slice!(src_entry_phys, u64);
let (_dst_mapping, dst_data) = paddr_as_slice!(new_page_phys, u64);
rmp_adjust(_dst_mapping.virt_addr(), RMPFlags::VMPL1 | RMPFlags::RWX, PageSize::Regular).unwrap();

let size = if level == 1 {
4096
} else if level == 2 {
512 * 4096 // 2MB
} else if level == 3 {
512 * 512 * 4096 // 1GB
} else {
unreachable!();
} / core::mem::size_of::<u64>();

for j in 0..size {
dst_data[j] = src_data[j];
}
}
}
}

pub fn copy_from(&mut self, other: &ProcessPageTableRef) {
// Copy the page table and its memory from the other ProcessPageTableRef
assert!(self.process_page_table != PhysAddr::null());
assert!(other.process_page_table != PhysAddr::null());
self._copy_page_table(other.process_page_table, self.process_page_table, 4);
}
}
Loading
Loading