diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs index c7e5230..2444ad4 100644 --- a/src/arch/x86_64/idt.rs +++ b/src/arch/x86_64/idt.rs @@ -27,6 +27,7 @@ lazy_static! { let mut opt = entries[i].set_handler_fn(unsafe { transmute(__vectors[i]) }); if ring3.contains(&(i as u8)) { opt.set_privilege_level(PrivilegeLevel::Ring3); + opt.disable_interrupts(false); } if i == T_DBLFLT as usize { unsafe{ opt.set_stack_index(DOUBLE_FAULT_IST_INDEX as u16); } diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs index e94ee18..a1b9ecb 100644 --- a/src/arch/x86_64/interrupt/mod.rs +++ b/src/arch/x86_64/interrupt/mod.rs @@ -18,6 +18,25 @@ pub unsafe fn disable() { x86_64::instructions::interrupts::disable(); } +#[inline(always)] +pub unsafe fn disable_and_store() -> usize { + let r: usize; + asm!("pushfq; popq $0; cli" : "=r"(r) :: "memory"); + r +} + +#[inline(always)] +pub unsafe fn restore(flags: usize) { + asm!("pushq $0; popfq" :: "r"(flags) : "memory" "flags"); +} + +#[inline(always)] +pub fn no_interrupt(f: impl FnOnce()) { + let flags = unsafe { disable_and_store() }; + f(); + unsafe { restore(flags) }; +} + #[inline(always)] pub fn enable_irq(irq: u8) { if cfg!(feature = "use_apic") { diff --git a/src/lib.rs b/src/lib.rs index 75bdc7e..411a090 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ mod consts; mod process; mod syscall; mod fs; +mod sync; #[allow(dead_code)] #[cfg(target_arch = "x86_64")] diff --git a/src/macros.rs b/src/macros.rs index 3cfea74..31049c5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,12 +23,3 @@ macro_rules! test { } ) } - -macro_rules! no_interrupt { - {$func:block} => { - use arch::interrupt; - unsafe{ interrupt::disable(); } - $func; - unsafe{ interrupt::enable(); } - }; -} \ No newline at end of file diff --git a/src/process/mod.rs b/src/process/mod.rs index f2e5855..078ec0d 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -1,5 +1,6 @@ use memory::MemoryController; -use spin::{Once, Mutex}; +use spin::Once; +use sync::Mutex; use core::slice; use alloc::String; diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..9eda90e --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,167 @@ +//! Spin & no-interrupt lock +//! +//! Modified from spin::mutex. +//! Search 'interrupt::' for difference. + +use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use core::fmt; +use arch::interrupt; + +/// Spin & no-interrupt lock +pub struct Mutex +{ + lock: AtomicBool, + data: UnsafeCell, +} + +/// A guard to which the protected data can be accessed +/// +/// When the guard falls out of scope it will release the lock. +pub struct MutexGuard<'a, T: ?Sized + 'a> +{ + lock: &'a AtomicBool, + data: &'a mut T, + flags: usize, +} + +// Same unsafe impls as `std::sync::Mutex` +unsafe impl Sync for Mutex {} + +unsafe impl Send for Mutex {} + +impl Mutex +{ + /// Creates a new spinlock wrapping the supplied data. + /// + /// May be used statically: + /// + /// ``` + /// #![feature(const_fn)] + /// use spin; + /// + /// static MUTEX: spin::Mutex<()> = spin::Mutex::new(()); + /// + /// fn demo() { + /// let lock = MUTEX.lock(); + /// // do something with lock + /// drop(lock); + /// } + /// ``` + pub const fn new(user_data: T) -> Mutex { + Mutex { + lock: ATOMIC_BOOL_INIT, + data: UnsafeCell::new(user_data), + } + } + + /// Consumes this mutex, returning the underlying data. + pub fn into_inner(self) -> T { + // We know statically that there are no outstanding references to + // `self` so there's no need to lock. + let Mutex { data, .. } = self; + unsafe { data.into_inner() } + } +} + +impl Mutex +{ + fn obtain_lock(&self) { + while self.lock.compare_and_swap(false, true, Ordering::Acquire) != false { + // Wait until the lock looks unlocked before retrying + while self.lock.load(Ordering::Relaxed) { + unsafe { asm!("pause" :::: "volatile"); } + } + } + } + + /// Locks the spinlock and returns a guard. + /// + /// The returned value may be dereferenced for data access + /// and the lock will be dropped when the guard falls out of scope. + /// + /// ``` + /// let mylock = spin::Mutex::new(0); + /// { + /// let mut data = mylock.lock(); + /// // The lock is now locked and the data can be accessed + /// *data += 1; + /// // The lock is implicitly dropped + /// } + /// + /// ``` + pub fn lock(&self) -> MutexGuard + { + let flags = unsafe { interrupt::disable_and_store() }; + self.obtain_lock(); + MutexGuard { + lock: &self.lock, + data: unsafe { &mut *self.data.get() }, + flags, + } + } + + /// Force unlock the spinlock. + /// + /// This is *extremely* unsafe if the lock is not held by the current + /// thread. However, this can be useful in some instances for exposing the + /// lock to FFI that doesn't know how to deal with RAII. + /// + /// If the lock isn't held, this is a no-op. + pub unsafe fn force_unlock(&self) { + self.lock.store(false, Ordering::Release); + } + + /// Tries to lock the mutex. If it is already locked, it will return None. Otherwise it returns + /// a guard within Some. + pub fn try_lock(&self) -> Option> { + let flags = unsafe { interrupt::disable_and_store() }; + if self.lock.compare_and_swap(false, true, Ordering::Acquire) == false { + Some(MutexGuard { + lock: &self.lock, + data: unsafe { &mut *self.data.get() }, + flags, + }) + } else { + unsafe { interrupt::restore(flags) }; + None + } + } +} + +impl fmt::Debug for Mutex +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.try_lock() { + Some(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard), + None => write!(f, "Mutex {{ }}"), + } + } +} + +impl Default for Mutex { + fn default() -> Mutex { + Mutex::new(Default::default()) + } +} + +impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> +{ + type Target = T; + fn deref<'b>(&'b self) -> &'b T { &*self.data } +} + +impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> +{ + fn deref_mut<'b>(&'b mut self) -> &'b mut T { &mut *self.data } +} + +impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> +{ + /// The dropping of the MutexGuard will release the lock it was created from. + fn drop(&mut self) { + self.lock.store(false, Ordering::Release); + unsafe { interrupt::restore(self.flags) }; + } +} \ No newline at end of file