From 09b8242bb249a4c141aec54b067a0ebff6b789cd Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 20 May 2021 11:42:31 +0800 Subject: [PATCH] kernel: get process termination "working" on risc-v This successfully allows a process to call `terminate_process(val)`. Signed-off-by: Sean Cross --- kernel/Cargo.toml | 2 +- kernel/src/arch/riscv/mem.rs | 10 +++++ kernel/src/arch/riscv/process.rs | 26 +++++------- kernel/src/irq.rs | 15 +++++++ kernel/src/mem.rs | 69 ++++++++++++++++++++++++++------ kernel/src/services.rs | 20 +++++---- kernel/src/syscall.rs | 7 +++- 7 files changed, 110 insertions(+), 39 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 2a522c6c9..546fd2d3a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -22,7 +22,7 @@ debug-print = [] print-panics = [] report-memory = ["stats_alloc"] wrap-print = [] -# default = ["print-panics", "debug-print"] +# default = ["print-panics", "debug-print", "wrap-print"] default = ["print-panics"] [target.'cfg(any(windows, unix))'.dependencies] diff --git a/kernel/src/arch/riscv/mem.rs b/kernel/src/arch/riscv/mem.rs index 2f8734b50..34e6a6fbb 100644 --- a/kernel/src/arch/riscv/mem.rs +++ b/kernel/src/arch/riscv/mem.rs @@ -477,6 +477,16 @@ pub fn move_page_inner( result } +/// Determine if a page has been lent. +pub fn page_is_lent(src_addr: *mut u8) -> bool { + let entry = if let Ok(val) = pagetable_entry(src_addr as usize) { + val + } else { + return false; + }; + *entry & MMUFlags::S.bits() != 0 +} + /// Mark the given virtual address as being lent. If `writable`, clear the /// `valid` bit so that this process can't accidentally write to this page while /// it is lent. diff --git a/kernel/src/arch/riscv/process.rs b/kernel/src/arch/riscv/process.rs index ec7824101..8bc9d4b02 100644 --- a/kernel/src/arch/riscv/process.rs +++ b/kernel/src/arch/riscv/process.rs @@ -70,16 +70,13 @@ struct ProcessTable { /// The process upon which the current syscall is operating current: PID, - /// The number of processes that exist - // total: usize, - - /// The actual table contents + /// The actual table contents. `true` if a process is allocated, + /// `false` if it is free. table: [bool; MAX_PROCESS_COUNT], } static mut PROCESS_TABLE: ProcessTable = ProcessTable { current: unsafe { PID::new_unchecked(1) }, - // total: 0, table: [false; MAX_PROCESS_COUNT], }; @@ -432,17 +429,14 @@ impl Process { todo!(); } - pub fn destroy(_pid: PID) -> Result<(), xous_kernel::Error> { - todo!(); - // let mut process_table = unsafe { &mut *PROCESS }; - // let pid_idx = pid.get() as usize - 1; - // if pid_idx >= process_table.table.len() { - // panic!("attempted to destroy PID that exceeds table index: {}", pid); - // } - // let process = process_table.table[pid_idx].as_mut().unwrap(); - // process_table.table[pid_idx] = None; - // process_table.total -= 1; - // Ok(()) + pub fn destroy(pid: PID) -> Result<(), xous_kernel::Error> { + let mut process_table = unsafe { &mut PROCESS_TABLE }; + let pid_idx = pid.get() as usize - 1; + if pid_idx >= process_table.table.len() { + panic!("attempted to destroy PID that exceeds table index: {}", pid); + } + process_table.table[pid_idx] = false; + Ok(()) } } diff --git a/kernel/src/irq.rs b/kernel/src/irq.rs index dec50ba4d..b056253be 100644 --- a/kernel/src/irq.rs +++ b/kernel/src/irq.rs @@ -65,3 +65,18 @@ pub fn interrupt_claim( result } } + +/// Iterate through the IRQ handlers and remove any handler that exists +/// for the given PID. +pub fn release_interrupts_for_pid(pid: PID) { + unsafe { + for (irq, handler) in IRQ_HANDLERS.iter_mut().enumerate() { + if let Some(h) = handler { + if h.0 == pid { + arch::irq::disable_irq(irq).unwrap(); + *handler = None; + } + } + } + } +} diff --git a/kernel/src/mem.rs b/kernel/src/mem.rs index a17dd27ab..0ae6aca6c 100644 --- a/kernel/src/mem.rs +++ b/kernel/src/mem.rs @@ -3,9 +3,8 @@ use core::fmt; use core::slice; -use core::str; -pub use crate::arch::mem::{MemoryMapping, PAGE_SIZE}; +pub use crate::arch::mem::{page_is_lent, MemoryMapping, PAGE_SIZE}; use crate::arch::process::Process; use xous_kernel::{MemoryFlags, MemoryRange, PID}; @@ -27,18 +26,13 @@ pub struct MemoryRangeExtra { impl fmt::Display for MemoryRangeExtra { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let tag_name_bytes = self.mem_tag.to_le_bytes(); - let s = unsafe { - // First, we build a &[u8]... - let slice = slice::from_raw_parts(tag_name_bytes.as_ptr(), 4); - // ... and then convert that slice into a string slice - str::from_utf8_unchecked(slice) - }; - write!( f, - "{} ({:08x}) {:08x} - {:08x} {} bytes", - s, + "{}{}{}{} - ({:08x}) {:08x} - {:08x} {} bytes", + ((self.mem_tag >> 0) & 0xff) as u8 as char, + ((self.mem_tag >> 8) & 0xff) as u8 as char, + ((self.mem_tag >> 16) & 0xff) as u8 as char, + ((self.mem_tag >> 24) & 0xff) as u8 as char, self.mem_tag, self.mem_start, self.mem_start + self.mem_size, @@ -699,4 +693,55 @@ impl MemoryManager { fn release_page(&mut self, addr: *mut usize, pid: PID) -> Result<(), xous_kernel::Error> { self.claim_release_move(addr, pid, ClaimReleaseMove::Release) } + + /// Convert an offset in the `MEMORY_ALLOCATIONS` array into a physical address. + fn allocation_offset_to_address(&self, offset: usize) -> Option { + // If the offset is within the RAM size, simply turn it into + // an address. + if offset < self.ram_size as usize / PAGE_SIZE { + Some(self.ram_start as usize + offset * PAGE_SIZE) + } else { + // The offset is not within RAM, so it must be in the extra + // regions list. Loop through all regions looking for the + // address. NOTE: This needs to be linear because each memory + // region has a different length. + let mut offset_in_region = offset - (self.ram_size as usize / PAGE_SIZE); + unsafe { + for region in EXTRA_REGIONS { + // If the offset exceeds the current region, skip to the + // next region. + if offset_in_region >= (region.mem_size as usize / PAGE_SIZE) { + offset_in_region -= region.mem_size as usize / PAGE_SIZE; + continue; + } + return Some(region.mem_start as usize + (offset_in_region * PAGE_SIZE)); + } + } + + // No region was found. + None + } + } + + /// Free all memory that belongs to a process. This does not unmap the + /// memory from the process, it only marks it as free. + /// This is very unsafe because the memory can immediately be re-allocated + /// to another process, so only call this as part of destroying a process. + pub unsafe fn release_all_memory_for_process(&mut self, pid: PID) { + for (idx, owner) in MEMORY_ALLOCATIONS.iter_mut().enumerate() { + // If this address has been allocated to this process, consider + // freeing it or reparenting it. + if owner == &mut Some(pid) { + let phys_addr = self.allocation_offset_to_address(idx).unwrap(); + if page_is_lent(phys_addr as *mut u8) { + // If the page is lent, reparent it to PID 1 so it will + // get freed when it is returned. + *owner = PID::new(1); + } else { + // Mark this page as free, which allows it to be re-allocated. + *owner = None; + } + } + } + } } diff --git a/kernel/src/services.rs b/kernel/src/services.rs index 4301da148..9bf26d9e7 100644 --- a/kernel/src/services.rs +++ b/kernel/src/services.rs @@ -194,11 +194,15 @@ impl Process { return Err(xous_kernel::Error::ProcessNotFound); } - // TODO: Free all pages + // Free all associated memory pages + unsafe { + crate::mem::MemoryManager::with_mut(|mm| mm.release_all_memory_for_process(self.pid)) + }; - // TODO: Free all IRQs + // Free all claimed IRQs + crate::irq::release_interrupts_for_pid(self.pid); - // TODO: Free memory mapping + // Remove this PID from the process table crate::arch::process::Process::destroy(self.pid)?; self.state = ProcessState::Free; Ok(()) @@ -582,12 +586,12 @@ impl SystemServices { tid: Option, ) -> Result<(), xous_kernel::Error> { let process = self.get_process_mut(pid)?; - // println!( + // klog!( // "switch_to_thread({}:{:?}): Old state was {:?}", // pid, tid, process.state // ); - // Determine which context number to switch to + // Determine which thread to switch to process.state = match process.state { ProcessState::Free => return Err(xous_kernel::Error::ProcessNotFound), ProcessState::Sleeping => return Err(xous_kernel::Error::ProcessNotFound), @@ -1841,6 +1845,8 @@ impl SystemServices { } } + let process = self.processes[(server.pid.get() - 1) as usize]; + process.activate().unwrap(); // Look through this server's memory space to determine if this process // is mentioned there as having some memory lent out. server.discard_messages_for_pid(target_pid); @@ -1850,10 +1856,8 @@ impl SystemServices { process.activate()?; let parent_pid = process.ppid; process.terminate()?; - // println!("KERNEL({}): Terminated", target_pid); - let process = self.get_process(parent_pid)?; - process.activate().unwrap(); + self.switch_to_thread(parent_pid, None).unwrap(); Ok(parent_pid) } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 88fd7d7e7..6d669082c 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -718,7 +718,8 @@ pub fn handle_inner(pid: PID, tid: TID, in_irq: bool, call: SysCall) -> SysCallR unsafe { assert!( SWITCHTO_CALLER.is_none(), - "SWITCHTO_CALLER was not None, indicating SwitchTo was called twice" + "SWITCHTO_CALLER was {:?} and not None, indicating SwitchTo was called twice", + SWITCHTO_CALLER, ); SWITCHTO_CALLER = Some((pid, tid)); } @@ -802,7 +803,9 @@ pub fn handle_inner(pid: PID, tid: TID, in_irq: bool, call: SysCall) -> SysCallR SysCall::TerminateProcess(_ret) => SystemServices::with_mut(|ss| { ss.switch_from_thread(pid, tid)?; ss.terminate_process(pid)?; - Ok(xous_kernel::Result::Ok) + // Clear out `SWITCHTO_CALLER` since we're resuming the parent process. + unsafe { SWITCHTO_CALLER = None }; + Ok(xous_kernel::Result::ResumeProcess) }), SysCall::Shutdown => { SystemServices::with_mut(|ss| ss.shutdown().map(|_| xous_kernel::Result::Ok))