diff --git a/Cargo.lock b/Cargo.lock index 37c05c2764..71971a438e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2669,15 +2669,6 @@ dependencies = [ "spin 0.9.4", ] -[[package]] -name = "pl011" -version = "1.0.0" -source = "git+https://github.com/theseus-os/pl011/?rev=464dbf22#464dbf2288a5d9e7445b0b1f404ddd41b1fd1c1e" -dependencies = [ - "log", - "volatile-register", -] - [[package]] name = "plain" version = "0.2.3" @@ -3347,11 +3338,10 @@ name = "serial_port_basic" version = "0.1.0" dependencies = [ "arm_boards", - "memory", - "pl011", "port_io", "spin 0.9.4", "sync_irq", + "uart_pl011", ] [[package]] @@ -4200,6 +4190,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "uart_pl011" +version = "0.1.0" +dependencies = [ + "log", + "memory", + "volatile 0.2.7", + "zerocopy", +] + [[package]] name = "uefi-bootloader-api" version = "0.1.0" diff --git a/kernel/serial_port/src/lib.rs b/kernel/serial_port/src/lib.rs index 66126f112b..cfa0f2b2f6 100644 --- a/kernel/serial_port/src/lib.rs +++ b/kernel/serial_port/src/lib.rs @@ -82,6 +82,8 @@ pub fn init_serial_port( serial_port_address: SerialPortAddress, serial_port: SerialPortBasic, ) -> Option<&'static Arc>> { + // Note: if we're called by device_manager, we cannot log (as we're modifying the logger config) + #[cfg(target_arch = "aarch64")] if serial_port_address != SerialPortAddress::COM1 { return None; @@ -309,7 +311,7 @@ fn serial_port_receive_deferred( let mut buf = DataChunk::empty(); let bytes_read; let base_port; - + let mut input_was_ignored = false; let mut send_result = Ok(()); @@ -331,9 +333,6 @@ fn serial_port_receive_deferred( // other than data being received, which is the only one we currently care about. return Ok(()); } - - #[cfg(target_arch = "aarch64")] - sp.enable_interrupt(SerialPortInterruptEvent::DataReceived, true); } if let Err(e) = send_result { @@ -387,15 +386,18 @@ static INTERRUPT_ACTION_COM2_COM4: Once> = Once::new // Cross-platform interrupt handler for COM1 and COM3 (IRQ 0x24 on x86_64). interrupt_handler!(com1_com3_interrupt_handler, Some(interrupts::IRQ_BASE_OFFSET + 0x4), _stack_frame, { - // trace!("COM1/COM3 serial handler"); + // log::trace!("COM1/COM3 serial handler"); + #[cfg(target_arch = "aarch64")] { let mut sp = COM1_SERIAL_PORT.get().unwrap().as_ref().lock(); - sp.enable_interrupt(SerialPortInterruptEvent::DataReceived, false); + sp.acknowledge_interrupt(SerialPortInterruptEvent::DataReceived); } if let Some(func) = INTERRUPT_ACTION_COM1_COM3.get() { func() } + + // log::trace!("COM1/COM3 serial handler done"); EoiBehaviour::HandlerDidNotSendEoi }); diff --git a/kernel/serial_port_basic/Cargo.toml b/kernel/serial_port_basic/Cargo.toml index 20b53639df..9b4e1a5421 100644 --- a/kernel/serial_port_basic/Cargo.toml +++ b/kernel/serial_port_basic/Cargo.toml @@ -13,9 +13,8 @@ spin = "0.9.4" port_io = { path = "../../libs/port_io" } [target.'cfg(target_arch = "aarch64")'.dependencies] -pl011 = { git = "https://github.com/theseus-os/pl011/", rev = "464dbf22" } +uart_pl011 = { path = "../uart_pl011" } arm_boards = { path = "../arm_boards" } -memory = { path = "../memory" } [lib] crate-type = ["rlib"] diff --git a/kernel/serial_port_basic/src/aarch64.rs b/kernel/serial_port_basic/src/aarch64.rs index 7ce000da37..67209e82f7 100644 --- a/kernel/serial_port_basic/src/aarch64.rs +++ b/kernel/serial_port_basic/src/aarch64.rs @@ -1,7 +1,6 @@ -use memory::{MappedPages, PAGE_SIZE, map_frame_range, MMIO_FLAGS}; use super::{TriState, SerialPortInterruptEvent}; use arm_boards::BOARD_CONFIG; -use pl011::PL011; +use uart_pl011::Pl011; use core::fmt; /// The base port I/O addresses for COM serial ports. @@ -19,11 +18,10 @@ pub enum SerialPortAddress { } /// A serial port and its various data and control registers. +#[derive(Debug)] pub struct SerialPort { port_address: SerialPortAddress, - inner: Option, - // Owner of the MMIO frames for the PL011 registers - _mapped_pages: Option, + inner: Option, } impl Drop for SerialPort { @@ -33,7 +31,6 @@ impl Drop for SerialPort { if let TriState::Taken = &*sp_locked { let dummy = SerialPort { inner: None, - _mapped_pages: None, port_address: self.port_address, }; let dropped = core::mem::replace(self, dummy); @@ -57,19 +54,12 @@ impl SerialPort { None => panic!("Board doesn't have {:?}", serial_port_address), }; - let mapped_pages = map_frame_range(*mmio_base, PAGE_SIZE, MMIO_FLAGS) - .expect("serial_port_basic: couldn't map the UART interface"); - let addr = mapped_pages.start_address().value(); - let mut pl011 = PL011::new(addr as *mut _); - - pl011.enable_rx_interrupt(true); - pl011.set_fifo_mode(false); - // pl011.log_status(); + let pl011 = Pl011::new(*mmio_base) + .expect("SerialPort::new: Couldn't initialize PL011 UART"); SerialPort { port_address: serial_port_address, inner: Some(pl011), - _mapped_pages: Some(mapped_pages), } } @@ -84,6 +74,15 @@ impl SerialPort { } } + /// Clears an interrupt in the serial port controller + pub fn acknowledge_interrupt(&mut self, event: SerialPortInterruptEvent) { + if matches!(event, SerialPortInterruptEvent::DataReceived) { + self.inner.as_mut().unwrap().acknowledge_rx_interrupt(); + } else { + unimplemented!() + } + } + /// Write the given string to the serial port, blocking until data can be transmitted. /// /// # Special characters diff --git a/kernel/serial_port_basic/src/x86_64.rs b/kernel/serial_port_basic/src/x86_64.rs index 59fac34e95..6c83e5aa72 100644 --- a/kernel/serial_port_basic/src/x86_64.rs +++ b/kernel/serial_port_basic/src/x86_64.rs @@ -187,6 +187,11 @@ impl SerialPort { } } + /// Clears an interrupt in the serial port controller + pub fn acknowledge_interrupt(&mut self, _event: SerialPortInterruptEvent) { + // no-op on x86_64 + } + /// Write the given string to the serial port, blocking until data can be transmitted. /// /// # Special characters diff --git a/kernel/uart_pl011/Cargo.toml b/kernel/uart_pl011/Cargo.toml new file mode 100644 index 0000000000..6f734ad396 --- /dev/null +++ b/kernel/uart_pl011/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "uart_pl011" +authors = ["Nathan Royer "] +description = "Simple Driver for PL011 UARTs" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4.8" +volatile = "0.2.7" +zerocopy = "0.5.0" +memory = { path = "../memory" } \ No newline at end of file diff --git a/kernel/uart_pl011/src/lib.rs b/kernel/uart_pl011/src/lib.rs new file mode 100644 index 0000000000..2587d3d4a9 --- /dev/null +++ b/kernel/uart_pl011/src/lib.rs @@ -0,0 +1,192 @@ +//! Driver for pl011 UARTs + +#![no_std] +use core::fmt; +use zerocopy::FromBytes; +use volatile::{Volatile, ReadOnly, WriteOnly}; +use memory::{BorrowedMappedPages, Mutable, PhysicalAddress, PAGE_SIZE, map_frame_range, MMIO_FLAGS}; + +/// Struct representing Pl011 registers. Not intended to be directly used +#[derive(Debug, FromBytes)] +#[repr(C)] +pub struct Pl011_Regs { + /// Data Register + pub uartdr: Volatile, + /// receive status / error clear + pub uartrsr: Volatile, + reserved0: [u32; 4], + /// flag register + pub uartfr: ReadOnly, + reserved1: u32, + /// IrDA Low power counter register + pub uartilpr: Volatile, + /// integer baud rate + pub uartibrd: Volatile, + /// fractional baud rate + pub uartfbrd: Volatile, + /// line control + pub uartlcr_h: Volatile, + /// control + pub uartcr: Volatile, + /// interrupt fifo level select + pub uartifls: Volatile, + /// interrupt mask set/clear + pub uartimsc: Volatile, + /// raw interrupt status + pub uartris: ReadOnly, + /// masked interrupt status + pub uartmis: ReadOnly, + /// interrupt clear + pub uarticr: WriteOnly, + /// dma control + pub uartdmacr: Volatile, + reserved2: [u32; 997], + /// UART Periph ID0 + pub uartperiphid0: ReadOnly, + /// UART Periph ID1 + pub uartperiphid1: ReadOnly, + /// UART Periph ID2 + pub uartperiphid2: ReadOnly, + /// UART Periph ID3 + pub uartperiphid3: ReadOnly, + /// UART PCell ID0 + pub uartpcellid0: ReadOnly, + /// UART PCell ID1 + pub uartpcellid1: ReadOnly, + /// UART PCell ID2 + pub uartpcellid2: ReadOnly, + /// UART PCell ID3 + pub uartpcellid3: ReadOnly, +} + +const UARTIMSC_RXIM: u32 = 1 << 4; +const UARTUCR_RXIC: u32 = 1 << 4; + +const UARTLCR_FEN: u32 = 1 << 4; + +const UARTCR_RX_ENABLED: u32 = 1 << 9; +const UARTCR_TX_ENABLED: u32 = 1 << 8; +const UARTCR_UART_ENABLED: u32 = 1 << 0; + +const UARTFR_RX_BUF_EMPTY: u32 = 1 << 4; +const UARTFR_TX_BUF_FULL: u32 = 1 << 5; + +/// A Pl011 Single-Serial-Port Controller. +pub struct Pl011 { + regs: BorrowedMappedPages +} + +impl core::fmt::Debug for Pl011 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.regs.fmt(f) + } +} + +/// Generic methods +impl Pl011 { + /// Initialize a UART driver. + pub fn new(base: PhysicalAddress) -> Result { + let mapped_pages = map_frame_range(base, PAGE_SIZE, MMIO_FLAGS)?; + + let mut this = Self { + regs: mapped_pages.into_borrowed_mut(0).map_err(|(_, e)| e)?, + }; + + this.enable_rx_interrupt(true); + this.set_fifo_mode(false); + + Ok(this) + } + + /// Enable on-receive interrupt + pub fn enable_rx_interrupt(&mut self, enable: bool) { + let mut reg = self.regs.uartimsc.read(); + + match enable { + true => reg |= UARTIMSC_RXIM, + false => reg &= !UARTIMSC_RXIM, + }; + + self.regs.uartimsc.write(reg); + } + + pub fn acknowledge_rx_interrupt(&mut self) { + self.regs.uarticr.write(UARTUCR_RXIC); + } + + /// Set FIFO mode + pub fn set_fifo_mode(&mut self, enable: bool) { + let mut reg = self.regs.uartlcr_h.read(); + + match enable { + true => reg |= UARTLCR_FEN, + false => reg &= !UARTLCR_FEN, + }; + + self.regs.uartlcr_h.write(reg); + } + + /// Outputs a summary of the state of the controller using `log::info!()` + pub fn log_status(&self) { + let reg = self.regs.uartcr.read(); + log::info!("RX enabled: {}", (reg & UARTCR_RX_ENABLED) > 0); + log::info!("TX enabled: {}", (reg & UARTCR_TX_ENABLED) > 0); + log::info!("UART enabled: {}", (reg & UARTCR_UART_ENABLED) > 0); + } + + /// Returns true if the receive-buffer-empty flag is clear. + pub fn has_incoming_data(&self) -> bool { + let uartfr = self.regs.uartfr.read(); + uartfr & UARTFR_RX_BUF_EMPTY == 0 + } + + /// Reads a single byte out the uart + /// + /// Spins until a byte is available in the fifo. + pub fn read_byte(&self) -> u8 { + while !self.has_incoming_data() {} + self.regs.uartdr.read() as u8 + } + + /// Reads bytes into a slice until there is none available. + pub fn read_bytes(&self, bytes: &mut [u8]) -> usize { + let mut read = 0; + + while read < bytes.len() && self.has_incoming_data() { + bytes[read] = self.read_byte(); + read += 1; + } + + read + } + + /// Returns true if the transmit-buffer-full flag is clear. + pub fn is_writeable(&self) -> bool { + let uartfr = self.regs.uartfr.read(); + uartfr & UARTFR_TX_BUF_FULL == 0 + } + + /// Writes a single byte out the uart. + /// + /// Spins until space is available in the fifo. + pub fn write_byte(&mut self, data: u8) { + while !self.is_writeable() {} + self.regs.uartdr.write(data as u32); + } + + /// Writes a byte slice out the uart. + /// + /// Spins until space is available in the fifo. + pub fn write_bytes(&mut self, bytes: &[u8]) { + for b in bytes { + self.write_byte(*b); + } + } +} + +impl fmt::Write for Pl011 { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_bytes(s.as_bytes()); + Ok(()) + } +}