rewrite trap.c

This commit is contained in:
Garen Tyler 2023-10-20 17:45:34 -06:00
parent 96cfda564c
commit 572545cb8f
Signed by: garentyler
GPG Key ID: D7A048C454CB7054
12 changed files with 216 additions and 297 deletions

View File

@ -9,7 +9,6 @@ OBJS = \
$K/proc.o \
$K/swtch.o \
$K/trampoline.o \
$K/trap.o \
$K/bio.o \
$K/fs.o \
$K/log.o \

View File

@ -10,14 +10,11 @@
use crate::{
file::{devsw, CONSOLE},
proc::{killed, myproc, sleep_mutex, wakeup, procdump},
proc::{killed, myproc, procdump, sleep_mutex, wakeup},
sync::spinmutex::SpinMutex,
uart::{uartinit, uartputc, uartputc_sync},
};
use core::{
ffi::c_void,
ptr::addr_of_mut,
uart::{uartinit, uartputc, Uart},
};
use core::{ffi::c_void, ptr::addr_of_mut};
extern "C" {
fn either_copyin(dst: *mut c_void, user_src: i32, src: u64, len: u64) -> i32;
@ -65,15 +62,13 @@ const fn ctrl_x(x: u8) -> u8 {
/// Called by printf(), and to echo input
/// characters but not from write().
pub fn consputc(c: u8) {
unsafe {
if c == BACKSPACE {
// If the user typed backspace, overwrite with a space.
uartputc_sync(0x08);
uartputc_sync(b' ');
uartputc_sync(0x08);
} else {
uartputc_sync(c);
}
if c == BACKSPACE {
// If the user typed backspace, overwrite with a space.
Uart::write_byte_sync(0x08);
Uart::write_byte_sync(b' ');
Uart::write_byte_sync(0x08);
} else {
Uart::write_byte_sync(c);
}
}
@ -114,12 +109,9 @@ pub unsafe extern "C" fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) ->
// cons.lock.unlock();
return -1;
}
sleep_mutex(
addr_of_mut!(console.read_index).cast(),
&mut console,
);
sleep_mutex(addr_of_mut!(console.read_index).cast(), &mut console);
}
c = *console.read_byte();
console.read_index += 1;
@ -177,7 +169,9 @@ pub fn consoleintr(mut c: u8) {
unsafe { procdump() };
} else if c == ctrl_x(b'U') {
// Kill line.
while console.edit_index != console.write_index && console.buffer[(console.edit_index - 1) % INPUT_BUF_SIZE] != b'\n' {
while console.edit_index != console.write_index
&& console.buffer[(console.edit_index - 1) % INPUT_BUF_SIZE] != b'\n'
{
console.edit_index -= 1;
consputc(BACKSPACE);
}
@ -197,7 +191,10 @@ pub fn consoleintr(mut c: u8) {
*console.edit_byte() = c;
console.edit_index += 1;
if c == b'\n' || c == ctrl_x(b'D') || console.edit_index - console.read_index == INPUT_BUF_SIZE {
if c == b'\n'
|| c == ctrl_x(b'D')
|| console.edit_index - console.read_index == INPUT_BUF_SIZE
{
// Wake up consoleread() if a whole line (or EOF) has arrived.
console.write_index = console.edit_index;
unsafe { wakeup(addr_of_mut!(console.read_index).cast()) };

View File

@ -4,8 +4,8 @@
use crate::{
riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE},
sync::spinlock::Spinlock,
string::memset,
sync::spinlock::Spinlock,
};
use core::{
ffi::{c_char, CStr},

View File

@ -16,9 +16,9 @@ pub(crate) mod param;
pub mod printf;
pub mod proc;
pub(crate) mod riscv;
pub mod sync;
pub mod start;
pub mod string;
pub mod sync;
pub mod syscall;
pub mod sysproc;
pub mod trap;
@ -85,7 +85,7 @@ fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! {
}
unsafe { crate::PANICKED = true };
loop {
core::hint::spin_loop();
}

View File

@ -90,7 +90,7 @@ impl Context {
#[derive(Copy, Clone)]
pub struct Cpu {
pub proc: *mut Proc,
/// swtch() here to enter scheduler()
/// swtch() here to enter scheduler()
pub context: Context,
/// Depth of push_off() nesting.
pub interrupt_disable_layers: i32,
@ -239,10 +239,9 @@ pub unsafe extern "C" fn mycpu() -> *mut Cpu {
/// Return the current struct proc *, or zero if none.
#[no_mangle]
pub unsafe extern "C" fn myproc() -> *mut Proc {
push_off();
let _ = crate::trap::InterruptBlocker::new();
let c = mycpu();
let p = (*c).proc;
pop_off();
p
}
@ -499,4 +498,3 @@ pub unsafe extern "C" fn killed(p: *mut Proc) -> i32 {
(*p).lock.unlock();
k
}

View File

@ -67,7 +67,7 @@ pub const PHYSTOP: u64 = KERNBASE + 128 * 1024 * 1024;
pub const TRAMPOLINE: u64 = MAXVA - PGSIZE;
// Map kernel stacks beneath the trampoline,
// each surrouned by invalid guard pages.
// each surrounded by invalid guard pages.
pub fn kstack(p: u64) -> u64 {
TRAMPOLINE - (p + 1) * 2 * PGSIZE
}

View File

@ -43,6 +43,10 @@ pub const MIE_MSIE: u64 = 1 << 3;
pub const SATP_SV39: u64 = 8 << 60;
pub fn make_satp(pagetable: u64) -> u64 {
SATP_SV39 | (pagetable >> 12)
}
/// Bytes per page
pub const PGSIZE: u64 = 4096;
/// Bits of offset within a page

View File

@ -57,7 +57,7 @@ impl Spinlock {
}
self.cpu = null_mut();
self.locked.store(false, Ordering::Release);
pop_off();

View File

@ -1,4 +1,8 @@
use core::{cell::UnsafeCell, ops::{Deref, DerefMut, Drop}, sync::atomic::{AtomicBool, Ordering}};
use core::{
cell::UnsafeCell,
ops::{Deref, DerefMut, Drop},
sync::atomic::{AtomicBool, Ordering},
};
pub struct SpinMutex<T> {
locked: AtomicBool,
@ -15,7 +19,7 @@ impl<T> SpinMutex<T> {
while self.locked.swap(true, Ordering::Acquire) {
core::hint::spin_loop();
}
SpinMutexGuard { mutex: &self }
SpinMutexGuard { mutex: self }
}
pub unsafe fn unlock(&self) {
self.locked.store(false, Ordering::Release);
@ -30,7 +34,7 @@ impl<'m, T> Deref for SpinMutexGuard<'m, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { & *self.mutex.inner.get() }
unsafe { &*self.mutex.inner.get() }
}
}
impl<'m, T> DerefMut for SpinMutexGuard<'m, T> {

View File

@ -1,17 +1,25 @@
use crate::{
printf::print,
proc::{cpuid, wakeup, mycpu},
proc::{cpuid, exit, killed, mycpu, myproc, r#yield, setkilled, wakeup, ProcState},
riscv::*,
sync::spinlock::Spinlock,
syscall::syscall,
};
use core::{
ffi::{c_char, CStr},
ptr::{addr_of, addr_of_mut},
};
use core::{ffi::CStr, ptr::addr_of_mut};
extern "C" {
pub fn kernelvec();
pub fn usertrap();
pub fn usertrapret();
fn syscall();
// pub fn usertrap();
// pub fn usertrapret();
// fn syscall();
// pub fn userret(satp: u64);
fn virtio_disk_intr();
pub static mut trampoline: [c_char; 0];
pub static mut uservec: [c_char; 0];
pub static mut userret: [c_char; 0];
}
#[no_mangle]
@ -43,59 +51,6 @@ pub unsafe extern "C" fn clockintr() {
tickslock.unlock();
}
// /// Handle an interrupt, exception, or syscall from user space.
// /// Called from trampoline.S.
// #[no_mangle]
// pub unsafe extern "C" fn usertrap() {
// if r_sstatus() & SSTATUS_SPP != 0 {
// panic!("usertrap: not from user mode");
// }
//
// // Send interrupts and exceptions to kerneltrap(),
// // since we're now in the kernel.
// w_stvec(kernelvec as usize as u64);
//
// let p = myproc();
//
// // Save user program counter.
// (*(*p).trapframe).epc = r_sepc();
//
// if r_scause() == 8 {
// // Syscall
//
// if killed(p) > 0 {
// exit(-1);
// }
//
// // sepc points to the ecall instruction,
// // but we want to return to the next instruction.
// (*(*p).trapframe).epc += 4;
//
// // An interrupt will change sepc, scause, and sstatus,
// // so enable only now that we're done with those registers.
// intr_on();
//
// syscall();
// } else {
// let which_dev = devintr();
// if which_dev == 0 {
// print!("usertrap(): unexpected scause {:#018x} pid={}\n", r_scause(), (*p).pid);
// print!(" sepc={:#018x} stval={:#018x}\n", r_sepc(), r_stval());
// setkilled(p);
// }
// if killed(p) > 0 {
// exit(-1);
// }
//
// // Give up the CPU if this is a timer interrupt.
// if which_dev == 2 {
// r#yield();
// }
//
// usertrapret();
// }
// }
/// Check if it's an external interrupt or software interrupt and handle it.
///
/// Returns 2 if timer interrupt, 1 if other device, 0 if not recognized.
@ -143,13 +98,14 @@ pub unsafe extern "C" fn devintr() -> i32 {
}
}
#[derive(Default)]
pub struct InterruptBlocker;
impl InterruptBlocker {
pub fn new() {
pub fn new() -> InterruptBlocker {
unsafe {
let interrupts_before = intr_get();
let cpu = mycpu();
intr_off();
if (*cpu).interrupt_disable_layers == 0 {
@ -158,6 +114,7 @@ impl InterruptBlocker {
(*cpu).interrupt_disable_layers += 1;
// crate::sync::spinlock::push_off();
}
InterruptBlocker
}
}
impl core::ops::Drop for InterruptBlocker {
@ -180,3 +137,145 @@ impl core::ops::Drop for InterruptBlocker {
}
}
impl !Send for InterruptBlocker {}
/// Return to user space
#[no_mangle]
pub unsafe extern "C" fn usertrapret() {
let p = myproc();
// We're about to switch the destination of traps from
// kerneltrap() to usertrap(), so turn off interrupts until
// we're back in user space, where usertrap() is correct.
intr_off();
// Send syscalls, interrupts, and exceptions to uservec in trampoline.S
let trampoline_uservec =
TRAMPOLINE + (addr_of!(uservec) as usize as u64) - (addr_of!(trampoline) as usize as u64);
w_stvec(trampoline_uservec);
// Set up trapframe values that uservec will need when
// the process next traps into the kernel.
// kernel page table
(*(*p).trapframe).kernel_satp = r_satp();
// process's kernel stack
(*(*p).trapframe).kernel_sp = (*p).kstack + PGSIZE;
(*(*p).trapframe).kernel_trap = usertrap as usize as u64;
// hartid for cpuid()
(*(*p).trapframe).kernel_hartid = r_tp();
// Set up the registers that trampoline.S's
// sret will use to get to user space.
// Set S Previous Privelege mode to User.
let mut x = r_sstatus();
// Clear SPP to 0 for user mode.
x &= !SSTATUS_SPP;
// Enable interrupts in user mode.
x |= SSTATUS_SPIE;
w_sstatus(x);
// Set S Exception Program Counter to the saved user pc.
w_sepc((*(*p).trapframe).epc);
// Tell trampoline.S the user page table to switch to.
let satp = make_satp((*p).pagetable as usize as u64);
// Jump to userret in trampoline.S at the top of memory, which
// switches to the user page table, restores user registers,
// and switches to user mode with sret.
let trampoline_userret = (TRAMPOLINE + (addr_of!(userret) as usize as u64)
- (addr_of!(trampoline) as usize as u64)) as usize;
let trampoline_userret = trampoline_userret as *const ();
// Rust's most dangerous function: core::mem::transmute
let trampoline_userret = core::mem::transmute::<*const (), fn(u64)>(trampoline_userret);
trampoline_userret(satp)
}
/// Interrupts and exceptions from kernel code go here via kernelvec,
/// on whatever the current kernel stack is.
#[no_mangle]
pub unsafe extern "C" fn kerneltrap() {
let sepc = r_sepc();
let sstatus = r_sstatus();
let scause = r_scause();
if sstatus & SSTATUS_SPP == 0 {
panic!("kerneltrap: not from supervisor mode");
} else if intr_get() != 0 {
panic!("kerneltrap: interrupts enabled");
}
let which_dev = devintr();
if which_dev == 0 {
print!("scause {}\nsepc={} stval={}\n", scause, r_sepc(), r_stval());
panic!("kerneltrap");
} else if which_dev == 2 && !myproc().is_null() && (*myproc()).state == ProcState::Running {
// Give up the CPU if this is a timer interrupt.
r#yield();
}
// The yield() may have caused some traps to occur,
// so restore trap registers for use by kernelvec.S's sepc instruction.
w_sepc(sepc);
w_sstatus(sstatus);
}
/// Handle an interrupt, exception, or system call from userspace.
///
/// Called from trampoline.S
#[no_mangle]
pub unsafe extern "C" fn usertrap() {
if r_sstatus() & SSTATUS_SPP != 0 {
panic!("usertrap: not from user mode");
}
// Send interrupts and exceptions to kerneltrap(),
// since we're now in the kernel.
w_stvec(kernelvec as usize as u64);
let p = myproc();
// Save user program counter.
(*(*p).trapframe).epc = r_sepc();
if r_scause() == 8 {
// System call
if killed(p) > 0 {
exit(-1);
}
// sepc points to the ecall instruction, but
// we want to return to the next instruction.
(*(*p).trapframe).epc += 4;
// An interrupt will change sepc, scause, and sstatus,
// so enable only now that we're done with those registers.
intr_on();
syscall();
}
let which_dev = devintr();
if r_scause() != 8 && which_dev == 0 {
print!(
"usertrap(): unexpected scause {} {}\n\tsepc={} stval={}",
r_scause(),
(*p).pid,
r_sepc(),
r_stval()
);
setkilled(p);
}
if killed(p) > 0 {
exit(-1);
}
// Give up the CPU if this is a timer interrupt.
if which_dev == 2 {
r#yield();
}
usertrapret();
}

View File

@ -5,7 +5,7 @@ use crate::{
console::consoleintr,
proc::{sleep, sleep_mutex, wakeup},
riscv::memlayout::UART0,
sync::spinlock::{pop_off, push_off, Spinlock},
sync::spinlock::Spinlock,
sync::spinmutex::SpinMutex,
trap::InterruptBlocker,
};
@ -22,15 +22,16 @@ enum Register {
}
impl Register {
pub fn as_ptr(&self) -> *mut u8 {
let addr = UART0 + match self {
Register::ReceiveHolding => 0,
Register::TransmitHolding => 0,
Register::InterruptEnable => 1,
Register::FIFOControl => 2,
Register::InterruptStatus => 2,
Register::LineControl => 2,
Register::LineStatus => 5,
};
let addr = UART0
+ match self {
Register::ReceiveHolding => 0,
Register::TransmitHolding => 0,
Register::InterruptEnable => 1,
Register::FIFOControl => 2,
Register::InterruptStatus => 2,
Register::LineControl => 2,
Register::LineStatus => 5,
};
addr as *mut u8
}
pub fn read(&self) -> u8 {
@ -53,9 +54,12 @@ pub struct Uart {
pub read_index: usize,
}
impl Uart {
/// Alternate version of Uart::write_byte() that doesn't
/// use interrupts, for use by kernel printf() and
/// to echo characters. It spins waiting for the UART's
/// output register to be empty.
pub fn write_byte_sync(x: u8) {
// let _ = InterruptBlocker::new();
unsafe { push_off(); }
let _ = InterruptBlocker::new();
if unsafe { crate::PANICKED } {
loop {
@ -69,8 +73,6 @@ impl Uart {
}
Register::TransmitHolding.write(x);
unsafe { pop_off(); }
}
/// If the UART is idle, and a character is
/// waiting in the transmit buffer, send it.
@ -175,36 +177,12 @@ pub(crate) unsafe fn uartputc(c: u8) {
);
}
uart_tx_buf[(uart_tx_w % UART_TX_BUF_SIZE) as usize] = c;
uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
uart_tx_w += 1;
uartstart();
uart_tx_lock.unlock();
}
/// Alternate version of uartputc() that doesn't
/// use interrupts, for use by kernel printf() and
/// to echo characters. It spins waiting for the UART's
/// output register to be empty.
pub(crate) unsafe fn uartputc_sync(c: u8) {
push_off();
Uart::write_byte_sync(c);
// if crate::PANICKED {
// loop {
// core::hint::spin_loop();
// }
// }
// // Wait for Transmit Holding Empty to be set in LSR.
// while Register::LineStatus.read() & LSR_TX_IDLE == 0 {
// core::hint::spin_loop();
// }
// Register::TransmitHolding.write(c);
pop_off();
}
/// If the UART is idle, and a character is waiting
/// in the transmit buffer, send it.
/// Caller must hold uart_tx_lock.
@ -223,7 +201,7 @@ unsafe fn uartstart() {
}
// let buf = uart_tx_buf.lock();
let c = uart_tx_buf[(uart_tx_r % UART_TX_BUF_SIZE) as usize];
let c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
uart_tx_r += 1;
// Maybe uartputc() is waiting for space in the buffer.
@ -248,12 +226,8 @@ pub(crate) fn uartgetc() -> Option<u8> {
/// both. Called from devintr().
pub(crate) unsafe fn uartintr() {
// Read and process incoming characters.
loop {
if let Some(c) = uartgetc() {
consoleintr(c);
} else {
break;
}
while let Some(c) = uartgetc() {
consoleintr(c);
}
// Send buffered characters.

View File

@ -1,156 +0,0 @@
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "spinlock.h"
#include "proc.h"
#include "defs.h"
extern char trampoline[], uservec[], userret[];
// in kernelvec.S, calls kerneltrap().
void kernelvec();
extern int devintr();
//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void usertrap(void)
{
int which_dev = 0;
if((r_sstatus() & SSTATUS_SPP) != 0)
panic("usertrap: not from user mode");
// send interrupts and exceptions to kerneltrap(),
// since we're now in the kernel.
w_stvec((uint64)kernelvec);
struct proc *p = myproc();
// save user program counter.
p->trapframe->epc = r_sepc();
if(r_scause() == 8){
// system call
if(killed(p))
exit(-1);
// sepc points to the ecall instruction,
// but we want to return to the next instruction.
p->trapframe->epc += 4;
// an interrupt will change sepc, scause, and sstatus,
// so enable only now that we're done with those registers.
intr_on();
syscall();
} else if((which_dev = devintr()) != 0){
// ok
} else {
printstr("usertrap(): unexepected scause ");
printptr(r_scause());
printstr(" pid=");
printint(p->pid);
printstr(" sepc=");
printptr(r_sepc());
printstr(" stval=");
printptr(r_stval());
printstr("\n");
setkilled(p);
}
if(killed(p))
exit(-1);
// give up the CPU if this is a timer interrupt.
if(which_dev == 2)
yield();
usertrapret();
}
//
// return to user space
//
void
usertrapret(void)
{
struct proc *p = myproc();
// we're about to switch the destination of traps from
// kerneltrap() to usertrap(), so turn off interrupts until
// we're back in user space, where usertrap() is correct.
intr_off();
// send syscalls, interrupts, and exceptions to uservec in trampoline.S
uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
w_stvec(trampoline_uservec);
// set up trapframe values that uservec will need when
// the process next traps into the kernel.
p->trapframe->kernel_satp = r_satp(); // kernel page table
p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
p->trapframe->kernel_trap = (uint64)usertrap;
p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
// set up the registers that trampoline.S's sret will use
// to get to user space.
// set S Previous Privilege mode to User.
unsigned long x = r_sstatus();
x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
x |= SSTATUS_SPIE; // enable interrupts in user mode
w_sstatus(x);
// set S Exception Program Counter to the saved user pc.
w_sepc(p->trapframe->epc);
// tell trampoline.S the user page table to switch to.
uint64 satp = MAKE_SATP(p->pagetable);
// jump to userret in trampoline.S at the top of memory, which
// switches to the user page table, restores user registers,
// and switches to user mode with sret.
uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);
((void (*)(uint64))trampoline_userret)(satp);
}
// interrupts and exceptions from kernel code go here via kernelvec,
// on whatever the current kernel stack is.
void
kerneltrap()
{
int which_dev = 0;
uint64 sepc = r_sepc();
uint64 sstatus = r_sstatus();
uint64 scause = r_scause();
if((sstatus & SSTATUS_SPP) == 0)
panic("kerneltrap: not from supervisor mode");
if(intr_get() != 0)
panic("kerneltrap: interrupts enabled");
if((which_dev = devintr()) == 0){
printstr("scause ");
printptr(scause);
printstr("\nsepc=");
printptr(r_sepc());
printstr(" stval=");
printptr(r_stval());
printstr("\n");
panic("kerneltrap");
}
// give up the CPU if this is a timer interrupt.
if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
yield();
// the yield() may have caused some traps to occur,
// so restore trap registers for use by kernelvec.S's sepc instruction.
w_sepc(sepc);
w_sstatus(sstatus);
}