feat: gicv2 and basic irq handling

This commit is contained in:
Mark Poliakov 2021-10-07 13:56:17 +03:00
parent ea7eb300f7
commit 58ee697a8c
24 changed files with 673 additions and 121 deletions

View File

@ -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

View File

@ -7,4 +7,5 @@ pub enum Errno {
NotADirectory,
OutOfMemory,
WouldBlock,
AlreadyExists,
}

View File

@ -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);
}
}

View File

@ -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;

View 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));
}
}

View 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 = &regs.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),
);
}
}
}

View 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]),
}
}
}

View File

@ -0,0 +1,3 @@
//! AArch64-specific IRQ handling functionality
pub mod gic;

View File

@ -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);

View File

@ -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) };

View File

@ -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
}
}
}

View File

@ -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 {};

View File

@ -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);
}

View File

@ -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))

View File

@ -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 .

View File

@ -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(())
}

View File

@ -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
View 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 }
}
}

View File

@ -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>;
}

View File

@ -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>;
}

View File

@ -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
}
}
}

View File

@ -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>;
}

View File

@ -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
)]

View File

@ -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;