From 5a165178ccd4a772b9a3e4072a4928cf513a0ba8 Mon Sep 17 00:00:00 2001 From: equation314 Date: Tue, 11 Dec 2018 15:07:13 +0800 Subject: [PATCH] aarch64: implement IRQ register & serial IRQ --- crate/bcm2837/src/interrupt.rs | 27 +++++++++++ crate/bcm2837/src/mini_uart.rs | 40 ++++++++++------ crate/bcm2837/src/timer/generic_timer.rs | 4 +- kernel/src/arch/aarch64/board/raspi3/irq.rs | 26 +++++++++-- .../src/arch/aarch64/board/raspi3/serial.rs | 46 +++++++++++-------- kernel/src/arch/aarch64/board/raspi3/timer.rs | 1 - kernel/src/arch/aarch64/io.rs | 2 +- kernel/src/fs.rs | 5 +- 8 files changed, 110 insertions(+), 41 deletions(-) diff --git a/crate/bcm2837/src/interrupt.rs b/crate/bcm2837/src/interrupt.rs index 7324085..9d6befe 100644 --- a/crate/bcm2837/src/interrupt.rs +++ b/crate/bcm2837/src/interrupt.rs @@ -4,6 +4,7 @@ use volatile::{ReadOnly, Volatile}; const INT_BASE: usize = IO_BASE + 0xB000 + 0x200; /// Allowed interrupts (ref: peripherals 7.5, page 113) +#[repr(u8)] #[derive(Copy, Clone, PartialEq, Debug)] pub enum Interrupt { Timer1 = 1, @@ -30,6 +31,24 @@ struct Registers { DisableBasicIRQ: Volatile, } +/// Pending interrupts +pub struct PendingInterrupts(u64); + +impl Iterator for PendingInterrupts { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + let int = self.0.trailing_zeros(); + if int < 64 { + self.0 &= !(1 << int); + Some(int as usize) + } else { + None + } + } +} + /// An interrupt controller. Used to enable and disable interrupts as well as to /// check if an interrupt is pending. pub struct Controller { @@ -38,6 +57,7 @@ pub struct Controller { impl Controller { /// Returns a new handle to the interrupt controller. + #[inline] pub fn new() -> Controller { Controller { registers: unsafe { &mut *(INT_BASE as *mut Registers) }, @@ -58,4 +78,11 @@ impl Controller { pub fn is_pending(&self, int: Interrupt) -> bool { self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0 } + + /// Return all pending interrupts. + pub fn pending_interrupts(&self) -> PendingInterrupts { + let irq1 = self.registers.IRQPending[0].read() as u64; + let irq2 = self.registers.IRQPending[1].read() as u64; + PendingInterrupts((irq2 << 32) | irq1) + } } diff --git a/crate/bcm2837/src/mini_uart.rs b/crate/bcm2837/src/mini_uart.rs index 28e2c13..d21c249 100644 --- a/crate/bcm2837/src/mini_uart.rs +++ b/crate/bcm2837/src/mini_uart.rs @@ -8,6 +8,13 @@ const MU_REG_BASE: usize = IO_BASE + 0x215040; /// The `AUXENB` register from page 9 of the BCM2837 documentation. const AUX_ENABLES: *mut Volatile = (IO_BASE + 0x215004) as *mut Volatile; +/// Enum representing bit fields of the `AUX_MU_IIR_REG` register. +#[repr(u8)] +pub enum MiniUartInterruptId { + Transmit = 0b010, + Recive = 0b100, +} + /// Enum representing bit fields of the `AUX_MU_LSR_REG` register. #[repr(u8)] enum LsrStatus { @@ -15,7 +22,7 @@ enum LsrStatus { TxAvailable = 1 << 5, } -/// MU registers starting from `AUX_ENABLES` (ref: peripherals 2.1, page 8) +/// MU registers starting from `MU_REG_BASE` (ref: peripherals 2.1, page 8) #[repr(C)] #[allow(non_snake_case)] struct Registers { @@ -62,23 +69,25 @@ impl MiniUart { &mut *(MU_REG_BASE as *mut Registers) }; - Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0); - Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0); - - registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now) - registers.AUX_MU_IER_REG.write(0); // Disable receive and transmit interrupts - registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode - registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high - registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200 - - registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver - MiniUart { registers: registers, timeout: None, } } + pub fn init(&mut self) { + Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0); + Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0); + + self.registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now) + self.registers.AUX_MU_IER_REG.write(1); // Enable receive interrupts and disable transmit interrupts + self.registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode + self.registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high + self.registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200 + + self.registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver + } + /// Set the read timeout to `milliseconds` milliseconds. pub fn set_read_timeout(&mut self, milliseconds: u32) { self.timeout = Some(milliseconds) @@ -111,8 +120,13 @@ impl MiniUart { } /// Reads a byte. Blocks indefinitely until a byte is ready to be read. - pub fn read_byte(&mut self) -> u8 { + pub fn read_byte(&self) -> u8 { while !self.has_byte() {} self.registers.AUX_MU_IO_REG.read() } + + // Read `AUX_MU_IIR_REG` and determine if the interrupt `id` is pending. + pub fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool { + self.registers.AUX_MU_IIR_REG.read() & 0b110 == id as u8 + } } diff --git a/crate/bcm2837/src/timer/generic_timer.rs b/crate/bcm2837/src/timer/generic_timer.rs index 6c3c0c8..14aabe6 100644 --- a/crate/bcm2837/src/timer/generic_timer.rs +++ b/crate/bcm2837/src/timer/generic_timer.rs @@ -58,7 +58,7 @@ impl Timer { /// Reads the generic timer's counter and returns the 64-bit counter value. /// The returned value is the number of elapsed microseconds. pub fn read(&self) -> u64 { - let cntfrq = CNTFRQ_EL0.get(); + let cntfrq = CNTFRQ_EL0.get(); // 62500000 (CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64 } @@ -66,7 +66,7 @@ impl Timer { /// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer /// interrupt will be issued in `us` microseconds. pub fn tick_in(&mut self, us: u32) { - let cntfrq = CNTFRQ_EL0.get(); + let cntfrq = CNTFRQ_EL0.get(); // 62500000 CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32); } diff --git a/kernel/src/arch/aarch64/board/raspi3/irq.rs b/kernel/src/arch/aarch64/board/raspi3/irq.rs index c78b651..a982947 100644 --- a/kernel/src/arch/aarch64/board/raspi3/irq.rs +++ b/kernel/src/arch/aarch64/board/raspi3/irq.rs @@ -1,11 +1,31 @@ use crate::arch::interrupt::TrapFrame; -use bcm2837::timer::Timer; -use bcm2837::interrupt::{Controller, Interrupt}; +use bcm2837::interrupt::Controller; +use log::*; + +pub use bcm2837::interrupt::Interrupt; + +static IRQ_HANDLERS: &'static [Option; 64] = &[None; 64]; pub fn handle_irq(tf: &mut TrapFrame) { - let controller = Timer::new(); + let controller = bcm2837::timer::Timer::new(); if controller.is_pending() { super::timer::set_next(); crate::trap::timer(); } + + for int in Controller::new().pending_interrupts() { + if let Some(handler) = IRQ_HANDLERS[int] { + handler(); + } else { + error!("Unregistered IRQ {}", int); + crate::trap::error(tf); + } + } +} + +pub fn register_irq(int: Interrupt, handler: fn()) { + unsafe { + *(&IRQ_HANDLERS[int as usize] as *const _ as *mut Option) = Some(handler); + } + Controller::new().enable(int); } diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs index 6e4fce8..74ed5dd 100644 --- a/kernel/src/arch/aarch64/board/raspi3/serial.rs +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -1,11 +1,11 @@ -use bcm2837::mini_uart::MiniUart; +use bcm2837::mini_uart::{MiniUart, MiniUartInterruptId}; +use lazy_static::lazy_static; use core::fmt; use spin::Mutex; -use once::*; /// Struct to get a global SerialPort interface pub struct SerialPort { - mu: Option, + mu: MiniUart, } pub trait SerialRead { @@ -14,30 +14,31 @@ pub trait SerialRead { impl SerialPort { /// Creates a new instance of `SerialPort`. - const fn new() -> SerialPort { - SerialPort { mu: None } + fn new() -> SerialPort { + SerialPort { + mu: MiniUart::new(), + } } /// Init a newly created SerialPort, can only be called once. pub fn init(&mut self) { - assert_has_not_been_called!("SerialPort::init must be called only once"); - self.mu = Some(MiniUart::new()); + self.mu.init(); + super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq); } /// Writes the byte `byte` to the UART device. - pub fn write_byte(&mut self, byte: u8) { - match &mut self.mu { - Some(mu) => mu.write_byte(byte), - None => panic!("SerialPort is not initialized"), - } + fn write_byte(&mut self, byte: u8) { + self.mu.write_byte(byte) } /// Reads a byte from the UART device, blocking until a byte is available. - pub fn read_byte(&mut self) -> u8 { - match &mut self.mu { - Some(mu) => return mu.read_byte(), - None => panic!("SerialPort is not initialized"), - } + fn read_byte(&self) -> u8 { + self.mu.read_byte() + } + + // Whether the interrupt `id` is pending. + fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool { + self.mu.interrupt_is_pending(id) } } @@ -70,4 +71,13 @@ impl fmt::Write for SerialPort { } } -pub static SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); +fn handle_serial_irq() { + let serial = SERIAL_PORT.lock(); + if serial.interrupt_is_pending(MiniUartInterruptId::Recive) { + crate::trap::serial(serial.read_byte() as char) + } +} + +lazy_static!{ + pub static ref SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); +} diff --git a/kernel/src/arch/aarch64/board/raspi3/timer.rs b/kernel/src/arch/aarch64/board/raspi3/timer.rs index 9a51d31..8948441 100644 --- a/kernel/src/arch/aarch64/board/raspi3/timer.rs +++ b/kernel/src/arch/aarch64/board/raspi3/timer.rs @@ -1,5 +1,4 @@ use bcm2837::timer; -use bcm2837::interrupt::{Controller, Interrupt}; use log::*; pub fn init() { diff --git a/kernel/src/arch/aarch64/io.rs b/kernel/src/arch/aarch64/io.rs index 1a9d1d4..bb3c986 100644 --- a/kernel/src/arch/aarch64/io.rs +++ b/kernel/src/arch/aarch64/io.rs @@ -1,7 +1,7 @@ //! Serial driver for aarch64. use core::fmt::{Arguments, Write}; -use super::board::serial::{SerialRead, SERIAL_PORT}; +use super::board::serial::*; pub fn getchar() -> char { unsafe { SERIAL_PORT.force_unlock(); } diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 9b9d04b..725642d 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -77,13 +77,12 @@ impl Stdin { pub fn pop(&self) -> char { // QEMU v3.0 don't support M-mode external interrupt (bug?) // So we have to use polling. - // TODO: serial interrupt on aarch64 - #[cfg(any(feature = "m_mode", target_arch = "aarch64"))] + #[cfg(feature = "m_mode")] loop { let c = crate::arch::io::getchar(); if c != '\0' { return c; } } - #[cfg(not(any(feature = "m_mode", target_arch = "aarch64")))] + #[cfg(not(feature = "m_mode"))] loop { let ret = self.buf.lock().pop_front(); match ret {