Skip to content

Commit d0e88c3

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

File tree

5 files changed

+150
-34
lines changed

5 files changed

+150
-34
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: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,99 @@
1-
use core::arch::asm;
1+
use alloc::collections::vec_deque::VecDeque;
2+
use alloc::vec::Vec;
23
use core::mem::MaybeUninit;
4+
use core::ptr::NonNull;
35

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

8-
impl SerialDevice {
17+
impl UartDevice {
918
pub fn new() -> Self {
1019
let base = crate::env::boot_info()
1120
.hardware_info
1221
.serial_port_base
1322
.map(|uartport| uartport.get())
1423
.unwrap();
1524

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

1954
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-
}
33-
34-
unsafe {
35-
asm!(
36-
"strb w8, [{port}]",
37-
port = in(reg) port,
38-
in("x8") byte,
39-
options(nostack),
40-
);
41-
}
55+
let mut guard = UART_DEVICE.lock();
56+
57+
for byte in buf {
58+
guard.uart.write_word(*byte);
4259
}
4360
}
4461

45-
pub fn read(&self, _buf: &mut [MaybeUninit<u8>]) -> crate::io::Result<usize> {
46-
Ok(0)
62+
pub fn read(&self, buf: &mut [MaybeUninit<u8>]) -> crate::io::Result<usize> {
63+
let mut guard = UART_DEVICE.lock();
64+
65+
if guard.buffer.is_empty() {
66+
Ok(0)
67+
} else {
68+
let min = core::cmp::min(buf.len(), guard.buffer.len());
69+
let drained = guard.buffer.drain(..min).collect::<Vec<_>>();
70+
buf[..min].write_copy_of_slice(drained.as_slice());
71+
Ok(min)
72+
}
4773
}
4874

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

0 commit comments

Comments
 (0)