Rewrite spinlock.c
This commit is contained in:
parent
b30ea849d1
commit
709ec3a50f
5
Makefile
5
Makefile
@ -10,7 +10,6 @@ OBJS = \
|
||||
$K/printf.o \
|
||||
$K/uart.o \
|
||||
$K/kalloc.o \
|
||||
$K/spinlock.o \
|
||||
$K/string.o \
|
||||
$K/main.o \
|
||||
$K/vm.o \
|
||||
@ -30,7 +29,8 @@ OBJS = \
|
||||
$K/sysfile.o \
|
||||
$K/kernelvec.o \
|
||||
$K/plic.o \
|
||||
$K/virtio_disk.o
|
||||
$K/virtio_disk.o \
|
||||
$K/riscv.o
|
||||
|
||||
# riscv64-unknown-elf- or riscv64-linux-gnu-
|
||||
# perhaps in /opt/riscv/bin
|
||||
@ -78,6 +78,7 @@ LDFLAGS = -z max-page-size=4096
|
||||
|
||||
$K/kernel: $(OBJS) $K/kernel.ld $U/initcode $R/src
|
||||
cargo +nightly -Z unstable-options -C $R build
|
||||
$(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/debug/librustkernel.a > $R/target/$(TARGET_TRIPLE)/debug/librustkernel.asm
|
||||
$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $R/target/$(TARGET_TRIPLE)/debug/librustkernel.a
|
||||
$(OBJDUMP) -S $K/kernel > $K/kernel.asm
|
||||
$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym
|
||||
|
@ -110,7 +110,7 @@ void procdump(void);
|
||||
// swtch.S
|
||||
void swtch(struct context*, struct context*);
|
||||
|
||||
// spinlock.c
|
||||
// spinlock.rs
|
||||
void acquire(struct spinlock*);
|
||||
int holding(struct spinlock*);
|
||||
void initlock(struct spinlock*, char*);
|
||||
|
@ -61,12 +61,12 @@ procinit(void)
|
||||
// Must be called with interrupts disabled,
|
||||
// to prevent race with process being moved
|
||||
// to a different CPU.
|
||||
int
|
||||
cpuid()
|
||||
{
|
||||
int id = r_tp();
|
||||
return id;
|
||||
}
|
||||
// int
|
||||
// cpuid()
|
||||
// {
|
||||
// int id = r_tp();
|
||||
// return id;
|
||||
// }
|
||||
|
||||
// Return this CPU's cpu struct.
|
||||
// Interrupts must be disabled.
|
||||
|
@ -105,3 +105,5 @@ struct proc {
|
||||
struct inode *cwd; // Current directory
|
||||
char name[16]; // Process name (debugging)
|
||||
};
|
||||
|
||||
int cpuid();
|
||||
|
30
kernel/riscv.c
Normal file
30
kernel/riscv.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include "kernel/riscv.h"
|
||||
|
||||
unsigned long rv_r_mhartid() {
|
||||
return r_mhartid();
|
||||
}
|
||||
|
||||
unsigned int rv_r_tp() {
|
||||
return r_tp();
|
||||
}
|
||||
|
||||
unsigned long rv_r_sstatus() {
|
||||
return r_sstatus();
|
||||
}
|
||||
|
||||
void rv_w_sstatus(unsigned long x) {
|
||||
w_sstatus(x);
|
||||
}
|
||||
|
||||
void rv_intr_on() {
|
||||
intr_on();
|
||||
}
|
||||
|
||||
void rv_intr_off() {
|
||||
intr_off();
|
||||
}
|
||||
|
||||
int rv_intr_get() {
|
||||
return intr_get();
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
// which hart (core) is this?
|
||||
static inline uint64
|
||||
r_mhartid()
|
||||
|
9
kernel/rustkernel/Cargo.lock
generated
9
kernel/rustkernel/Cargo.lock
generated
@ -2,6 +2,15 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
|
||||
[[package]]
|
||||
name = "rustkernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = { version = "0.2.149", default-features = false }
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
28
kernel/rustkernel/src/kalloc.rs
Normal file
28
kernel/rustkernel/src/kalloc.rs
Normal file
@ -0,0 +1,28 @@
|
||||
extern "C" {
|
||||
fn kalloc() -> *mut u8;
|
||||
fn kfree(ptr: *mut u8);
|
||||
}
|
||||
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
struct KernelAllocator;
|
||||
|
||||
unsafe impl GlobalAlloc for KernelAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
if layout.size() > 4096 {
|
||||
panic!("can only allocate one page of memory at a time");
|
||||
}
|
||||
let ptr = kalloc();
|
||||
if ptr.is_null() {
|
||||
panic!("kernel could not allocate memory");
|
||||
}
|
||||
ptr
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
kfree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static GLOBAL: KernelAllocator = KernelAllocator;
|
@ -1,12 +1,26 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
#![allow(dead_code)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate core;
|
||||
|
||||
extern "C" {
|
||||
fn print(message: *const c_char);
|
||||
fn panic(panic_message: *const c_char) -> !;
|
||||
}
|
||||
|
||||
mod kalloc;
|
||||
pub(crate) mod param;
|
||||
pub mod proc;
|
||||
pub(crate) mod riscv;
|
||||
pub mod spinlock;
|
||||
|
||||
use core::ffi::{c_char, CStr};
|
||||
|
||||
extern "C" {
|
||||
pub fn print(message: *const c_char);
|
||||
fn panic(panic_message: *const c_char) -> !;
|
||||
}
|
||||
pub use proc::*;
|
||||
pub use spinlock::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() {
|
||||
@ -19,8 +33,11 @@ pub extern "C" fn rust_main() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[panic_handler]
|
||||
unsafe fn panic_wrapper(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||
panic(CStr::from_bytes_with_nul(b"panic from rust\0").unwrap_or_default().as_ptr())
|
||||
panic(
|
||||
CStr::from_bytes_with_nul(b"panic from rust\0")
|
||||
.unwrap_or_default()
|
||||
.as_ptr(),
|
||||
)
|
||||
}
|
||||
|
26
kernel/rustkernel/src/param.rs
Normal file
26
kernel/rustkernel/src/param.rs
Normal file
@ -0,0 +1,26 @@
|
||||
/// Maximum number of processes
|
||||
pub const NPROC: usize = 64;
|
||||
/// Maximum number of CPUs
|
||||
pub const NCPU: usize = 8;
|
||||
/// Maximum number of open files per process
|
||||
pub const NOFILE: usize = 16;
|
||||
/// Maximum number of open files per system
|
||||
pub const NFILE: usize = 100;
|
||||
/// Maximum number of active inodes
|
||||
pub const NINODE: usize = 50;
|
||||
/// Maximum major device number
|
||||
pub const NDEV: usize = 10;
|
||||
/// Device number of file system root disk
|
||||
pub const ROOTDEV: usize = 1;
|
||||
/// Max exec arguments
|
||||
pub const MAXARG: usize = 32;
|
||||
/// Max num of blocks any FS op writes
|
||||
pub const MAXOPBLOCKS: usize = 10;
|
||||
/// Max data blocks in on-disk log
|
||||
pub const LOGSIZE: usize = MAXOPBLOCKS * 3;
|
||||
/// Size of disk block cache
|
||||
pub const NBUF: usize = MAXOPBLOCKS * 3;
|
||||
/// Size of file system in blocks
|
||||
pub const FSSIZE: usize = 2000;
|
||||
/// Maximum file path size
|
||||
pub const MAXPATH: usize = 128;
|
162
kernel/rustkernel/src/proc.rs
Normal file
162
kernel/rustkernel/src/proc.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use crate::{
|
||||
riscv::{self, Pagetable},
|
||||
spinlock::Spinlock,
|
||||
};
|
||||
use core::ffi::c_char;
|
||||
|
||||
/// Saved registers for kernel context switches.
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
pub ra: u64,
|
||||
pub sp: u64,
|
||||
|
||||
// callee-saved
|
||||
pub s0: u64,
|
||||
pub s1: u64,
|
||||
pub s2: u64,
|
||||
pub s3: u64,
|
||||
pub s4: u64,
|
||||
pub s5: u64,
|
||||
pub s6: u64,
|
||||
pub s7: u64,
|
||||
pub s8: u64,
|
||||
pub s9: u64,
|
||||
pub s10: u64,
|
||||
pub s11: u64,
|
||||
}
|
||||
|
||||
/// Per-CPU state.
|
||||
#[repr(C)]
|
||||
pub struct Cpu {
|
||||
/// The process running on this cpu, or null.
|
||||
pub proc: *mut Proc,
|
||||
/// swtch() here to enter scheduler()
|
||||
pub context: Context,
|
||||
/// Depth of push_off() nesting.
|
||||
pub noff: i32,
|
||||
/// Were interrupts enabled before push_off()?
|
||||
pub intena: i32,
|
||||
}
|
||||
|
||||
/// Per-process data for the trap handling code in trampoline.S.
|
||||
///
|
||||
/// sits in a page by itself just under the trampoline page in the
|
||||
/// user page table. not specially mapped in the kernel page table.
|
||||
/// uservec in trampoline.S saves user registers in the trapframe,
|
||||
/// then initializes registers from the trapframe's
|
||||
/// kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap.
|
||||
/// usertrapret() and userret in trampoline.S set up
|
||||
/// the trapframe's kernel_*, restore user registers from the
|
||||
/// trapframe, switch to the user page table, and enter user space.
|
||||
/// the trapframe includes callee-saved user registers like s0-s11 because the
|
||||
/// return-to-user path via usertrapret() doesn't return through
|
||||
/// the entire kernel call stack.
|
||||
#[repr(C)]
|
||||
pub struct TrapFrame {
|
||||
/// Kernel page table.
|
||||
pub kernel_satp: u64,
|
||||
/// Top of process's kernel stack.
|
||||
pub kernel_sp: u64,
|
||||
/// usertrap()
|
||||
pub kernel_trap: u64,
|
||||
/// Saved user program counter.
|
||||
pub epc: u64,
|
||||
/// Saved kernel tp.
|
||||
pub kernel_hartid: u64,
|
||||
pub ra: u64,
|
||||
pub sp: u64,
|
||||
pub gp: u64,
|
||||
pub tp: u64,
|
||||
pub t0: u64,
|
||||
pub t1: u64,
|
||||
pub t2: u64,
|
||||
pub s0: u64,
|
||||
pub s1: u64,
|
||||
pub a0: u64,
|
||||
pub a1: u64,
|
||||
pub a2: u64,
|
||||
pub a3: u64,
|
||||
pub a4: u64,
|
||||
pub a5: u64,
|
||||
pub a6: u64,
|
||||
pub a7: u64,
|
||||
pub s2: u64,
|
||||
pub s3: u64,
|
||||
pub s4: u64,
|
||||
pub s5: u64,
|
||||
pub s6: u64,
|
||||
pub s7: u64,
|
||||
pub s8: u64,
|
||||
pub s9: u64,
|
||||
pub s10: u64,
|
||||
pub s11: u64,
|
||||
pub t3: u64,
|
||||
pub t4: u64,
|
||||
pub t5: u64,
|
||||
pub t6: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum ProcState {
|
||||
Unused,
|
||||
Used,
|
||||
Sleeping,
|
||||
Runnable,
|
||||
Running,
|
||||
Zombie,
|
||||
}
|
||||
|
||||
/// Per-process state.
|
||||
#[repr(C)]
|
||||
pub struct Proc {
|
||||
pub lock: Spinlock,
|
||||
|
||||
// p->lock must be held when using these:
|
||||
/// Process state
|
||||
pub state: ProcState,
|
||||
/// If non-zero, sleeping on chan
|
||||
pub chan: *mut u8,
|
||||
/// If non-zero, have been killed
|
||||
pub killed: i32,
|
||||
/// Exit status to be returned to parent's wait
|
||||
pub xstate: i32,
|
||||
/// Process ID
|
||||
pub pid: i32,
|
||||
|
||||
// wait_lock msut be held when using this:
|
||||
/// Parent process
|
||||
pub parent: *mut Proc,
|
||||
|
||||
// These are private to the process, so p->lock need not be held.
|
||||
/// Virtual address of kernel stack
|
||||
pub kstack: u64,
|
||||
/// Size of process memory (bytes)
|
||||
pub sz: u64,
|
||||
/// User page table
|
||||
pub pagetable: Pagetable,
|
||||
/// Data page for trampoline.S
|
||||
pub trapframe: *mut TrapFrame,
|
||||
/// swtch() here to run process
|
||||
pub context: Context,
|
||||
/// Open files
|
||||
pub ofile: *mut u8, // TODO: Change u8 ptr to File ptr.
|
||||
/// Current directory
|
||||
pub cwd: *mut u8, // TODO: Change u8 ptr to inode ptr.
|
||||
/// Process name (debugging)
|
||||
pub name: [c_char; 16],
|
||||
}
|
||||
|
||||
/// Must be called with interrupts disabled
|
||||
/// to prevent race with process being moved
|
||||
/// to a different CPU.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn cpuid() -> i32 {
|
||||
riscv::r_tp() as i32
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// pub fn cpuid() -> i32;
|
||||
/// Return this CPU's cpu struct.
|
||||
/// Interrupts must be disabled.
|
||||
pub fn mycpu() -> *mut Cpu;
|
||||
}
|
190
kernel/rustkernel/src/riscv.rs
Normal file
190
kernel/rustkernel/src/riscv.rs
Normal file
@ -0,0 +1,190 @@
|
||||
use core::arch::asm;
|
||||
|
||||
pub type Pte = u64;
|
||||
pub type Pagetable = *mut [Pte; 512];
|
||||
|
||||
/// Previous mode
|
||||
pub const MSTATUS_MPP_MASK: u64 = 3 << 11;
|
||||
pub const MSTATUS_MPP_M: u64 = 3 << 11;
|
||||
pub const MSTATUS_MPP_S: u64 = 1 << 11;
|
||||
pub const MSTATUS_MPP_U: u64 = 0 << 11;
|
||||
/// Machine-mode interrupt enable.
|
||||
pub const MSTATUS_MIE: u64 = 1 << 3;
|
||||
|
||||
/// Previous mode: 1 = Supervisor, 0 = User
|
||||
pub const SSTATUS_SPP: u64 = 1 << 8;
|
||||
/// Supervisor Previous Interrupt Enable
|
||||
pub const SSTATUS_SPIE: u64 = 1 << 5;
|
||||
/// User Previous Interrupt Enable
|
||||
pub const SSTATUS_UPIE: u64 = 1 << 4;
|
||||
/// Supervisor Interrupt Enable
|
||||
pub const SSTATUS_SIE: u64 = 1 << 1;
|
||||
/// User Interrupt Enable
|
||||
pub const SSTATUS_UIE: u64 = 1 << 0;
|
||||
|
||||
/// Supervisor External Interrupt Enable
|
||||
pub const SIE_SEIE: u64 = 1 << 9;
|
||||
/// Supervisor Timer Interrupt Enable
|
||||
pub const SIE_STIE: u64 = 1 << 5;
|
||||
/// Supervisor Software Interrupt Enable
|
||||
pub const SIE_SSIE: u64 = 1 << 1;
|
||||
|
||||
/// Machine-mode External Interrupt Enable
|
||||
pub const MIE_MEIE: u64 = 1 << 11;
|
||||
/// Machine-mode Timer Interrupt Enable
|
||||
pub const MIE_MTIE: u64 = 1 << 7;
|
||||
/// Machine-mode Software Interrupt Enable
|
||||
pub const MIE_MSIE: u64 = 1 << 3;
|
||||
|
||||
pub const SATP_SV39: u64 = 8 << 60;
|
||||
|
||||
/// Bytes per page
|
||||
pub const PGSIZE: u64 = 4096;
|
||||
/// Bits of offset within a page
|
||||
pub const PGSHIFT: u64 = 12;
|
||||
|
||||
pub const PTE_V: u64 = 1 << 0;
|
||||
pub const PTE_R: u64 = 1 << 1;
|
||||
pub const PTE_W: u64 = 1 << 2;
|
||||
pub const PTE_X: u64 = 1 << 3;
|
||||
pub const PTE_U: u64 = 1 << 4;
|
||||
|
||||
/// Which hart (core) is this?
|
||||
#[inline(always)]
|
||||
pub unsafe fn r_mhartid() -> u64 {
|
||||
let x: u64;
|
||||
asm!("csrr {}, mhartid", out(reg) x);
|
||||
x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn r_tp() -> u64 {
|
||||
let x: u64;
|
||||
asm!("mv {}, tp", out(reg) x);
|
||||
x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn w_sstatus(x: u64) {
|
||||
asm!("csrw sstatus, {}", in(reg) x);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn r_sstatus() -> u64 {
|
||||
let x: u64;
|
||||
asm!("csrr {}, sstatus", out(reg) x);
|
||||
x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn intr_on() {
|
||||
w_sstatus(r_sstatus() | SSTATUS_SIE);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn intr_off() {
|
||||
w_sstatus(r_sstatus() & !SSTATUS_SIE);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn intr_get() -> i32 {
|
||||
if (r_sstatus() & SSTATUS_SIE) > 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
/// Which hart (core) is this?
|
||||
pub fn rv_r_mhartid() -> u64;
|
||||
|
||||
// Machine Status Register, mstatus
|
||||
pub fn r_mstatus() -> u64;
|
||||
pub fn w_mstatus(x: u64);
|
||||
|
||||
// Machine Exception Program Counter
|
||||
// MEPC holds the instruction address to which a return from exception will go.
|
||||
pub fn w_mepc(x: u64);
|
||||
|
||||
// Supervisor Status Register, sstatus
|
||||
pub fn rv_r_sstatus() -> u64;
|
||||
pub fn rv_w_sstatus(x: u64);
|
||||
|
||||
// Supervisor Interrupt Pending
|
||||
pub fn r_sip() -> u64;
|
||||
pub fn w_sip(x: u64);
|
||||
|
||||
// Supervisor Interrupt Enable
|
||||
pub fn r_sie() -> u64;
|
||||
pub fn w_sie(x: u64);
|
||||
|
||||
// Machine-mode Interrupt Enable
|
||||
pub fn r_mie() -> u64;
|
||||
pub fn w_mie(x: u64);
|
||||
|
||||
// Supervisor Exception Program Counter
|
||||
// SEPC holds the instruction address to which a return from exception will go.
|
||||
pub fn r_sepc() -> u64;
|
||||
pub fn w_sepc(x: u64);
|
||||
|
||||
// Machine Exception Deletgation
|
||||
pub fn r_medeleg() -> u64;
|
||||
pub fn w_medeleg(x: u64);
|
||||
|
||||
// Machine Interrupt Deletgation
|
||||
pub fn r_mideleg() -> u64;
|
||||
pub fn w_mideleg(x: u64);
|
||||
|
||||
// Supervisor Trap-Vector Base Address
|
||||
pub fn r_stvec() -> u64;
|
||||
pub fn w_stvec(x: u64);
|
||||
|
||||
// Machine-mode Interrupt Vector
|
||||
pub fn w_mtvec(x: u64);
|
||||
|
||||
// Physical Memory Protection
|
||||
pub fn w_pmpcfg0(x: u64);
|
||||
pub fn w_pmpaddr0(x: u64);
|
||||
|
||||
// Supervisor Address Translation and Protection
|
||||
// SATP holds the address of the page table.
|
||||
pub fn r_satp() -> u64;
|
||||
pub fn w_satp(x: u64);
|
||||
|
||||
pub fn w_mscratch(x: u64);
|
||||
|
||||
// Supervisor Trap Cause
|
||||
pub fn r_scause() -> u64;
|
||||
|
||||
// Supervisor Trap Value
|
||||
pub fn r_stval() -> u64;
|
||||
|
||||
// Machine-mode Counter-Enable
|
||||
pub fn r_mcounteren() -> u64;
|
||||
pub fn w_mcounteren(x: u64);
|
||||
|
||||
// Machine-mode cycle counter
|
||||
pub fn r_time() -> u64;
|
||||
|
||||
// /// Enable device interrupts
|
||||
// pub fn intr_on();
|
||||
|
||||
// /// Disable device interrupts
|
||||
// pub fn intr_off();
|
||||
|
||||
// // Are device interrupts enabled?
|
||||
// pub fn intr_get() -> i32;
|
||||
|
||||
pub fn r_sp() -> u64;
|
||||
|
||||
// Read and write TP (thread pointer), which xv6 uses
|
||||
// to hold this core's hartid, the index into cpus[].
|
||||
// pub fn rv_r_tp() -> u64;
|
||||
pub fn w_tp(x: u64);
|
||||
|
||||
pub fn r_ra() -> u64;
|
||||
|
||||
/// Flush the TLB.
|
||||
pub fn sfence_vma();
|
||||
}
|
110
kernel/rustkernel/src/spinlock.rs
Normal file
110
kernel/rustkernel/src/spinlock.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::{
|
||||
proc::{mycpu, Cpu},
|
||||
riscv,
|
||||
};
|
||||
use core::{
|
||||
ffi::c_char,
|
||||
ptr::null_mut,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Spinlock {
|
||||
pub locked: AtomicBool,
|
||||
pub name: *mut c_char,
|
||||
pub cpu: *mut Cpu,
|
||||
}
|
||||
impl Spinlock {
|
||||
/// Initializes a `Spinlock`.
|
||||
pub fn new(name: *mut c_char) -> Spinlock {
|
||||
Spinlock {
|
||||
locked: AtomicBool::new(false),
|
||||
cpu: null_mut(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
/// Check whether this cpu is holding the lock.
|
||||
///
|
||||
/// Interrupts must be off.
|
||||
pub fn held_by_current_cpu(&self) -> bool {
|
||||
self.cpu == unsafe { mycpu() } && self.locked.load(Ordering::Relaxed)
|
||||
}
|
||||
pub unsafe fn lock(&mut self) {
|
||||
push_off();
|
||||
|
||||
if self.held_by_current_cpu() {
|
||||
panic!("Attempt to acquire twice by the same CPU");
|
||||
}
|
||||
|
||||
while self.locked.swap(true, Ordering::Acquire) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
// The lock is now locked and we can write our CPU info.
|
||||
|
||||
self.cpu = mycpu();
|
||||
}
|
||||
pub unsafe fn unlock(&mut self) {
|
||||
if !self.held_by_current_cpu() {
|
||||
panic!("Attempt to release lock from different CPU");
|
||||
}
|
||||
|
||||
self.cpu = null_mut();
|
||||
|
||||
self.locked.store(false, Ordering::Release);
|
||||
|
||||
pop_off();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn initlock(lock: *mut Spinlock, name: *mut c_char) {
|
||||
*lock = Spinlock::new(name);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn holding(lock: *mut Spinlock) -> i32 {
|
||||
(*lock).held_by_current_cpu().into()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn acquire(lock: *mut Spinlock) {
|
||||
(*lock).lock();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn release(lock: *mut Spinlock) {
|
||||
(*lock).unlock();
|
||||
}
|
||||
|
||||
// push_off/pop_off are like intr_off()/intr_on() except that they are matched:
|
||||
// it takes two pop_off()s to undo two push_off()s. Also, if interrupts
|
||||
// are initially off, then push_off, pop_off leaves them off.
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn push_off() {
|
||||
let old = riscv::intr_get();
|
||||
let cpu = mycpu();
|
||||
|
||||
riscv::intr_off();
|
||||
if (*cpu).noff == 0 {
|
||||
(*cpu).intena = old;
|
||||
}
|
||||
(*cpu).noff += 1;
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pop_off() {
|
||||
let cpu = mycpu();
|
||||
|
||||
if riscv::intr_get() == 1 {
|
||||
panic!("pop_off - interruptible");
|
||||
} else if (*cpu).noff < 1 {
|
||||
panic!("pop_off");
|
||||
}
|
||||
|
||||
(*cpu).noff -= 1;
|
||||
|
||||
if (*cpu).noff == 0 && (*cpu).intena == 1 {
|
||||
riscv::intr_on();
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
// Mutual exclusion spin locks.
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "spinlock.h"
|
||||
#include "riscv.h"
|
||||
#include "proc.h"
|
||||
#include "defs.h"
|
||||
|
||||
void
|
||||
initlock(struct spinlock *lk, char *name)
|
||||
{
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->cpu = 0;
|
||||
}
|
||||
|
||||
// Acquire the lock.
|
||||
// Loops (spins) until the lock is acquired.
|
||||
void
|
||||
acquire(struct spinlock *lk)
|
||||
{
|
||||
push_off(); // disable interrupts to avoid deadlock.
|
||||
if(holding(lk))
|
||||
panic("acquire");
|
||||
|
||||
// On RISC-V, sync_lock_test_and_set turns into an atomic swap:
|
||||
// a5 = 1
|
||||
// s1 = &lk->locked
|
||||
// amoswap.w.aq a5, a5, (s1)
|
||||
while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
|
||||
;
|
||||
|
||||
// Tell the C compiler and the processor to not move loads or stores
|
||||
// past this point, to ensure that the critical section's memory
|
||||
// references happen strictly after the lock is acquired.
|
||||
// On RISC-V, this emits a fence instruction.
|
||||
__sync_synchronize();
|
||||
|
||||
// Record info about lock acquisition for holding() and debugging.
|
||||
lk->cpu = mycpu();
|
||||
}
|
||||
|
||||
// Release the lock.
|
||||
void
|
||||
release(struct spinlock *lk)
|
||||
{
|
||||
if(!holding(lk))
|
||||
panic("release");
|
||||
|
||||
lk->cpu = 0;
|
||||
|
||||
// Tell the C compiler and the CPU to not move loads or stores
|
||||
// past this point, to ensure that all the stores in the critical
|
||||
// section are visible to other CPUs before the lock is released,
|
||||
// and that loads in the critical section occur strictly before
|
||||
// the lock is released.
|
||||
// On RISC-V, this emits a fence instruction.
|
||||
__sync_synchronize();
|
||||
|
||||
// Release the lock, equivalent to lk->locked = 0.
|
||||
// This code doesn't use a C assignment, since the C standard
|
||||
// implies that an assignment might be implemented with
|
||||
// multiple store instructions.
|
||||
// On RISC-V, sync_lock_release turns into an atomic swap:
|
||||
// s1 = &lk->locked
|
||||
// amoswap.w zero, zero, (s1)
|
||||
__sync_lock_release(&lk->locked);
|
||||
|
||||
pop_off();
|
||||
}
|
||||
|
||||
// Check whether this cpu is holding the lock.
|
||||
// Interrupts must be off.
|
||||
int
|
||||
holding(struct spinlock *lk)
|
||||
{
|
||||
int r;
|
||||
r = (lk->locked && lk->cpu == mycpu());
|
||||
return r;
|
||||
}
|
||||
|
||||
// push_off/pop_off are like intr_off()/intr_on() except that they are matched:
|
||||
// it takes two pop_off()s to undo two push_off()s. Also, if interrupts
|
||||
// are initially off, then push_off, pop_off leaves them off.
|
||||
|
||||
void
|
||||
push_off(void)
|
||||
{
|
||||
int old = intr_get();
|
||||
|
||||
intr_off();
|
||||
if(mycpu()->noff == 0)
|
||||
mycpu()->intena = old;
|
||||
mycpu()->noff += 1;
|
||||
}
|
||||
|
||||
void
|
||||
pop_off(void)
|
||||
{
|
||||
struct cpu *c = mycpu();
|
||||
if(intr_get())
|
||||
panic("pop_off - interruptible");
|
||||
if(c->noff < 1)
|
||||
panic("pop_off");
|
||||
c->noff -= 1;
|
||||
if(c->noff == 0 && c->intena)
|
||||
intr_on();
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned char uchar;
|
||||
|
Loading…
x
Reference in New Issue
Block a user