Skip to content

Commit 3cbf474

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

File tree

5 files changed

+151
-33
lines changed

5 files changed

+151
-33
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
@@ -173,6 +173,7 @@ memory_addresses = { version = "0.2.3", default-features = false, features = [
173173

174174
[target.'cfg(target_arch = "aarch64")'.dependencies]
175175
aarch64 = { version = "0.0.14", default-features = false }
176+
arm-pl011-uart = { version = "0.3", default-features = false }
176177
arm-gic = { version = "0.6" }
177178
hermit-dtb = { version = "0.1" }
178179
semihosting = { version = "0.1", optional = true }

src/arch/aarch64/kernel/interrupts.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::drivers::mmio::get_interrupt_handlers;
2323
#[cfg(feature = "pci")]
2424
use crate::drivers::pci::get_interrupt_handlers;
2525
use crate::drivers::{InterruptHandlerQueue, InterruptLine};
26+
use crate::kernel::serial::handle_uart_interrupt;
2627
use crate::mm::virtualmem::KERNEL_FREE_LIST;
2728
use crate::scheduler::{self, CoreId};
2829
use crate::{core_id, core_scheduler, env};
@@ -37,6 +38,8 @@ pub(crate) const SGI_RESCHED: u8 = 1;
3738

3839
/// Number of the timer interrupt
3940
static mut TIMER_INTERRUPT: u32 = 0;
41+
/// Number of the UART interrupt
42+
static mut UART_INTERRUPT: u32 = 0;
4043
/// Possible interrupt handlers
4144
static INTERRUPT_HANDLERS: OnceCell<HashMap<u8, InterruptHandlerQueue, RandomState>> =
4245
OnceCell::new();
@@ -110,6 +113,15 @@ pub(crate) fn install_handlers() {
110113
queue.push_back(timer_handler);
111114
handlers.insert(u8::try_from(TIMER_INTERRUPT).unwrap() + PPI_START, queue);
112115
}
116+
117+
if let Some(queue) = handlers.get_mut(&(u8::try_from(UART_INTERRUPT).unwrap() + SPI_START))
118+
{
119+
queue.push_back(handle_uart_interrupt);
120+
} else {
121+
let mut queue = VecDeque::<fn()>::new();
122+
queue.push_back(handle_uart_interrupt);
123+
handlers.insert(u8::try_from(UART_INTERRUPT).unwrap() + SPI_START, queue);
124+
}
113125
}
114126

115127
INTERRUPT_HANDLERS.set(handlers).unwrap();
@@ -350,10 +362,12 @@ pub(crate) fn init() {
350362

351363
for node in dtb.enum_subnodes("/") {
352364
let parts: Vec<_> = node.split('@').collect();
365+
let Some(compatible) = dtb.get_property(parts.first().unwrap(), "compatible") else {
366+
continue;
367+
};
368+
let compatible = core::str::from_utf8(compatible).unwrap();
353369

354-
if let Some(compatible) = dtb.get_property(parts.first().unwrap(), "compatible")
355-
&& core::str::from_utf8(compatible).unwrap().contains("timer")
356-
{
370+
if compatible.contains("timer") {
357371
let irq_slice = dtb
358372
.get_property(parts.first().unwrap(), "interrupts")
359373
.unwrap();
@@ -395,6 +409,47 @@ pub(crate) fn init() {
395409
panic!("Invalid interrupt level!");
396410
}
397411
gic.enable_interrupt(timer_irqid, Some(cpu_id), true);
412+
} else if compatible.contains("pl011") {
413+
let irq_slice = dtb
414+
.get_property(parts.first().unwrap(), "interrupts")
415+
.unwrap();
416+
417+
let (irqtype, irq_slice) = irq_slice.split_at(4);
418+
let (irq, irq_slice) = irq_slice.split_at(4);
419+
let (irqflags, _) = irq_slice.split_at(4);
420+
421+
let irqtype = u32::from_be_bytes(irqtype.try_into().unwrap());
422+
let irq = u32::from_be_bytes(irq.try_into().unwrap());
423+
let irqflags = u32::from_be_bytes(irqflags.try_into().unwrap());
424+
425+
unsafe {
426+
UART_INTERRUPT = irq;
427+
}
428+
429+
debug!("UART interrupt: {irq}, type {irqtype}, flags {irqflags}");
430+
431+
let uart_irqid = if irqtype == 1 {
432+
IntId::ppi(irq)
433+
} else if irqtype == 0 {
434+
IntId::spi(irq)
435+
} else {
436+
panic!("Invalid interrupt type");
437+
};
438+
439+
gic.set_interrupt_priority(uart_irqid, Some(cpu_id), 0x00);
440+
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
441+
gic.set_trigger(uart_irqid, Some(cpu_id), Trigger::Level);
442+
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1 {
443+
gic.set_trigger(uart_irqid, Some(cpu_id), Trigger::Edge);
444+
} else {
445+
panic!("Invalid interrupt level!");
446+
}
447+
448+
gic.enable_interrupt(uart_irqid, Some(cpu_id), true);
449+
450+
IRQ_NAMES
451+
.lock()
452+
.insert(u8::try_from(irq).unwrap() + SPI_START, "UART");
398453
}
399454
}
400455

src/arch/aarch64/kernel/serial.rs

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,101 @@
1-
use core::arch::asm;
1+
use alloc::collections::vec_deque::VecDeque;
22
use core::mem::MaybeUninit;
3+
use core::ptr::NonNull;
34

4-
pub(crate) struct SerialDevice {
5-
pub addr: u32,
5+
use arm_pl011_uart::{DataBits, Interrupts, LineConfig, Parity, StopBits, Uart, UniqueMmioPointer};
6+
use hermit_sync::{InterruptTicketMutex, Lazy};
7+
8+
static UART_DEVICE: Lazy<InterruptTicketMutex<UartDevice>> =
9+
Lazy::new(|| InterruptTicketMutex::new(UartDevice::new()));
10+
11+
pub(crate) struct UartDevice {
12+
uart: Uart<'static>,
13+
buffer: VecDeque<u8>,
614
}
715

8-
impl SerialDevice {
16+
impl UartDevice {
917
pub fn new() -> Self {
1018
let base = crate::env::boot_info()
1119
.hardware_info
1220
.serial_port_base
1321
.map(|uartport| uartport.get())
1422
.unwrap();
1523

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

1953
pub fn write(&self, buf: &[u8]) {
20-
let port = core::ptr::with_exposed_provenance_mut::<u8>(self.addr as usize);
21-
for &byte in buf {
22-
// LF newline characters need to be extended to CRLF over a real serial port.
23-
if byte == b'\n' {
24-
unsafe {
25-
asm!(
26-
"strb w8, [{port}]",
27-
port = in(reg) port,
28-
in("x8") b'\r',
29-
options(nostack),
30-
);
31-
}
32-
}
54+
let mut guard = UART_DEVICE.lock();
3355

34-
unsafe {
35-
asm!(
36-
"strb w8, [{port}]",
37-
port = in(reg) port,
38-
in("x8") byte,
39-
options(nostack),
40-
);
41-
}
56+
for byte in buf {
57+
guard.uart.write_word(*byte);
4258
}
4359
}
4460

45-
pub fn read(&self, _buf: &mut [MaybeUninit<u8>]) -> crate::io::Result<usize> {
46-
Ok(0)
61+
pub fn read(&self, buf: &mut [MaybeUninit<u8>]) -> crate::io::Result<usize> {
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.write(src);
71+
}
72+
73+
Ok(min)
74+
}
4775
}
4876

4977
pub fn can_read(&self) -> bool {
50-
false
78+
!UART_DEVICE.lock().buffer.is_empty()
5179
}
5280
}
81+
82+
pub(crate) fn handle_uart_interrupt() {
83+
let mut guard = UART_DEVICE.lock();
84+
85+
while let Ok(Some(mut byte)) = guard.uart.read_word() {
86+
// Normalize CR to LF
87+
if byte == b'\r' {
88+
byte = b'\n';
89+
}
90+
91+
guard.buffer.push_back(byte);
92+
}
93+
94+
guard
95+
.uart
96+
.clear_interrupts(Interrupts::RXI | Interrupts::RTI);
97+
98+
drop(guard);
99+
100+
crate::console::CONSOLE_WAKER.lock().wake();
101+
}

0 commit comments

Comments
 (0)