Skip to content

Commit 1697352

Browse files
committed
feat(aarch64): Add support for stdin
Signed-off-by: Jens Reidel <[email protected]>
1 parent daec749 commit 1697352

File tree

5 files changed

+141
-30
lines changed

5 files changed

+141
-30
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ jobs:
191191
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package hello_world --no-default-features qemu ${{ matrix.flags }} --microvm
192192
if: matrix.arch == 'x86_64'
193193
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package stdin qemu
194-
if: matrix.arch == 'x86_64'
194+
if: matrix.arch != 'riscv64'
195195
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package stdin --features hermit/console qemu --devices virtio-console-pci
196196
if: matrix.arch != 'riscv64'
197197
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package stdin --features hermit/console --no-default-features qemu --devices virtio-console-mmio

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ memory_addresses = { version = "0.2.3", default-features = false, features = [
177177
[target.'cfg(target_arch = "aarch64")'.dependencies]
178178
aarch64 = { version = "0.0.14", default-features = false }
179179
arm-gic = { version = "0.6" }
180+
arm-pl011-uart = { version = "0.3", default-features = false }
180181
semihosting = { version = "0.1", optional = true }
181182
memory_addresses = { version = "0.2.3", default-features = false, features = [
182183
"aarch64",

src/arch/aarch64/kernel/interrupts.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::drivers::mmio::get_interrupt_handlers;
2121
#[cfg(feature = "pci")]
2222
use crate::drivers::pci::get_interrupt_handlers;
2323
use crate::drivers::{InterruptHandlerQueue, InterruptLine};
24+
use crate::kernel::serial::handle_uart_interrupt;
2425
use crate::mm::virtualmem::KERNEL_FREE_LIST;
2526
use crate::scheduler::{self, CoreId};
2627
use crate::{core_id, core_scheduler, env};
@@ -35,6 +36,8 @@ pub(crate) const SGI_RESCHED: u8 = 1;
3536

3637
/// Number of the timer interrupt
3738
static mut TIMER_INTERRUPT: u32 = 0;
39+
/// Number of the UART interrupt
40+
static mut UART_INTERRUPT: u32 = 0;
3841
/// Possible interrupt handlers
3942
static INTERRUPT_HANDLERS: OnceCell<HashMap<u8, InterruptHandlerQueue, RandomState>> =
4043
OnceCell::new();
@@ -108,6 +111,15 @@ pub(crate) fn install_handlers() {
108111
queue.push_back(timer_handler);
109112
handlers.insert(u8::try_from(TIMER_INTERRUPT).unwrap() + PPI_START, queue);
110113
}
114+
115+
if let Some(queue) = handlers.get_mut(&(u8::try_from(UART_INTERRUPT).unwrap() + SPI_START))
116+
{
117+
queue.push_back(handle_uart_interrupt);
118+
} else {
119+
let mut queue = VecDeque::<fn()>::new();
120+
queue.push_back(handle_uart_interrupt);
121+
handlers.insert(u8::try_from(UART_INTERRUPT).unwrap() + SPI_START, queue);
122+
}
111123
}
112124

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

394+
if let Some(uart_node) = fdt.find_compatible(&["arm,pl011"]) {
395+
let irq_slice = uart_node.property("interrupts").unwrap().value;
396+
let (irqtype, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
397+
let (irq, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
398+
let (irqflags, _) = irq_slice.split_at(core::mem::size_of::<u32>());
399+
let irqtype = u32::from_be_bytes(irqtype.try_into().unwrap());
400+
let irq = u32::from_be_bytes(irq.try_into().unwrap());
401+
let irqflags = u32::from_be_bytes(irqflags.try_into().unwrap());
402+
403+
unsafe {
404+
UART_INTERRUPT = irq;
405+
}
406+
407+
debug!("UART interrupt: {irq}, type {irqtype}, flags {irqflags}");
408+
409+
IRQ_NAMES
410+
.lock()
411+
.insert(u8::try_from(irq).unwrap() + SPI_START, "UART");
412+
413+
// enable uart interrupt
414+
let uart_irqid = if irqtype == 1 {
415+
IntId::ppi(irq)
416+
} else if irqtype == 0 {
417+
IntId::spi(irq)
418+
} else {
419+
panic!("Invalid interrupt type");
420+
};
421+
gic.set_interrupt_priority(uart_irqid, Some(cpu_id), 0x00);
422+
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
423+
gic.set_trigger(uart_irqid, Some(cpu_id), Trigger::Level);
424+
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1 {
425+
gic.set_trigger(uart_irqid, Some(cpu_id), Trigger::Edge);
426+
} else {
427+
panic!("Invalid interrupt level!");
428+
}
429+
gic.enable_interrupt(uart_irqid, Some(cpu_id), true);
430+
}
431+
382432
let reschedid = IntId::sgi(SGI_RESCHED.into());
383433
gic.set_interrupt_priority(reschedid, Some(cpu_id), 0x01);
384434
gic.enable_interrupt(reschedid, Some(cpu_id), true);

src/arch/aarch64/kernel/serial.rs

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,55 @@
1-
use core::arch::asm;
1+
use alloc::collections::vec_deque::VecDeque;
2+
use core::ptr::NonNull;
23

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

58
use crate::errno::Errno;
69

7-
pub(crate) struct SerialDevice {
8-
pub addr: u32,
10+
static UART_DEVICE: Lazy<InterruptTicketMutex<UartDevice>> =
11+
Lazy::new(|| InterruptTicketMutex::new(UartDevice::new()));
12+
13+
pub(crate) struct UartDevice {
14+
uart: Uart<'static>,
15+
buffer: VecDeque<u8>,
916
}
1017

11-
impl SerialDevice {
18+
impl UartDevice {
1219
pub fn new() -> Self {
1320
let base = crate::env::boot_info()
1421
.hardware_info
1522
.serial_port_base
1623
.map(|uartport| uartport.get())
1724
.unwrap();
1825

19-
Self { addr: base as u32 }
26+
let uart_pointer =
27+
unsafe { UniqueMmioPointer::new(NonNull::new_unchecked(base as *mut _)) };
28+
29+
let mut uart = Uart::new(uart_pointer);
30+
31+
let line_config = LineConfig {
32+
data_bits: DataBits::Bits8,
33+
parity: Parity::None,
34+
stop_bits: StopBits::One,
35+
};
36+
uart.enable(line_config, 115_200, 16_000_000).unwrap();
37+
38+
uart.set_interrupt_masks(Interrupts::RXI | Interrupts::RTI);
39+
uart.clear_interrupts(Interrupts::all());
40+
41+
Self {
42+
uart,
43+
buffer: VecDeque::new(),
44+
}
45+
}
46+
}
47+
48+
pub(crate) struct SerialDevice;
49+
50+
impl SerialDevice {
51+
pub fn new() -> Self {
52+
Self
2053
}
2154
}
2255

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

2760
impl Read for SerialDevice {
2861
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
29-
let _ = buf;
30-
Ok(0)
62+
let mut guard = UART_DEVICE.lock();
63+
64+
if guard.buffer.is_empty() {
65+
Ok(0)
66+
} else {
67+
let min = buf.len().min(guard.buffer.len());
68+
69+
for (dst, src) in buf[..min].iter_mut().zip(guard.buffer.drain(..min)) {
70+
*dst = src;
71+
}
72+
73+
Ok(min)
74+
}
3175
}
3276
}
3377

3478
impl ReadReady for SerialDevice {
3579
fn read_ready(&mut self) -> Result<bool, Self::Error> {
36-
Ok(false)
80+
Ok(!UART_DEVICE.lock().buffer.is_empty())
3781
}
3882
}
3983

4084
impl Write for SerialDevice {
4185
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
42-
let port = core::ptr::with_exposed_provenance_mut::<u8>(self.addr as usize);
43-
for &byte in buf {
44-
// LF newline characters need to be extended to CRLF over a real serial port.
45-
if byte == b'\n' {
46-
unsafe {
47-
asm!(
48-
"strb w8, [{port}]",
49-
port = in(reg) port,
50-
in("x8") b'\r',
51-
options(nostack),
52-
);
53-
}
54-
}
86+
let mut guard = UART_DEVICE.lock();
5587

56-
unsafe {
57-
asm!(
58-
"strb w8, [{port}]",
59-
port = in(reg) port,
60-
in("x8") byte,
61-
options(nostack),
62-
);
63-
}
88+
for byte in buf {
89+
guard.uart.write_word(*byte);
6490
}
6591

6692
Ok(buf.len())
@@ -70,3 +96,24 @@ impl Write for SerialDevice {
7096
Ok(())
7197
}
7298
}
99+
100+
pub(crate) fn handle_uart_interrupt() {
101+
let mut guard = UART_DEVICE.lock();
102+
103+
while let Ok(Some(mut byte)) = guard.uart.read_word() {
104+
// Normalize CR to LF
105+
if byte == b'\r' {
106+
byte = b'\n';
107+
}
108+
109+
guard.buffer.push_back(byte);
110+
}
111+
112+
guard
113+
.uart
114+
.clear_interrupts(Interrupts::RXI | Interrupts::RTI);
115+
116+
drop(guard);
117+
118+
crate::console::CONSOLE_WAKER.lock().wake();
119+
}

0 commit comments

Comments
 (0)