Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ jobs:
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package hello_world --no-default-features qemu ${{ matrix.flags }} --microvm
if: matrix.arch == 'x86_64'
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package stdin qemu
if: matrix.arch == 'x86_64'
if: matrix.arch != 'riscv64'
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package stdin --features hermit/console qemu --devices virtio-console-pci
if: matrix.arch != 'riscv64'
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package stdin --features hermit/console --no-default-features qemu --devices virtio-console-mmio
Expand Down
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ memory_addresses = { version = "0.2.3", default-features = false, features = [
[target.'cfg(target_arch = "aarch64")'.dependencies]
aarch64 = { version = "0.0.14", default-features = false }
arm-gic = { version = "0.6" }
arm-pl011-uart = { version = "0.3", default-features = false }
semihosting = { version = "0.1", optional = true }
memory_addresses = { version = "0.2.3", default-features = false, features = [
"aarch64",
Expand Down
50 changes: 50 additions & 0 deletions src/arch/aarch64/kernel/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::drivers::mmio::get_interrupt_handlers;
#[cfg(feature = "pci")]
use crate::drivers::pci::get_interrupt_handlers;
use crate::drivers::{InterruptHandlerQueue, InterruptLine};
use crate::kernel::serial::handle_uart_interrupt;
use crate::mm::virtualmem::KERNEL_FREE_LIST;
use crate::scheduler::{self, CoreId};
use crate::{core_id, core_scheduler, env};
Expand All @@ -35,6 +36,8 @@ pub(crate) const SGI_RESCHED: u8 = 1;

/// Number of the timer interrupt
static mut TIMER_INTERRUPT: u32 = 0;
/// Number of the UART interrupt
static mut UART_INTERRUPT: u32 = 0;
/// Possible interrupt handlers
static INTERRUPT_HANDLERS: OnceCell<HashMap<u8, InterruptHandlerQueue, RandomState>> =
OnceCell::new();
Expand Down Expand Up @@ -108,6 +111,15 @@ pub(crate) fn install_handlers() {
queue.push_back(timer_handler);
handlers.insert(u8::try_from(TIMER_INTERRUPT).unwrap() + PPI_START, queue);
}

if let Some(queue) = handlers.get_mut(&(u8::try_from(UART_INTERRUPT).unwrap() + SPI_START))
{
queue.push_back(handle_uart_interrupt);
} else {
let mut queue = VecDeque::<fn()>::new();
queue.push_back(handle_uart_interrupt);
handlers.insert(u8::try_from(UART_INTERRUPT).unwrap() + SPI_START, queue);
}
}

INTERRUPT_HANDLERS.set(handlers).unwrap();
Expand Down Expand Up @@ -379,6 +391,44 @@ pub(crate) fn init() {
gic.enable_interrupt(timer_irqid, Some(cpu_id), true);
}

if let Some(uart_node) = fdt.find_compatible(&["arm,pl011"]) {
let irq_slice = uart_node.property("interrupts").unwrap().value;
let (irqtype, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
let (irq, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
let (irqflags, _) = irq_slice.split_at(core::mem::size_of::<u32>());
let irqtype = u32::from_be_bytes(irqtype.try_into().unwrap());
let irq = u32::from_be_bytes(irq.try_into().unwrap());
let irqflags = u32::from_be_bytes(irqflags.try_into().unwrap());

unsafe {
UART_INTERRUPT = irq;
}

debug!("UART interrupt: {irq}, type {irqtype}, flags {irqflags}");

IRQ_NAMES
.lock()
.insert(u8::try_from(irq).unwrap() + SPI_START, "UART");

// enable uart interrupt
let uart_irqid = if irqtype == 1 {
IntId::ppi(irq)
} else if irqtype == 0 {
IntId::spi(irq)
} else {
panic!("Invalid interrupt type");
};
gic.set_interrupt_priority(uart_irqid, Some(cpu_id), 0x00);
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
gic.set_trigger(uart_irqid, Some(cpu_id), Trigger::Level);
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1 {
gic.set_trigger(uart_irqid, Some(cpu_id), Trigger::Edge);
} else {
panic!("Invalid interrupt level!");
}
gic.enable_interrupt(uart_irqid, Some(cpu_id), true);
}

let reschedid = IntId::sgi(SGI_RESCHED.into());
gic.set_interrupt_priority(reschedid, Some(cpu_id), 0x01);
gic.enable_interrupt(reschedid, Some(cpu_id), true);
Expand Down
105 changes: 76 additions & 29 deletions src/arch/aarch64/kernel/serial.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,55 @@
use core::arch::asm;
use alloc::collections::vec_deque::VecDeque;
use core::ptr::NonNull;

use arm_pl011_uart::{DataBits, Interrupts, LineConfig, Parity, StopBits, Uart, UniqueMmioPointer};
use embedded_io::{ErrorType, Read, ReadReady, Write};
use hermit_sync::{InterruptTicketMutex, Lazy};

use crate::errno::Errno;

pub(crate) struct SerialDevice {
pub addr: u32,
static UART_DEVICE: Lazy<InterruptTicketMutex<UartDevice>> =
Lazy::new(|| InterruptTicketMutex::new(UartDevice::new()));

pub(crate) struct UartDevice {
uart: Uart<'static>,
buffer: VecDeque<u8>,
}

impl SerialDevice {
impl UartDevice {
pub fn new() -> Self {
let base = crate::env::boot_info()
.hardware_info
.serial_port_base
.map(|uartport| uartport.get())
.unwrap();

Self { addr: base as u32 }
let uart_pointer =
unsafe { UniqueMmioPointer::new(NonNull::new_unchecked(base as *mut _)) };

let mut uart = Uart::new(uart_pointer);

let line_config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
uart.enable(line_config, 115_200, 16_000_000).unwrap();

uart.set_interrupt_masks(Interrupts::RXI | Interrupts::RTI);
uart.clear_interrupts(Interrupts::all());

Self {
uart,
buffer: VecDeque::new(),
}
}
}

pub(crate) struct SerialDevice;

impl SerialDevice {
pub fn new() -> Self {
Self
}
}

Expand All @@ -26,41 +59,34 @@ impl ErrorType for SerialDevice {

impl Read for SerialDevice {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let _ = buf;
Ok(0)
let mut guard = UART_DEVICE.lock();

if guard.buffer.is_empty() {
Ok(0)
} else {
let min = buf.len().min(guard.buffer.len());

for (dst, src) in buf[..min].iter_mut().zip(guard.buffer.drain(..min)) {
*dst = src;
}

Ok(min)
}
}
}

impl ReadReady for SerialDevice {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
Ok(false)
Ok(!UART_DEVICE.lock().buffer.is_empty())
}
}

impl Write for SerialDevice {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let port = core::ptr::with_exposed_provenance_mut::<u8>(self.addr as usize);
for &byte in buf {
// LF newline characters need to be extended to CRLF over a real serial port.
if byte == b'\n' {
unsafe {
asm!(
"strb w8, [{port}]",
port = in(reg) port,
in("x8") b'\r',
options(nostack),
);
}
}
let mut guard = UART_DEVICE.lock();

unsafe {
asm!(
"strb w8, [{port}]",
port = in(reg) port,
in("x8") byte,
options(nostack),
);
}
for byte in buf {
guard.uart.write_word(*byte);
}

Ok(buf.len())
Expand All @@ -70,3 +96,24 @@ impl Write for SerialDevice {
Ok(())
}
}

pub(crate) fn handle_uart_interrupt() {
let mut guard = UART_DEVICE.lock();

while let Ok(Some(mut byte)) = guard.uart.read_word() {
// Normalize CR to LF
if byte == b'\r' {
byte = b'\n';
}

guard.buffer.push_back(byte);
}

guard
.uart
.clear_interrupts(Interrupts::RXI | Interrupts::RTI);

drop(guard);

crate::console::CONSOLE_WAKER.lock().wake();
}