1
0
mirror of https://github.com/sgmarz/osblog.git synced 2024-11-27 20:03:32 +04:00

Updated to allow for system calls that change process state

This commit is contained in:
Stephen Marz 2020-04-24 14:39:33 -04:00
parent e39f6b71c5
commit 599627b74a
6 changed files with 309 additions and 128 deletions

View File

@ -38,9 +38,6 @@ m_trap_vector:
# in cpu.rs we have a structure of: # in cpu.rs we have a structure of:
# 32 gp regs 0 # 32 gp regs 0
# 32 fp regs 256 # 32 fp regs 256
# SATP register 512
# Trap stack 520
# CPU HARTID 528
# We use t6 as the temporary register because it is the very # We use t6 as the temporary register because it is the very
# bottom register (x31) # bottom register (x31)
.set i, 0 .set i, 0
@ -99,15 +96,22 @@ switch_to_user:
# a2 - SATP Register # a2 - SATP Register
csrw mscratch, a0 csrw mscratch, a0
// Load program counter # Load program counter
ld a1, 520(a0) ld a1, 520(a0)
// Load satp # Load satp
ld a2, 512(a0) ld a2, 512(a0)
# Load processor mode
ld a3, 552(a0)
# Pid
# ld a4, 544(a0)
# 1 << 7 is MPIE # 1 << 7 is MPIE
# Since user mode is 00, we don't need to set anything # Since user mode is 00, we don't need to set anything
# in MPP (bits 12:11) # in MPP (bits 12:11)
li t0, 1 << 7 | 1 << 5 li t0, 1 << 7 | 1 << 5
# Combine enable bits with mode bits.
slli a3, a3, 11
or t0, t0, a3
csrw mstatus, t0 csrw mstatus, t0
csrw mepc, a1 csrw mepc, a1
csrw satp, a2 csrw satp, a2
@ -128,11 +132,21 @@ switch_to_user:
load_gp %i, t6 load_gp %i, t6
.set i, i+1 .set i, i+1
.endr .endr
# j .
mret mret
.global make_syscall .global make_syscall
make_syscall: make_syscall:
# We're setting this up to work with libgloss
# They want a7 to be the system call number and all parameters
# in a0 - a5
mv a7, a0
mv a0, a1
mv a1, a2
mv a2, a3
mv a3, a4
mv a4, a5
mv a5, a6
ecall ecall
ret ret

View File

@ -20,10 +20,18 @@ pub enum SatpMode {
Sv48 = 9, Sv48 = 9,
} }
#[repr(usize)]
pub enum CpuMode {
User = 0,
Supervisor = 1,
Machine = 3,
}
/// The trap frame is set into a structure /// The trap frame is set into a structure
/// and packed into each hart's mscratch register. /// and packed into each hart's mscratch register.
/// This allows for quick reference and full /// This allows for quick reference and full
/// context switch handling. /// context switch handling.
/// To make offsets easier, everything will be a usize (8 bytes)
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct TrapFrame { pub struct TrapFrame {
@ -33,6 +41,8 @@ pub struct TrapFrame {
pub pc: usize, // 520 pub pc: usize, // 520
pub hartid: usize, // 528 pub hartid: usize, // 528
pub qm: usize, // 536 pub qm: usize, // 536
pub pid: usize, // 544
pub mode: usize, // 552
} }
/// Rust requires that we initialize our structures /// Rust requires that we initialize our structures
@ -51,6 +61,8 @@ impl TrapFrame {
pc: 0, pc: 0,
hartid: 0, hartid: 0,
qm: 1, qm: 1,
pid: 0,
mode: 0,
} }
} }
} }

View File

@ -3,15 +3,8 @@
// Stephen Marz // Stephen Marz
// 27 Nov 2019 // 27 Nov 2019
use crate::{cpu::{TrapFrame, satp_fence_asid, build_satp, SatpMode}, use crate::{cpu::{build_satp, satp_fence_asid, CpuMode, SatpMode, TrapFrame},
page::{alloc, page::{alloc, dealloc, map, unmap, zalloc, EntryBits, Table, PAGE_SIZE}};
dealloc,
map,
unmap,
zalloc,
EntryBits,
Table,
PAGE_SIZE}};
use alloc::collections::vec_deque::VecDeque; use alloc::collections::vec_deque::VecDeque;
// How many pages are we going to give a process for their // How many pages are we going to give a process for their
@ -42,6 +35,56 @@ extern "C" {
fn make_syscall(a: usize) -> usize; fn make_syscall(a: usize) -> usize;
} }
/// Set a process' state to running. This doesn't do any checks.
/// If this PID is not found, this returns false. Otherwise, it
/// returns true.
pub fn set_running(pid: u16) -> bool {
// Yes, this is O(n). A better idea here would be a static list
// of process pointers.
let mut retval = false;
unsafe {
if let Some(mut pl) = PROCESS_LIST.take() {
for proc in pl.iter_mut() {
if proc.pid == pid {
proc.set_state(ProcessState::Running);
retval = true;
break;
}
}
// Now, we no longer need the owned Deque, so we hand it
// back by replacing the PROCESS_LIST's None with the
// Some(pl).
PROCESS_LIST.replace(pl);
}
}
retval
}
/// Set a process' state to waiting. This doesn't do any checks.
/// If this PID is not found, this returns false. Otherwise, it
/// returns true.
pub fn set_waiting(pid: u16) -> bool {
// Yes, this is O(n). A better idea here would be a static list
// of process pointers.
let mut retval = false;
unsafe {
if let Some(mut pl) = PROCESS_LIST.take() {
for proc in pl.iter_mut() {
if proc.pid == pid {
proc.set_state(ProcessState::Waiting);
retval = true;
break;
}
}
// Now, we no longer need the owned Deque, so we hand it
// back by replacing the PROCESS_LIST's None with the
// Some(pl).
PROCESS_LIST.replace(pl);
}
}
retval
}
/// We will eventually move this function out of here, but its /// We will eventually move this function out of here, but its
/// job is just to take a slot in the process list. /// job is just to take a slot in the process list.
fn init_process() { fn init_process() {
@ -50,6 +93,7 @@ fn init_process() {
let mut i: usize = 0; let mut i: usize = 0;
loop { loop {
i += 1; i += 1;
// Eventually, this will be a sleep system call.
if i > 100_000_000 { if i > 100_000_000 {
unsafe { unsafe {
make_syscall(1); make_syscall(1);
@ -89,6 +133,61 @@ pub fn add_process_default(pr: fn()) {
} }
} }
/// Add a kernel process.
pub fn add_kernel_process(func: fn()) {
// This is the Rust-ism that really trips up C++ programmers.
// PROCESS_LIST is wrapped in an Option<> enumeration, which
// means that the Option owns the Deque. We can only borrow from
// it or move ownership to us. In this case, we choose the
// latter, where we move ownership to us, add a process, and
// then move ownership back to the PROCESS_LIST.
// This allows mutual exclusion as anyone else trying to grab
// the process list will get None rather than the Deque.
if let Some(mut pl) = unsafe { PROCESS_LIST.take() } {
// .take() will replace PROCESS_LIST with None and give
// us the only copy of the Deque.
let func_addr = func as usize;
let func_vaddr = func_addr; //- 0x6000_0000;
// println!("func_addr = {:x} -> {:x}", func_addr, func_vaddr);
// We will convert NEXT_PID below into an atomic increment when
// we start getting into multi-hart processing. For now, we want
// a process. Get it to work, then improve it!
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
stack: alloc(STACK_PAGES),
pid: unsafe { NEXT_PID },
root: zalloc(1) as *mut Table,
state: ProcessState::Running,
data: ProcessData::zero(),
sleep_until: 0, };
unsafe {
NEXT_PID += 1;
}
// Now we move the stack pointer to the bottom of the
// allocation. The spec shows that register x2 (2) is the stack
// pointer.
// We could use ret_proc.stack.add, but that's an unsafe
// function which would require an unsafe block. So, convert it
// to usize first and then add PAGE_SIZE is better.
// We also need to set the stack adjustment so that it is at the
// bottom of the memory and far away from heap allocations.
unsafe {
(*ret_proc.frame).pc = func_vaddr;
(*ret_proc.frame).regs[2] = ret_proc.stack as usize + STACK_PAGES * 4096;
(*ret_proc.frame).mode = CpuMode::Machine as usize;
(*ret_proc.frame).pid = ret_proc.pid as usize;
}
pl.push_back(ret_proc);
// Now, we no longer need the owned Deque, so we hand it
// back by replacing the PROCESS_LIST's None with the
// Some(pl).
unsafe { PROCESS_LIST.replace(pl); }
}
// TODO: When we get to multi-hart processing, we need to keep
// trying to grab the process list. We can do this with an
// atomic instruction. but right now, we're a single-processor
// computer.
}
/// This should only be called once, and its job is to create /// This should only be called once, and its job is to create
/// the init process. Right now, this process is in the kernel, /// the init process. Right now, this process is in the kernel,
/// but later, it should call the shell. /// but later, it should call the shell.
@ -147,21 +246,31 @@ impl Process {
pub fn get_frame_address(&self) -> usize { pub fn get_frame_address(&self) -> usize {
self.frame as usize self.frame as usize
} }
pub fn get_program_counter(&self) -> usize { pub fn get_program_counter(&self) -> usize {
unsafe { (*self.frame).pc } unsafe { (*self.frame).pc }
} }
pub fn get_table_address(&self) -> usize { pub fn get_table_address(&self) -> usize {
self.root as usize self.root as usize
} }
pub fn get_state(&self) -> &ProcessState { pub fn get_state(&self) -> &ProcessState {
&self.state &self.state
} }
pub fn set_state(&mut self, ps: ProcessState) {
self.state = ps;
}
pub fn get_pid(&self) -> u16 { pub fn get_pid(&self) -> u16 {
self.pid self.pid
} }
pub fn get_sleep_until(&self) -> usize { pub fn get_sleep_until(&self) -> usize {
self.sleep_until self.sleep_until
} }
pub fn new_default(func: fn()) -> Self { pub fn new_default(func: fn()) -> Self {
let func_addr = func as usize; let func_addr = func as usize;
let func_vaddr = func_addr; //- 0x6000_0000; let func_vaddr = func_addr; //- 0x6000_0000;
@ -169,15 +278,13 @@ impl Process {
// We will convert NEXT_PID below into an atomic increment when // We will convert NEXT_PID below into an atomic increment when
// we start getting into multi-hart processing. For now, we want // we start getting into multi-hart processing. For now, we want
// a process. Get it to work, then improve it! // a process. Get it to work, then improve it!
let mut ret_proc = let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
Process { frame: zalloc(1) as *mut TrapFrame,
stack: alloc(STACK_PAGES), stack: alloc(STACK_PAGES),
pid: unsafe { NEXT_PID }, pid: unsafe { NEXT_PID },
root: zalloc(1) as *mut Table, root: zalloc(1) as *mut Table,
state: ProcessState::Running, state: ProcessState::Running,
data: ProcessData::zero(), data: ProcessData::zero(),
sleep_until: 0 sleep_until: 0, };
};
unsafe { unsafe {
satp_fence_asid(NEXT_PID as usize); satp_fence_asid(NEXT_PID as usize);
NEXT_PID += 1; NEXT_PID += 1;
@ -194,50 +301,37 @@ impl Process {
unsafe { unsafe {
(*ret_proc.frame).pc = func_vaddr; (*ret_proc.frame).pc = func_vaddr;
(*ret_proc.frame).regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES; (*ret_proc.frame).regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;
(*ret_proc.frame).mode = CpuMode::User as usize;
(*ret_proc.frame).pid = ret_proc.pid as usize;
} }
// Map the stack on the MMU // Map the stack on the MMU
let pt; let pt;
unsafe { unsafe {
pt = &mut *ret_proc.root; pt = &mut *ret_proc.root;
(*ret_proc.frame).satp = build_satp(SatpMode::Sv39, ret_proc.pid as usize, ret_proc.root as usize); (*ret_proc.frame).satp =
build_satp(SatpMode::Sv39, ret_proc.pid as usize, ret_proc.root as usize);
} }
// We need to map the stack onto the user process' virtual // We need to map the stack onto the user process' virtual
// memory This gets a little hairy because we need to also map // memory This gets a little hairy because we need to also map
// the function code too. // the function code too.
for i in 0..STACK_PAGES { for i in 0..STACK_PAGES {
let addr = i * PAGE_SIZE; let addr = i * PAGE_SIZE;
map( map(pt, STACK_ADDR + addr, saddr + addr, EntryBits::UserReadWrite.val(), 0);
pt,
STACK_ADDR + addr,
saddr + addr,
EntryBits::UserReadWrite.val(),
0,
);
// println!("Set stack from 0x{:016x} -> 0x{:016x}", STACK_ADDR + addr, saddr + addr); // println!("Set stack from 0x{:016x} -> 0x{:016x}", STACK_ADDR + addr, saddr + addr);
} }
// Map the program counter on the MMU and other bits // Map the program counter on the MMU and other bits
for i in 0..=100 { for i in 0..=100 {
let modifier = i * 0x1000; let modifier = i * 0x1000;
map( map(pt, func_vaddr + modifier, func_addr + modifier, EntryBits::UserReadWriteExecute.val(), 0);
pt,
func_vaddr + modifier,
func_addr + modifier,
EntryBits::UserReadWriteExecute.val(),
0,
);
} }
// This is the make_syscall function // This is the make_syscall function
// The reason we need this is because we're running a process // The reason we need this is because we're running a process
// that is inside of the kernel. When we start loading from a block // that is inside of the kernel. When we start loading from a block
// devices, we can load the instructions anywhere in memory. // devices, we can load the instructions anywhere in memory.
map(pt, 0x8000_0000, 0x8000_0000, EntryBits::UserReadExecute.val(), 0); for i in 0..=7 {
map(pt, 0x8000_1000, 0x8000_1000, EntryBits::UserReadExecute.val(), 0); let addr = 0x8000_0000 | i << 12;
map(pt, 0x8000_2000, 0x8000_2000, EntryBits::UserReadExecute.val(), 0); map(pt, addr, addr, EntryBits::UserReadExecute.val(), 0);
map(pt, 0x8000_3000, 0x8000_3000, EntryBits::UserReadExecute.val(), 0); }
map(pt, 0x8000_4000, 0x8000_4000, EntryBits::UserReadExecute.val(), 0);
map(pt, 0x8000_5000, 0x8000_5000, EntryBits::UserReadExecute.val(), 0);
map(pt, 0x8000_6000, 0x8000_6000, EntryBits::UserReadExecute.val(), 0);
map(pt, 0x8000_7000, 0x8000_7000, EntryBits::UserReadExecute.val(), 0);
ret_proc ret_proc
} }
} }

View File

@ -6,10 +6,12 @@
use crate::process::{ProcessState, PROCESS_LIST}; use crate::process::{ProcessState, PROCESS_LIST};
pub fn schedule() -> usize { pub fn schedule() -> usize {
let mut frame_addr: usize = 0x1111;
unsafe { unsafe {
if let Some(mut pl) = PROCESS_LIST.take() { if let Some(mut pl) = PROCESS_LIST.take() {
let mut done = false;
while !done {
pl.rotate_left(1); pl.rotate_left(1);
let mut frame_addr: usize = 0;
// let mut mepc: usize = 0; // let mut mepc: usize = 0;
// let mut satp: usize = 0; // let mut satp: usize = 0;
// let mut pid: usize = 0; // let mut pid: usize = 0;
@ -18,6 +20,8 @@ pub fn schedule() -> usize {
ProcessState::Running => { ProcessState::Running => {
frame_addr = frame_addr =
prc.get_frame_address(); prc.get_frame_address();
done = true;
// println!("Process is running on frame 0x{:x}", frame_addr);
// satp = prc.get_table_address(); // satp = prc.get_table_address();
// pid = prc.get_pid() as usize; // pid = prc.get_pid() as usize;
}, },
@ -25,17 +29,12 @@ pub fn schedule() -> usize {
_ => {}, _ => {},
} }
} }
// println!("Scheduling {}", pid); }
PROCESS_LIST.replace(pl); PROCESS_LIST.replace(pl);
if frame_addr != 0 { }
// MODE 8 is 39-bit virtual address MMU else {
// I'm using the PID as the address space println!("could not take process list");
// identifier to hopefully help with (not?)
// flushing the TLB whenever we switch
// processes.
return frame_addr;
} }
} }
} frame_addr
0
} }

View File

@ -3,13 +3,13 @@
// Stephen Marz // Stephen Marz
// 3 Jan 2020 // 3 Jan 2020
use crate::cpu::TrapFrame; use crate::{block::process_read, cpu::TrapFrame};
pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize { pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
let syscall_number; let syscall_number;
unsafe { unsafe {
// A0 is X10, so it's register number 10. // A7 is X17, so it's register number 17.
syscall_number = (*frame).regs[10]; syscall_number = (*frame).regs[17];
// for i in 0..32 { // for i in 0..32 {
// print!("regs[{:02}] = 0x{:08x} ", i, (*frame).regs[i]); // print!("regs[{:02}] = 0x{:08x} ", i, (*frame).regs[i]);
// if (i+1) % 4 == 0 { // if (i+1) % 4 == 0 {
@ -17,8 +17,52 @@ pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
// } // }
// } // }
} }
// These system call numbers come from libgloss so that we can use newlib
// for our system calls.
// Libgloss wants the system call number in A7 and arguments in A0..A6
// #define SYS_getcwd 17
// #define SYS_dup 23
// #define SYS_fcntl 25
// #define SYS_faccessat 48
// #define SYS_chdir 49
// #define SYS_openat 56
// #define SYS_close 57
// #define SYS_getdents 61
// #define SYS_lseek 62
// #define SYS_read 63
// #define SYS_write 64
// #define SYS_writev 66
// #define SYS_pread 67
// #define SYS_pwrite 68
// #define SYS_fstatat 79
// #define SYS_fstat 80
// #define SYS_exit 93
// #define SYS_exit_group 94
// #define SYS_kill 129
// #define SYS_rt_sigaction 134
// #define SYS_times 153
// #define SYS_uname 160
// #define SYS_gettimeofday 169
// #define SYS_getpid 172
// #define SYS_getuid 174
// #define SYS_geteuid 175
// #define SYS_getgid 176
// #define SYS_getegid 177
// #define SYS_brk 214
// #define SYS_munmap 215
// #define SYS_mremap 216
// #define SYS_mmap 222
// #define SYS_open 1024
// #define SYS_link 1025
// #define SYS_unlink 1026
// #define SYS_mkdir 1030
// #define SYS_access 1033
// #define SYS_stat 1038
// #define SYS_lstat 1039
// #define SYS_time 1062
// #define SYS_getmainvars 2011
match syscall_number { match syscall_number {
0 => { 0 | 93 => {
// Exit // Exit
// Currently, we cannot kill a process, it runs forever. We will delete // Currently, we cannot kill a process, it runs forever. We will delete
// the process later and free the resources, but for now, we want to get // the process later and free the resources, but for now, we want to get
@ -29,9 +73,23 @@ pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
println!("Test syscall"); println!("Test syscall");
mepc + 4 mepc + 4
}, },
63 => unsafe {
// Read system call
// This is an asynchronous call. This will get the process going. We won't hear the answer until
// we an interrupt back.
let _ = process_read(
(*frame).pid as u16,
(*frame).regs[10],
(*frame).regs[11] as *mut u8,
(*frame).regs[12] as u32,
(*frame).regs[13] as u64,
);
// If we return 0, the trap handler will schedule another process.
0
},
_ => { _ => {
println!("Unknown syscall number {}", syscall_number); println!("Unknown syscall number {}", syscall_number);
mepc + 4 mepc + 4
} },
} }
} }

View File

@ -37,11 +37,15 @@ extern "C" fn m_trap(epc: usize,
// number. So, here we narrow down just the cause number. // number. So, here we narrow down just the cause number.
let cause_num = cause & 0xfff; let cause_num = cause & 0xfff;
let mut return_pc = epc; let mut return_pc = epc;
unsafe {
(*frame).pc = return_pc;
}
if is_async { if is_async {
// Asynchronous trap // Asynchronous trap
match cause_num { match cause_num {
3 => { 3 => {
// Machine software // We will use this to awaken our other CPUs so they can process
// processes.
println!("Machine software interrupt CPU #{}", hart); println!("Machine software interrupt CPU #{}", hart);
}, },
7 => { 7 => {
@ -80,19 +84,19 @@ extern "C" fn m_trap(epc: usize,
// them later. // them later.
loop {} loop {}
}, },
8 => { 8 | 9 | 11 => unsafe {
// Environment (system) call from User mode // Environment (system) call from User, Supervisor, and Machine modes
// println!("E-call from User mode! CPU#{} -> 0x{:08x}", hart, epc); // println!("E-call from User mode! CPU#{} -> 0x{:08x}", hart, epc);
return_pc = do_syscall(return_pc, frame); return_pc = do_syscall(return_pc, frame);
}, if return_pc == 0 {
9 => { // We are about to schedule something else here, so we need to store PAST
// Environment (system) call from Supervisor mode // the system call so that when we resume this process, we're after the ecall.
println!("E-call from Supervisor mode! CPU#{} -> 0x{:08x}", hart, epc); (*frame).pc += 4;
return_pc = do_syscall(return_pc, frame); let frame = schedule();
}, // let p = frame as *const crate::process::Process;
11 => { schedule_next_context_switch(1);
// Environment (system) call from Machine mode rust_switch_to_user(frame);
panic!("E-call from Machine mode! CPU#{} -> 0x{:08x}\n", hart, epc); }
}, },
// Page faults // Page faults
12 => { 12 => {