kernel: get process termination "working" on risc-v

This successfully allows a process to call `terminate_process(val)`.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2021-05-20 11:42:31 +08:00
parent eb7895d601
commit 09b8242bb2
7 changed files with 110 additions and 39 deletions

View file

@ -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]

View file

@ -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.

View file

@ -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(())
}
}

View file

@ -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;
}
}
}
}
}

View file

@ -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<usize> {
// 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;
}
}
}
}
}

View file

@ -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<TID>,
) -> 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)
}

View file

@ -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))