feat: gicv2 and basic irq handling
This commit is contained in:
parent
ea7eb300f7
commit
58ee697a8c
2
Makefile
2
Makefile
@ -57,7 +57,7 @@ clean:
|
||||
cargo clean
|
||||
|
||||
qemu: all
|
||||
qemu-system-$(ARCH) $(QEMU_OPTS)
|
||||
$(QEMU_PREFIX)qemu-system-$(ARCH) $(QEMU_OPTS)
|
||||
|
||||
gdb: all
|
||||
$(GDB) -x etc/gdbrc $(O)/kernel
|
||||
|
@ -7,4 +7,5 @@ pub enum Errno {
|
||||
NotADirectory,
|
||||
OutOfMemory,
|
||||
WouldBlock,
|
||||
AlreadyExists,
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
//! aarch64 common boot logic
|
||||
|
||||
use crate::arch::{aarch64::asm::CPACR_EL1, machine};
|
||||
use crate::dev::{Device, serial::SerialDevice, timer::TimestampSource};
|
||||
use crate::dev::Device;
|
||||
use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use cortex_a::registers::{SCTLR_EL1, VBAR_EL1};
|
||||
use cortex_a::registers::{DAIF, SCTLR_EL1, VBAR_EL1};
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
|
||||
#[no_mangle]
|
||||
@ -29,17 +29,12 @@ fn __aa64_bsp_main() {
|
||||
}
|
||||
|
||||
machine::init_board().unwrap();
|
||||
|
||||
unsafe {
|
||||
machine::local_timer().lock().enable().unwrap();
|
||||
machine::local_timer().enable().unwrap();
|
||||
}
|
||||
|
||||
let base = machine::local_timer().lock().timestamp().unwrap();
|
||||
|
||||
loop {
|
||||
let count = machine::local_timer().lock().timestamp().unwrap();
|
||||
let ch = machine::console().lock().recv(true).unwrap();
|
||||
debugln!("[{:?}] {:#04x} = '{}'!", count - base, ch, ch as char);
|
||||
DAIF.modify(DAIF::I::CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
//! AArch64 exception handling
|
||||
|
||||
use crate::arch::machine;
|
||||
use crate::dev::irq::{IntController, IrqContext};
|
||||
|
||||
/// Trapped SIMD/FP functionality
|
||||
pub const EC_FP_TRAP: u64 = 0b000111;
|
||||
/// Data Abort at current EL
|
||||
@ -55,7 +58,15 @@ const fn data_abort_access_size(iss: u64) -> &'static str {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_exc_handler(exc: &mut ExceptionFrame) {
|
||||
extern "C" fn __aa64_exc_irq_handler() {
|
||||
unsafe {
|
||||
let ic = IrqContext::new();
|
||||
machine::intc().handle_pending_irqs(&ic);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
let err_code = exc.esr >> 26;
|
||||
let iss = exc.esr & 0x1FFFFFF;
|
||||
|
||||
|
59
kernel/src/arch/aarch64/irq/gic/gicc.rs
Normal file
59
kernel/src/arch/aarch64/irq/gic/gicc.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::dev::irq::IrqContext;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::ReadWrite;
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CTLR [
|
||||
Enable OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
PMR [
|
||||
Priority OFFSET(0) NUMBITS(8) []
|
||||
],
|
||||
IAR [
|
||||
InterruptID OFFSET(0) NUMBITS(10) []
|
||||
],
|
||||
EOIR [
|
||||
EOINTID OFFSET(0) NUMBITS(10) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
GiccRegs {
|
||||
(0x00 => CTLR: ReadWrite<u32, CTLR::Register>),
|
||||
(0x04 => PMR: ReadWrite<u32, PMR::Register>),
|
||||
(0x08 => _res0),
|
||||
(0x0C => IAR: ReadWrite<u32, IAR::Register>),
|
||||
(0x10 => EOIR: ReadWrite<u32, EOIR::Register>),
|
||||
(0x14 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Gicc {
|
||||
regs: MemoryIo<GiccRegs>,
|
||||
}
|
||||
|
||||
impl Gicc {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
regs: MemoryIo::new(base),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn enable(&self) {
|
||||
debugln!("Enable GICC");
|
||||
self.regs.CTLR.write(CTLR::Enable::SET);
|
||||
self.regs.PMR.write(PMR::Priority.val(0xFF));
|
||||
}
|
||||
|
||||
pub fn pending_irq_number<'q>(&'q self, _ic: &IrqContext<'q>) -> usize {
|
||||
self.regs.IAR.read(IAR::InterruptID) as usize
|
||||
}
|
||||
|
||||
pub fn clear_irq<'q>(&'q self, irq: u32, _ic: &IrqContext<'q>) {
|
||||
self.regs.EOIR.write(EOIR::EOINTID.val(irq));
|
||||
}
|
||||
}
|
125
kernel/src/arch/aarch64/irq/gic/gicd.rs
Normal file
125
kernel/src/arch/aarch64/irq/gic/gicd.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CTLR [
|
||||
Enable OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
TYPER [
|
||||
ITLinesNumber OFFSET(0) NUMBITS(5) []
|
||||
],
|
||||
ITARGETSR [
|
||||
Offset3 OFFSET(24) NUMBITS(8) [],
|
||||
Offset2 OFFSET(16) NUMBITS(8) [],
|
||||
Offset1 OFFSET(8) NUMBITS(8) [],
|
||||
Offset0 OFFSET(0) NUMBITS(8) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
GicdSharedRegs {
|
||||
(0x000 => CTLR: ReadWrite<u32, CTLR::Register>),
|
||||
(0x004 => TYPER: ReadWrite<u32, TYPER::Register>),
|
||||
(0x008 => _res0),
|
||||
(0x104 => ISENABLER: [ReadWrite<u32>; 31]),
|
||||
(0x180 => _res1),
|
||||
(0x820 => ITARGETSR: [ReadWrite<u32, ITARGETSR::Register>; 248]),
|
||||
(0xC00 => _res2),
|
||||
(0xC08 => ICFGR: [ReadWrite<u32>; 62]),
|
||||
(0xC0C => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
GicdBankedRegs {
|
||||
(0x000 => _res0),
|
||||
(0x100 => ISENABLER: ReadWrite<u32>),
|
||||
(0x104 => _res1),
|
||||
(0x800 => ITARGETSR: [ReadOnly<u32, ITARGETSR::Register>; 8]),
|
||||
(0x804 => _res2),
|
||||
(0xC00 => ICFGR: [ReadWrite<u32>; 2]),
|
||||
(0xC04 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl GicdSharedRegs {
|
||||
#[inline(always)]
|
||||
fn num_irqs(&self) -> usize {
|
||||
((self.TYPER.read(TYPER::ITLinesNumber) as usize) + 1) * 32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn itargets_slice(&self) -> &[ReadWrite<u32, ITARGETSR::Register>] {
|
||||
assert!(self.num_irqs() >= 36);
|
||||
let itargetsr_max_index = ((self.num_irqs() - 32) >> 2) - 1;
|
||||
&self.ITARGETSR[0..itargetsr_max_index]
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Gicd {
|
||||
shared_regs: IrqSafeNullLock<MemoryIo<GicdSharedRegs>>,
|
||||
banked_regs: MemoryIo<GicdBankedRegs>,
|
||||
}
|
||||
|
||||
impl Gicd {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
shared_regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
banked_regs: MemoryIo::new(base),
|
||||
}
|
||||
}
|
||||
|
||||
fn local_gic_target_mask(&self) -> u32 {
|
||||
self.banked_regs.ITARGETSR[0].read(ITARGETSR::Offset0)
|
||||
}
|
||||
|
||||
fn enable_irq_inner(&self, irq: usize) {
|
||||
let reg = irq >> 5;
|
||||
let bit = 1u32 << (irq & 0x1F);
|
||||
|
||||
match reg {
|
||||
// Private
|
||||
0 => {
|
||||
let reg = &self.banked_regs.ISENABLER;
|
||||
|
||||
reg.set(reg.get() | bit);
|
||||
}
|
||||
// Shared
|
||||
_ => {
|
||||
let regs = self.shared_regs.lock();
|
||||
let reg = ®s.ISENABLER[reg - 1];
|
||||
|
||||
reg.set(reg.get() | bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_irq(&self, irq: super::IrqNumber) {
|
||||
let irq = irq.get();
|
||||
|
||||
self.enable_irq_inner(irq);
|
||||
}
|
||||
|
||||
pub unsafe fn enable(&self) {
|
||||
let mask = self.local_gic_target_mask();
|
||||
let regs = self.shared_regs.lock();
|
||||
debugln!("Enable GICD, max IRQ number: {}", regs.num_irqs());
|
||||
|
||||
regs.CTLR.write(CTLR::Enable::SET);
|
||||
|
||||
for reg in regs.itargets_slice().iter() {
|
||||
reg.write(
|
||||
ITARGETSR::Offset0.val(mask)
|
||||
+ ITARGETSR::Offset1.val(mask)
|
||||
+ ITARGETSR::Offset2.val(mask)
|
||||
+ ITARGETSR::Offset3.val(mask),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
109
kernel/src/arch/aarch64/irq/gic/mod.rs
Normal file
109
kernel/src/arch/aarch64/irq/gic/mod.rs
Normal file
@ -0,0 +1,109 @@
|
||||
//! ARM Generic Interrupt Controller
|
||||
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource, IrqContext},
|
||||
Device,
|
||||
};
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use error::Errno;
|
||||
|
||||
mod gicc;
|
||||
use gicc::Gicc;
|
||||
mod gicd;
|
||||
use gicd::Gicd;
|
||||
|
||||
/// Maximum available IRQ number
|
||||
pub const MAX_IRQ: usize = 300;
|
||||
|
||||
/// Range-checked IRQ number type
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct IrqNumber(usize);
|
||||
|
||||
/// ARM Generic Interrupt Controller
|
||||
pub struct Gic {
|
||||
gicc: Gicc,
|
||||
gicd: Gicd,
|
||||
table: IrqSafeNullLock<[Option<&'static (dyn IntSource + Sync)>; MAX_IRQ]>,
|
||||
}
|
||||
|
||||
impl IrqNumber {
|
||||
/// Returns numeric representation for given [IrqNumber]
|
||||
#[inline(always)]
|
||||
pub const fn get(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
///
|
||||
#[inline(always)]
|
||||
pub const fn new(v: usize) -> Self {
|
||||
assert!(v < MAX_IRQ);
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Gic {
|
||||
fn name(&self) -> &'static str {
|
||||
"ARM Generic Interrupt Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.gicd.enable();
|
||||
self.gicc.enable();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntController for Gic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno> {
|
||||
self.gicd.enable_irq(irq);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
|
||||
let irq_number = self.gicc.pending_irq_number(ic);
|
||||
if irq_number >= MAX_IRQ {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let table = self.table.lock();
|
||||
match table[irq_number] {
|
||||
None => panic!("No handler registered for irq{}", irq_number),
|
||||
Some(handler) => handler.handle_irq().expect("irq handler failed"),
|
||||
}
|
||||
}
|
||||
|
||||
self.gicc.clear_irq(irq_number as u32, ic);
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno> {
|
||||
let mut table = self.table.lock();
|
||||
let irq = irq.get();
|
||||
if table[irq].is_some() {
|
||||
return Err(Errno::AlreadyExists);
|
||||
}
|
||||
|
||||
debugln!("Bound irq{} to {:?}", irq, Device::name(handler));
|
||||
table[irq] = Some(handler);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Gic {
|
||||
///
|
||||
pub const unsafe fn new(gicd_base: usize, gicc_base: usize) -> Self {
|
||||
Self {
|
||||
gicc: Gicc::new(gicc_base),
|
||||
gicd: Gicd::new(gicd_base),
|
||||
table: IrqSafeNullLock::new([None; MAX_IRQ]),
|
||||
}
|
||||
}
|
||||
}
|
3
kernel/src/arch/aarch64/irq/mod.rs
Normal file
3
kernel/src/arch/aarch64/irq/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! AArch64-specific IRQ handling functionality
|
||||
|
||||
pub mod gic;
|
@ -27,17 +27,17 @@ pub(super) struct Gpio {
|
||||
}
|
||||
|
||||
impl Device for Gpio {
|
||||
fn name() -> &'static str {
|
||||
fn name(&self) -> &'static str {
|
||||
"Allwinner H6 GPIO Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno> {
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioDevice for Gpio {
|
||||
unsafe fn set_pin_config(&mut self, pin: u32, cfg: &PinConfig) -> Result<(), Errno> {
|
||||
unsafe fn set_pin_config(&self, pin: u32, cfg: &PinConfig) -> Result<(), Errno> {
|
||||
let pull = match cfg.pull {
|
||||
PullMode::None => 0,
|
||||
PullMode::Up => 1,
|
||||
@ -65,30 +65,30 @@ impl GpioDevice for Gpio {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn get_pin_config(&mut self, _pin: u32) -> Result<PinConfig, Errno> {
|
||||
unsafe fn get_pin_config(&self, _pin: u32) -> Result<PinConfig, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_pin(&mut self, pin: u32) {
|
||||
fn set_pin(&self, pin: u32) {
|
||||
self.regs.DAT.set(self.regs.DAT.get() | (1 << pin));
|
||||
}
|
||||
|
||||
fn clear_pin(&mut self, pin: u32) {
|
||||
fn clear_pin(&self, pin: u32) {
|
||||
self.regs.DAT.set(self.regs.DAT.get() & !(1 << pin));
|
||||
}
|
||||
|
||||
fn toggle_pin(&mut self, pin: u32) {
|
||||
fn toggle_pin(&self, pin: u32) {
|
||||
self.regs.DAT.set(self.regs.DAT.get() ^ (1 << pin));
|
||||
}
|
||||
|
||||
fn read_pin(&mut self, pin: u32) -> Result<bool, Errno> {
|
||||
fn read_pin(&self, pin: u32) -> Result<bool, Errno> {
|
||||
Ok(self.regs.DAT.get() & (1 << pin) != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Gpio {
|
||||
#[inline]
|
||||
fn set_pin_cfg_inner(&mut self, pin: u32, cfg: u32) {
|
||||
fn set_pin_cfg_inner(&self, pin: u32, cfg: u32) {
|
||||
let reg = pin >> 3;
|
||||
let shift = (pin & 0x7) * 4;
|
||||
let tmp = self.regs.CFG[reg as usize].get() & !(0xF << shift);
|
||||
@ -96,7 +96,7 @@ impl Gpio {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_pin_pul_inner(&mut self, pin: u32, pul: u32) {
|
||||
fn set_pin_pul_inner(&self, pin: u32, pul: u32) {
|
||||
let reg = pin >> 4;
|
||||
let shift = (pin & 0xF) * 2;
|
||||
let tmp = self.regs.PUL[reg as usize].get() & !(0x3 << shift);
|
||||
|
@ -1,50 +1,65 @@
|
||||
//! Xunlong Orange Pi 3, with Allwinner H6 SoC
|
||||
|
||||
use crate::arch::aarch64::timer::GenericTimer;
|
||||
use crate::arch::aarch64::{
|
||||
irq::gic::{self, Gic},
|
||||
timer::GenericTimer,
|
||||
};
|
||||
use crate::dev::{
|
||||
gpio::{GpioDevice, PinConfig},
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
timer::TimestampSource,
|
||||
Device,
|
||||
};
|
||||
use crate::sync::Spin;
|
||||
use error::Errno;
|
||||
|
||||
mod gpio;
|
||||
mod uart;
|
||||
|
||||
pub use gic::IrqNumber;
|
||||
use gpio::Gpio;
|
||||
use uart::Uart;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
let mut gpioh = GPIOH.lock();
|
||||
gpioh.set_pin_config(0, &PinConfig::alt(gpio::PH0_UART0_TX))?;
|
||||
gpioh.set_pin_config(1, &PinConfig::alt(gpio::PH1_UART0_RX))?;
|
||||
GIC.enable()?;
|
||||
|
||||
UART0.lock().enable()?;
|
||||
GPIOH.set_pin_config(0, &PinConfig::alt(gpio::PH0_UART0_TX))?;
|
||||
GPIOH.set_pin_config(1, &PinConfig::alt(gpio::PH1_UART0_RX))?;
|
||||
|
||||
UART0.enable()?;
|
||||
UART0.init_irqs()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const UART0_BASE: usize = 0x05000000;
|
||||
const PIO_BASE: usize = 0x0300B000;
|
||||
const GICD_BASE: usize = 0x03021000;
|
||||
const GICC_BASE: usize = 0x03022000;
|
||||
|
||||
/// Returns primary console for this machine
|
||||
#[inline]
|
||||
pub fn console() -> &'static Spin<impl SerialDevice> {
|
||||
pub fn console() -> &'static impl SerialDevice {
|
||||
&UART0
|
||||
}
|
||||
|
||||
/// Returns the timer used as CPU-local periodic IRQ source
|
||||
#[inline]
|
||||
pub fn local_timer() -> &'static Spin<impl TimestampSource> {
|
||||
pub fn local_timer() -> &'static impl TimestampSource {
|
||||
&LOCAL_TIMER
|
||||
}
|
||||
|
||||
static UART0: Spin<Uart> = Spin::new(unsafe { Uart::new(UART0_BASE) });
|
||||
static LOCAL_TIMER: Spin<GenericTimer> = Spin::new(GenericTimer {});
|
||||
///
|
||||
#[inline]
|
||||
pub fn intc() -> &'static impl IntController<IrqNumber = IrqNumber> {
|
||||
&GIC
|
||||
}
|
||||
|
||||
static UART0: Uart = unsafe { Uart::new(UART0_BASE, IrqNumber::new(32)) };
|
||||
static LOCAL_TIMER: GenericTimer = GenericTimer {};
|
||||
#[allow(dead_code)]
|
||||
static GPIOD: Spin<Gpio> = Spin::new(unsafe { Gpio::new(PIO_BASE + 0x24 * 3) });
|
||||
static GPIOH: Spin<Gpio> = Spin::new(unsafe { Gpio::new(PIO_BASE + 0x24 * 7) });
|
||||
static GPIOD: Gpio = unsafe { Gpio::new(PIO_BASE + 0x24 * 3) };
|
||||
static GPIOH: Gpio = unsafe { Gpio::new(PIO_BASE + 0x24 * 7) };
|
||||
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
|
||||
|
@ -1,8 +1,16 @@
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::dev::{serial::SerialDevice, Device};
|
||||
use crate::arch::{
|
||||
machine::{self, IrqNumber},
|
||||
MemoryIo,
|
||||
};
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
use error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable, ReadWriteable};
|
||||
use tock_registers::registers::{Aliased, ReadOnly, ReadWrite};
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! [
|
||||
@ -66,41 +74,62 @@ register_structs! {
|
||||
}
|
||||
|
||||
pub(super) struct Uart {
|
||||
regs: MemoryIo<Regs>,
|
||||
regs: IrqSafeNullLock<MemoryIo<Regs>>,
|
||||
irq: IrqNumber
|
||||
}
|
||||
|
||||
impl Device for Uart {
|
||||
fn name() -> &'static str {
|
||||
fn name(&self) -> &'static str {
|
||||
"Allwinner H6 UART"
|
||||
}
|
||||
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno> {
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Uart {
|
||||
fn send(&mut self, byte: u8) -> Result<(), Errno> {
|
||||
while !self.regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
fn send(&self, byte: u8) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
while !regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
self.regs.DR_DLL.set(byte as u32);
|
||||
regs.DR_DLL.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&mut self, _blocking: bool) -> Result<u8, Errno> {
|
||||
while !self.regs.LSR.matches_all(LSR::DR::SET) {
|
||||
fn recv(&self, _blocking: bool) -> Result<u8, Errno> {
|
||||
let regs = self.regs.lock();
|
||||
while !regs.LSR.matches_all(LSR::DR::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
Ok(self.regs.DR_DLL.get() as u8)
|
||||
Ok(regs.DR_DLL.get() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSource for Uart {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
let byte = regs.DR_DLL.get();
|
||||
debugln!("irq byte = {:#04x}!", byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.regs.lock().IER_DLH.modify(IER::ERBFI::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Uart {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: MemoryIo::new(base),
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
irq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,52 @@
|
||||
//! QEMU virt machine
|
||||
|
||||
use crate::arch::aarch64::timer::GenericTimer;
|
||||
use crate::dev::{Device, serial::{pl011::Pl011, SerialDevice}};
|
||||
use crate::arch::aarch64::{
|
||||
irq::gic::{self, Gic},
|
||||
timer::GenericTimer,
|
||||
};
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::sync::Spin;
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
};
|
||||
use error::Errno;
|
||||
|
||||
pub use gic::IrqNumber;
|
||||
|
||||
const UART0_BASE: usize = 0x09000000;
|
||||
const GICD_BASE: usize = 0x08000000;
|
||||
const GICC_BASE: usize = 0x08010000;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
UART0.lock().enable()?;
|
||||
GIC.enable()?;
|
||||
|
||||
UART0.enable()?;
|
||||
UART0.init_irqs()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns primary console for this machine
|
||||
#[inline]
|
||||
pub fn console() -> &'static Spin<impl SerialDevice> {
|
||||
pub fn console() -> &'static impl SerialDevice {
|
||||
&UART0
|
||||
}
|
||||
|
||||
/// Returns the timer used as CPU-local periodic IRQ source
|
||||
#[inline]
|
||||
pub fn local_timer() -> &'static Spin<impl TimestampSource> {
|
||||
pub fn local_timer() -> &'static impl TimestampSource {
|
||||
&LOCAL_TIMER
|
||||
}
|
||||
|
||||
static UART0: Spin<Pl011> = Spin::new(unsafe { Pl011::new(UART0_BASE) });
|
||||
static LOCAL_TIMER: Spin<GenericTimer> = Spin::new(GenericTimer {});
|
||||
///
|
||||
#[inline]
|
||||
pub fn intc() -> &'static impl IntController<IrqNumber = IrqNumber> {
|
||||
&GIC
|
||||
}
|
||||
|
||||
static UART0: Pl011 = unsafe { Pl011::new(UART0_BASE, IrqNumber::new(33)) };
|
||||
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
|
||||
static LOCAL_TIMER: GenericTimer = GenericTimer {};
|
||||
|
@ -1,9 +1,13 @@
|
||||
//! aarch64 architecture implementation
|
||||
|
||||
pub mod boot;
|
||||
pub mod timer;
|
||||
use cortex_a::registers::DAIF;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
pub mod asm;
|
||||
pub mod boot;
|
||||
pub mod exception;
|
||||
pub mod irq;
|
||||
pub mod timer;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "mach_qemu")] {
|
||||
@ -16,3 +20,17 @@ cfg_if! {
|
||||
pub use mach_orangepi3 as machine;
|
||||
}
|
||||
}
|
||||
|
||||
/// Masks IRQs and returns previous IRQ mask state
|
||||
#[inline(always)]
|
||||
pub unsafe fn irq_mask_save() -> u64 {
|
||||
let state = DAIF.get();
|
||||
asm!("msr daifset, {bits}", bits = const 2, options(nomem, nostack, preserves_flags));
|
||||
state
|
||||
}
|
||||
|
||||
/// Restores IRQ mask state
|
||||
#[inline(always)]
|
||||
pub unsafe fn irq_restore(state: u64) {
|
||||
DAIF.set(state);
|
||||
}
|
||||
|
@ -10,18 +10,18 @@ use tock_registers::interfaces::{Readable, Writeable};
|
||||
pub struct GenericTimer;
|
||||
|
||||
impl Device for GenericTimer {
|
||||
fn name() -> &'static str {
|
||||
fn name(&self) -> &'static str {
|
||||
"ARM Generic Timer"
|
||||
}
|
||||
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno> {
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TimestampSource for GenericTimer {
|
||||
fn timestamp(&mut self) -> Result<Duration, Errno> {
|
||||
fn timestamp(&self) -> Result<Duration, Errno> {
|
||||
let cnt = CNTPCT_EL0.get() * 1_000_000_000;
|
||||
let frq = CNTFRQ_EL0.get();
|
||||
Ok(Duration::from_nanos(cnt / frq))
|
||||
|
@ -1,3 +1,35 @@
|
||||
.macro EXC_SAVE_CTX
|
||||
stp x0, x1, [sp, #0]
|
||||
stp x2, x3, [sp, #16]
|
||||
stp x4, x5, [sp, #32]
|
||||
stp x6, x7, [sp, #48]
|
||||
stp x8, x9, [sp, #64]
|
||||
stp x10, x11, [sp, #80]
|
||||
stp x12, x13, [sp, #96]
|
||||
stp x14, x15, [sp, #112]
|
||||
stp x16, x17, [sp, #128]
|
||||
stp x18, x29, [sp, #144]
|
||||
|
||||
mrs x0, elr_el1
|
||||
stp x30, x0, [sp, #160]
|
||||
.endm
|
||||
|
||||
.macro EXC_RESTORE_CTX
|
||||
ldp x30, x0, [sp, #160]
|
||||
msr elr_el1, x0
|
||||
|
||||
ldp x0, x1, [sp, #0]
|
||||
ldp x2, x3, [sp, #16]
|
||||
ldp x4, x5, [sp, #32]
|
||||
ldp x6, x7, [sp, #48]
|
||||
ldp x8, x9, [sp, #64]
|
||||
ldp x10, x11, [sp, #80]
|
||||
ldp x12, x13, [sp, #96]
|
||||
ldp x14, x15, [sp, #112]
|
||||
ldp x16, x17, [sp, #128]
|
||||
ldp x18, x29, [sp, #144]
|
||||
.endm
|
||||
|
||||
.section .text
|
||||
.global aa64_el1_vectors
|
||||
.p2align 12
|
||||
@ -18,31 +50,30 @@ aa64_el1_vectors:
|
||||
.el1_sp_el1_sync:
|
||||
sub sp, sp, #192
|
||||
|
||||
stp x0, x1, [sp, #0]
|
||||
stp x2, x3, [sp, #16]
|
||||
stp x4, x5, [sp, #32]
|
||||
stp x6, x7, [sp, #48]
|
||||
stp x8, x9, [sp, #64]
|
||||
stp x10, x11, [sp, #80]
|
||||
stp x12, x13, [sp, #96]
|
||||
stp x14, x15, [sp, #112]
|
||||
stp x16, x17, [sp, #128]
|
||||
stp x18, x29, [sp, #144]
|
||||
|
||||
mrs x0, elr_el1
|
||||
stp x30, x0, [sp, #160]
|
||||
EXC_SAVE_CTX
|
||||
|
||||
mrs x0, esr_el1
|
||||
mrs x1, far_el1
|
||||
stp x0, x1, [sp, #176]
|
||||
|
||||
mov x0, sp
|
||||
bl __aa64_exc_handler
|
||||
bl __aa64_exc_sync_handler
|
||||
|
||||
b .
|
||||
.p2align 7
|
||||
.el1_sp_el1_irq:
|
||||
b .
|
||||
sub sp, sp, #176
|
||||
|
||||
EXC_SAVE_CTX
|
||||
|
||||
mov x0, sp
|
||||
bl __aa64_exc_irq_handler
|
||||
|
||||
EXC_RESTORE_CTX
|
||||
|
||||
add sp, sp, #176
|
||||
eret
|
||||
|
||||
.p2align 7
|
||||
.el1_sp_el1_fiq:
|
||||
b .
|
||||
|
@ -3,23 +3,22 @@
|
||||
//! The module provides [debug!] and [debugln!] macros
|
||||
//! which can be used in similar way to print! and
|
||||
//! println! from std.
|
||||
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::sync::Spin;
|
||||
use core::fmt;
|
||||
|
||||
struct SerialOutput<T: 'static + SerialDevice> {
|
||||
inner: &'static Spin<T>,
|
||||
inner: &'static T,
|
||||
}
|
||||
|
||||
impl<T: SerialDevice> fmt::Write for SerialOutput<T> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
let mut lock = self.inner.lock();
|
||||
for &byte in s.as_bytes() {
|
||||
if byte == b'\n' {
|
||||
lock.send(b'\r').ok();
|
||||
self.inner.send(b'\r').ok();
|
||||
}
|
||||
// TODO check for errors
|
||||
lock.send(byte).ok();
|
||||
self.inner.send(byte).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -41,18 +41,18 @@ pub struct PinConfig {
|
||||
/// Generic GPIO controller interface
|
||||
pub trait GpioDevice: Device {
|
||||
/// Initializes configuration for given pin
|
||||
unsafe fn set_pin_config(&mut self, pin: u32, cfg: &PinConfig) -> Result<(), Errno>;
|
||||
unsafe fn set_pin_config(&self, pin: u32, cfg: &PinConfig) -> Result<(), Errno>;
|
||||
/// Returns current configuration of given pin
|
||||
unsafe fn get_pin_config(&mut self, pin: u32) -> Result<PinConfig, Errno>;
|
||||
unsafe fn get_pin_config(&self, pin: u32) -> Result<PinConfig, Errno>;
|
||||
|
||||
/// Sets `pin` to HIGH state
|
||||
fn set_pin(&mut self, pin: u32);
|
||||
fn set_pin(&self, pin: u32);
|
||||
/// Sets `pin` to LOW state
|
||||
fn clear_pin(&mut self, pin: u32);
|
||||
fn clear_pin(&self, pin: u32);
|
||||
/// Toggles `pin`'s HIGH/LOW state
|
||||
fn toggle_pin(&mut self, pin: u32);
|
||||
fn toggle_pin(&self, pin: u32);
|
||||
/// Returns `true` if input `pin` is in HIGH state
|
||||
fn read_pin(&mut self, pin: u32) -> Result<bool, Errno>;
|
||||
fn read_pin(&self, pin: u32) -> Result<bool, Errno>;
|
||||
}
|
||||
|
||||
impl PinConfig {
|
||||
|
43
kernel/src/dev/irq.rs
Normal file
43
kernel/src/dev/irq.rs
Normal file
@ -0,0 +1,43 @@
|
||||
//! Interrupt controller and handler interfaces
|
||||
use crate::dev::Device;
|
||||
use core::marker::PhantomData;
|
||||
use error::Errno;
|
||||
|
||||
/// Token to indicate the local core is running in IRQ context
|
||||
pub struct IrqContext<'irq_context> {
|
||||
_0: PhantomData<&'irq_context ()>,
|
||||
}
|
||||
|
||||
/// Interrupt controller interface
|
||||
pub trait IntController: Device {
|
||||
/// Implementation-specific definition for "IRQ line"
|
||||
type IrqNumber;
|
||||
|
||||
/// Binds a handler [IntSource] to a specific [irq] line
|
||||
fn register_handler(&self, irq: Self::IrqNumber, handler: &'static (dyn IntSource + Sync)) -> Result<(), Errno>;
|
||||
|
||||
/// Enables/unmasks [irq] line
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno>;
|
||||
|
||||
/// Handles all pending IRQs for this interrupt controller
|
||||
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>);
|
||||
}
|
||||
|
||||
/// Interface for peripherals capable of emitting IRQs
|
||||
pub trait IntSource: Device {
|
||||
/// Handles pending IRQs, if any, of this [IntSource].
|
||||
///
|
||||
/// If no IRQ is pending, returns [Errno::DoesNotExist]
|
||||
fn handle_irq(&self) -> Result<(), Errno>;
|
||||
|
||||
///
|
||||
fn init_irqs(&'static self) -> Result<(), Errno>;
|
||||
}
|
||||
|
||||
impl<'q> IrqContext<'q> {
|
||||
///
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
Self { _0: PhantomData }
|
||||
}
|
||||
}
|
@ -6,11 +6,12 @@ use error::Errno;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
pub mod gpio;
|
||||
pub mod irq;
|
||||
|
||||
/// Generic device trait
|
||||
pub trait Device {
|
||||
/// Returns device type/driver name
|
||||
fn name() -> &'static str;
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Performs device initialization logic.
|
||||
///
|
||||
@ -18,5 +19,5 @@ pub trait Device {
|
||||
///
|
||||
/// Marked unsafe as it may cause direct hardware-specific side-effects.
|
||||
/// Additionally, may be called twice with undefined results.
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno>;
|
||||
unsafe fn enable(&self) -> Result<(), Errno>;
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ pub mod pl011;
|
||||
/// Generic interface for serial devices
|
||||
pub trait SerialDevice: Device {
|
||||
/// Transmits (blocking) a byte through the serial device
|
||||
fn send(&mut self, byte: u8) -> Result<(), Errno>;
|
||||
fn send(&self, byte: u8) -> Result<(), Errno>;
|
||||
/// Receives a byte through the serial interface.
|
||||
///
|
||||
/// If `blocking` is `false` and there's no data in device's queue,
|
||||
/// will return [Errno::WouldBlock].
|
||||
fn recv(&mut self, blocking: bool) -> Result<u8, Errno>;
|
||||
fn recv(&self, blocking: bool) -> Result<u8, Errno>;
|
||||
}
|
||||
|
@ -1,20 +1,15 @@
|
||||
//! PL011 - ARM PrimeCell UART implementation
|
||||
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::dev::{serial::SerialDevice, Device};
|
||||
use crate::arch::{MemoryIo, machine::{self, IrqNumber}};
|
||||
use crate::dev::{serial::SerialDevice, irq::{IntController, IntSource}, Device};
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
interfaces::{Readable, Writeable, ReadWriteable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
/// Device struct for PL011
|
||||
#[repr(transparent)]
|
||||
pub struct Pl011 {
|
||||
regs: MemoryIo<Regs>,
|
||||
}
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
/// Flag register
|
||||
@ -39,6 +34,10 @@ register_bitfields! {
|
||||
ICR [
|
||||
/// Writing this to ICR clears all IRQs
|
||||
ALL OFFSET(0) NUMBITS(11) []
|
||||
],
|
||||
/// Interrupt mask set/clear register
|
||||
IMSC [
|
||||
RXIM OFFSET(4) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
@ -51,50 +50,83 @@ register_structs! {
|
||||
(0x04 => _res1),
|
||||
/// Flag register
|
||||
(0x18 => FR: ReadOnly<u32, FR::Register>),
|
||||
(0x1C => _res2),
|
||||
/// Line control register
|
||||
(0x2C => LCR_H: ReadWrite<u32>),
|
||||
/// Control register
|
||||
(0x30 => CR: ReadWrite<u32, CR::Register>),
|
||||
(0x34 => IFLS: ReadWrite<u32>),
|
||||
(0x38 => IMSC: ReadWrite<u32, IMSC::Register>),
|
||||
(0x3C => _res3),
|
||||
/// Interrupt clear register
|
||||
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
|
||||
(0x04 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Pl011 {
|
||||
fn send(&mut self, byte: u8) -> Result<(), Errno> {
|
||||
while self.regs.FR.matches_all(FR::TXFF::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte as u32);
|
||||
/// Device struct for PL011
|
||||
pub struct Pl011 {
|
||||
regs: IrqSafeNullLock<MemoryIo<Regs>>,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl IntSource for Pl011 {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
regs.ICR.write(ICR::ALL::CLEAR);
|
||||
|
||||
let byte = regs.DR.get();
|
||||
debugln!("irq byte = {:#04x}", byte);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&mut self, blocking: bool) -> Result<u8, Errno> {
|
||||
if self.regs.FR.matches_all(FR::RXFE::SET) {
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.regs.lock().IMSC.modify(IMSC::RXIM::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Pl011 {
|
||||
fn send(&self, byte: u8) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
while regs.FR.matches_all(FR::TXFF::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
regs.DR.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&self, blocking: bool) -> Result<u8, Errno> {
|
||||
let regs = self.regs.lock();
|
||||
if regs.FR.matches_all(FR::RXFE::SET) {
|
||||
if !blocking {
|
||||
return Err(Errno::WouldBlock);
|
||||
}
|
||||
while self.regs.FR.matches_all(FR::RXFE::SET) {
|
||||
while regs.FR.matches_all(FR::RXFE::SET) {
|
||||
// TODO allow IRQs here?
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.regs.DR.get() as u8)
|
||||
Ok(regs.DR.get() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pl011 {
|
||||
fn name() -> &'static str {
|
||||
fn name(&self) -> &'static str {
|
||||
"PL011 UART"
|
||||
}
|
||||
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno> {
|
||||
self.regs.CR.set(0);
|
||||
self.regs.ICR.write(ICR::ALL::CLEAR);
|
||||
self.regs
|
||||
.CR
|
||||
.write(CR::UARTEN::SET + CR::TXE::SET + CR::RXE::SET);
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
regs.CR.set(0);
|
||||
regs.ICR.write(ICR::ALL::CLEAR);
|
||||
regs.CR.write(CR::UARTEN::SET + CR::TXE::SET + CR::RXE::SET);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -106,9 +138,10 @@ impl Pl011 {
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: MemoryIo::new(base),
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
irq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ use error::Errno;
|
||||
/// Interface for generic timestamp source
|
||||
pub trait TimestampSource: Device {
|
||||
/// Reads current timestamp as a [Duration] from system start time
|
||||
fn timestamp(&mut self) -> Result<Duration, Errno>;
|
||||
fn timestamp(&self) -> Result<Duration, Errno>;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
const_mut_refs,
|
||||
const_raw_ptr_deref,
|
||||
const_fn_fn_ptr_basics,
|
||||
const_fn_trait_bound,
|
||||
const_panic,
|
||||
panic_info_message
|
||||
)]
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use crate::arch::platform::{irq_mask_save, irq_restore};
|
||||
|
||||
/// Dummy lock implementation, does not do any locking.
|
||||
///
|
||||
@ -12,12 +13,69 @@ pub struct NullLock<T: ?Sized> {
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// Dummy lock guard for [NullLock].
|
||||
/// Same as [NullLock], but ensures IRQs are disabled while
|
||||
/// the lock is held
|
||||
pub struct IrqSafeNullLock<T: ?Sized> {
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// Dummy lock guard for [NullLock]
|
||||
#[repr(transparent)]
|
||||
pub struct NullLockGuard<'a, T: ?Sized> {
|
||||
value: &'a mut T,
|
||||
}
|
||||
|
||||
/// Same as [NullLockGuard], but reverts IRQ mask back to normal
|
||||
/// when dropped
|
||||
pub struct IrqSafeNullLockGuard<'a, T: ?Sized> {
|
||||
value: &'a mut T,
|
||||
irq_state: u64
|
||||
}
|
||||
|
||||
impl<T> IrqSafeNullLock<T> {
|
||||
/// Constructs a new instance of the lock, wrapping `value`
|
||||
#[inline(always)]
|
||||
pub const fn new(value: T) -> Self {
|
||||
Self {
|
||||
value: UnsafeCell::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [IrqSafeNullLockGuard] for this lock
|
||||
#[inline]
|
||||
pub fn lock(&self) -> IrqSafeNullLockGuard<T> {
|
||||
unsafe {
|
||||
IrqSafeNullLockGuard {
|
||||
value: &mut *self.value.get(),
|
||||
irq_state: irq_mask_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for IrqSafeNullLockGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for IrqSafeNullLockGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for IrqSafeNullLockGuard<'_, T> {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
irq_restore(self.irq_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NullLock<T> {
|
||||
/// Constructs a new instance of the lock, wrapping `value`
|
||||
#[inline(always)]
|
||||
@ -51,5 +109,6 @@ impl<T: ?Sized> DerefMut for NullLockGuard<'_, T> {
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized> Sync for NullLock<T> {}
|
||||
unsafe impl<T: ?Sized> Sync for IrqSafeNullLock<T> {}
|
||||
|
||||
pub use NullLock as Spin;
|
||||
pub use IrqSafeNullLock as Spin;
|
||||
|
Loading…
x
Reference in New Issue
Block a user