Support UART console
This commit is contained in:
parent
81c2f8d4bd
commit
000ad53c9f
|
|
@ -184,6 +184,7 @@ dependencies = [
|
||||||
"aster-softirq",
|
"aster-softirq",
|
||||||
"aster-systree",
|
"aster-systree",
|
||||||
"aster-time",
|
"aster-time",
|
||||||
|
"aster-uart",
|
||||||
"aster-util",
|
"aster-util",
|
||||||
"aster-virtio",
|
"aster-virtio",
|
||||||
"atomic-integer-wrapper",
|
"atomic-integer-wrapper",
|
||||||
|
|
@ -338,6 +339,19 @@ dependencies = [
|
||||||
"spin",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aster-uart"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aster-console",
|
||||||
|
"component",
|
||||||
|
"fdt",
|
||||||
|
"inherit-methods-macro",
|
||||||
|
"log",
|
||||||
|
"ostd",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aster-util"
|
name = "aster-util"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ members = [
|
||||||
"kernel/comps/softirq",
|
"kernel/comps/softirq",
|
||||||
"kernel/comps/systree",
|
"kernel/comps/systree",
|
||||||
"kernel/comps/time",
|
"kernel/comps/time",
|
||||||
|
"kernel/comps/uart",
|
||||||
"kernel/comps/virtio",
|
"kernel/comps/virtio",
|
||||||
"kernel/libs/aster-bigtcp",
|
"kernel/libs/aster-bigtcp",
|
||||||
"kernel/libs/aster-rights",
|
"kernel/libs/aster-rights",
|
||||||
|
|
@ -103,6 +104,7 @@ aster-pci = { path = "kernel/comps/pci" }
|
||||||
aster-softirq = { path = "kernel/comps/softirq" }
|
aster-softirq = { path = "kernel/comps/softirq" }
|
||||||
aster-systree = { path = "kernel/comps/systree" }
|
aster-systree = { path = "kernel/comps/systree" }
|
||||||
aster-time = { path = "kernel/comps/time" }
|
aster-time = { path = "kernel/comps/time" }
|
||||||
|
aster-uart = { path = "kernel/comps/uart" }
|
||||||
aster-virtio = { path = "kernel/comps/virtio" }
|
aster-virtio = { path = "kernel/comps/virtio" }
|
||||||
|
|
||||||
# Crates under kernel/libs
|
# Crates under kernel/libs
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ pci = { name = "aster-pci" }
|
||||||
softirq = { name = "aster-softirq" }
|
softirq = { name = "aster-softirq" }
|
||||||
systree = { name = "aster-systree" }
|
systree = { name = "aster-systree" }
|
||||||
time = { name = "aster-time" }
|
time = { name = "aster-time" }
|
||||||
|
uart = { name = "aster-uart" }
|
||||||
virtio = { name = "aster-virtio" }
|
virtio = { name = "aster-virtio" }
|
||||||
|
|
||||||
[whitelist]
|
[whitelist]
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -244,6 +244,7 @@ OSDK_CRATES := \
|
||||||
kernel/comps/softirq \
|
kernel/comps/softirq \
|
||||||
kernel/comps/systree \
|
kernel/comps/systree \
|
||||||
kernel/comps/time \
|
kernel/comps/time \
|
||||||
|
kernel/comps/uart \
|
||||||
kernel/comps/virtio \
|
kernel/comps/virtio \
|
||||||
kernel/libs/aster-bigtcp \
|
kernel/libs/aster-bigtcp \
|
||||||
kernel/libs/aster-util \
|
kernel/libs/aster-util \
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ aster-rights-proc.workspace = true
|
||||||
aster-softirq.workspace = true
|
aster-softirq.workspace = true
|
||||||
aster-systree.workspace = true
|
aster-systree.workspace = true
|
||||||
aster-time.workspace = true
|
aster-time.workspace = true
|
||||||
|
aster-uart.workspace = true
|
||||||
aster-util.workspace = true
|
aster-util.workspace = true
|
||||||
aster-virtio.workspace = true
|
aster-virtio.workspace = true
|
||||||
atomic-integer-wrapper.workspace = true
|
atomic-integer-wrapper.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
[package]
|
||||||
|
name = "aster-uart"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aster-console.workspace = true
|
||||||
|
component.workspace = true
|
||||||
|
inherit-methods-macro.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
ostd.workspace = true
|
||||||
|
spin.workspace = true
|
||||||
|
|
||||||
|
[target.riscv64imac-unknown-none-elf.dependencies]
|
||||||
|
fdt = { version = "0.1.5", features = ["pretty-printing"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use ostd::arch::serial::SERIAL_PORT;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CONSOLE_NAME,
|
||||||
|
alloc::string::ToString,
|
||||||
|
console::{Uart, UartConsole},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
let Some(uart) = SERIAL_PORT.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let uart_console = UartConsole::new(uart);
|
||||||
|
|
||||||
|
aster_console::register_device(CONSOLE_NAME.to_string(), uart_console.clone());
|
||||||
|
|
||||||
|
// TODO: Set up the IRQ line and handle the received data.
|
||||||
|
// Suppress the dead code warnings of the related methods.
|
||||||
|
let _ = || uart_console.trigger_input_callbacks();
|
||||||
|
let _ = || uart.flush();
|
||||||
|
|
||||||
|
log::info!("[UART]: Registered NS16550A as a console");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use ostd::arch::boot::DEVICE_TREE;
|
||||||
|
|
||||||
|
mod ns16550a;
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
let device_tree = DEVICE_TREE.get().unwrap();
|
||||||
|
|
||||||
|
if let Some(ns16550a_node) = device_tree.find_compatible(&["ns16550a"]) {
|
||||||
|
ns16550a::init(ns16550a_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
|
use fdt::node::FdtNode;
|
||||||
|
use ostd::{
|
||||||
|
arch::irq::{IRQ_CHIP, InterruptSourceInFdt, MappedIrqLine},
|
||||||
|
console::uart_ns16650a::{Ns16550aAccess, Ns16550aRegister, Ns16550aUart},
|
||||||
|
io::IoMem,
|
||||||
|
irq::IrqLine,
|
||||||
|
mm::VmIoOnce,
|
||||||
|
sync::SpinLock,
|
||||||
|
};
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CONSOLE_NAME,
|
||||||
|
console::{Uart, UartConsole},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Access to serial registers via `IoMem`.
|
||||||
|
struct SerialAccess {
|
||||||
|
io_mem: IoMem,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ns16550aAccess for SerialAccess {
|
||||||
|
fn read(&self, reg: Ns16550aRegister) -> u8 {
|
||||||
|
self.io_mem.read_once(reg as u16 as usize).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, reg: Ns16550aRegister, val: u8) {
|
||||||
|
self.io_mem.write_once(reg as u16 as usize, &val).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IRQ line for UART serial.
|
||||||
|
static IRQ_LINE: Once<MappedIrqLine> = Once::new();
|
||||||
|
|
||||||
|
pub(super) fn init(fdt_node: FdtNode) {
|
||||||
|
let Some(reg) = fdt_node.reg().and_then(|mut regs| regs.next()) else {
|
||||||
|
log::info!("[UART]: Failed to read 'reg' property from NS16550A node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(reg_size) = reg.size else {
|
||||||
|
log::info!("[UART]: Incomplete 'reg' property found in NS16550A node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let reg_addr = reg.starting_address as usize;
|
||||||
|
let Ok(io_mem) = IoMem::acquire(reg_addr..reg_addr + reg_size) else {
|
||||||
|
log::info!("[UART]: I/O memory is not available for NS16550A");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(intr_parent) = fdt_node
|
||||||
|
.property("interrupt-parent")
|
||||||
|
.and_then(|prop| prop.as_usize())
|
||||||
|
else {
|
||||||
|
log::info!("[UART]: Failed to read 'interrupt-parent' property from NS16550A node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(intr) = fdt_node.interrupts().and_then(|mut intrs| intrs.next()) else {
|
||||||
|
log::info!("[UART]: Failed to read 'interrupts' property from NS16550A node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(mut irq_line) = IrqLine::alloc().and_then(|irq_line| {
|
||||||
|
IRQ_CHIP.get().unwrap().map_fdt_pin_to(
|
||||||
|
InterruptSourceInFdt {
|
||||||
|
interrupt_parent: intr_parent as u32,
|
||||||
|
interrupt: intr as u32,
|
||||||
|
},
|
||||||
|
irq_line,
|
||||||
|
)
|
||||||
|
}) else {
|
||||||
|
log::info!("[UART]: IRQ line is not available for NS16550A");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut uart = Ns16550aUart::new(SerialAccess { io_mem });
|
||||||
|
uart.init();
|
||||||
|
|
||||||
|
let uart_console = UartConsole::new(SpinLock::new(uart));
|
||||||
|
|
||||||
|
aster_console::register_device(CONSOLE_NAME.to_string(), uart_console.clone());
|
||||||
|
|
||||||
|
let cloned_uart_console = uart_console.clone();
|
||||||
|
irq_line.on_active(move |_| cloned_uart_console.trigger_input_callbacks());
|
||||||
|
IRQ_LINE.call_once(move || irq_line);
|
||||||
|
uart_console.uart().flush();
|
||||||
|
|
||||||
|
log::info!("[UART]: Registered NS16550A as a console");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::string::ToString;
|
||||||
|
|
||||||
|
use ostd::{
|
||||||
|
arch::{
|
||||||
|
irq::{IRQ_CHIP, MappedIrqLine},
|
||||||
|
serial::SERIAL_PORT,
|
||||||
|
},
|
||||||
|
irq::IrqLine,
|
||||||
|
};
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CONSOLE_NAME,
|
||||||
|
console::{Uart, UartConsole},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ISA interrupt number for UART serial.
|
||||||
|
// FIXME: The interrupt number should be retrieved from the ACPI table instead of being hard-coded.
|
||||||
|
const ISA_INTR_NUM: u8 = 4;
|
||||||
|
|
||||||
|
/// IRQ line for UART serial.
|
||||||
|
static IRQ_LINE: Once<MappedIrqLine> = Once::new();
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
let Some(uart) = SERIAL_PORT.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(mut irq_line) = IrqLine::alloc().and_then(|irq_line| {
|
||||||
|
IRQ_CHIP
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.map_isa_pin_to(irq_line, ISA_INTR_NUM)
|
||||||
|
}) else {
|
||||||
|
log::info!("[UART]: IRQ line is not available");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let uart_console = UartConsole::new(uart);
|
||||||
|
|
||||||
|
aster_console::register_device(CONSOLE_NAME.to_string(), uart_console.clone());
|
||||||
|
|
||||||
|
irq_line.on_active(move |_| uart_console.trigger_input_callbacks());
|
||||||
|
IRQ_LINE.call_once(move || irq_line);
|
||||||
|
uart.flush();
|
||||||
|
|
||||||
|
log::info!("[UART]: Registered NS16550A as a console");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::{sync::Arc, vec::Vec};
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use aster_console::{AnyConsoleDevice, ConsoleCallback};
|
||||||
|
use inherit_methods_macro::inherit_methods;
|
||||||
|
use ostd::{
|
||||||
|
console::uart_ns16650a::{Ns16550aAccess, Ns16550aUart},
|
||||||
|
mm::VmReader,
|
||||||
|
sync::{LocalIrqDisabled, SpinLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A UART console.
|
||||||
|
pub(super) struct UartConsole<U: Uart> {
|
||||||
|
uart: U,
|
||||||
|
callbacks: SpinLock<Vec<&'static ConsoleCallback>, LocalIrqDisabled>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Uart> Debug for UartConsole<U> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("UartConsole").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Uart> UartConsole<U> {
|
||||||
|
/// Creates a new UART console.
|
||||||
|
pub(super) fn new(uart: U) -> Arc<Self> {
|
||||||
|
Arc::new(Self {
|
||||||
|
uart,
|
||||||
|
callbacks: SpinLock::new(Vec::new()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the UART instance.
|
||||||
|
#[cfg_attr(not(target_arch = "riscv64"), expect(dead_code))]
|
||||||
|
pub(super) fn uart(&self) -> &U {
|
||||||
|
&self.uart
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triggers the registered input callbacks.
|
||||||
|
pub(super) fn trigger_input_callbacks(&self) {
|
||||||
|
let mut buf = [0; 16];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let num_rcv = self.uart.recv(&mut buf);
|
||||||
|
if num_rcv == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let reader = VmReader::from(&buf[..num_rcv]);
|
||||||
|
for callback in self.callbacks.lock().iter() {
|
||||||
|
(callback)(reader.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if num_rcv < buf.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Uart + Send + Sync + 'static> AnyConsoleDevice for UartConsole<U> {
|
||||||
|
fn send(&self, buf: &[u8]) {
|
||||||
|
self.uart.send(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_callback(&self, callback: &'static ConsoleCallback) {
|
||||||
|
self.callbacks.lock().push(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait that abstracts UART devices.
|
||||||
|
pub(super) trait Uart {
|
||||||
|
/// Sends a sequence of bytes to UART.
|
||||||
|
fn send(&self, buf: &[u8]);
|
||||||
|
|
||||||
|
/// Receives a sequence of bytes from UART and returns the number of received bytes.
|
||||||
|
#[must_use]
|
||||||
|
fn recv(&self, buf: &mut [u8]) -> usize;
|
||||||
|
|
||||||
|
/// Flushes the received buffer.
|
||||||
|
///
|
||||||
|
/// This method should be called after setting up the IRQ handlers to ensure new received data
|
||||||
|
/// will trigger IRQs.
|
||||||
|
fn flush(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Ns16550aAccess> Uart for SpinLock<Ns16550aUart<A>, LocalIrqDisabled> {
|
||||||
|
fn send(&self, buf: &[u8]) {
|
||||||
|
let mut uart = self.lock();
|
||||||
|
|
||||||
|
for byte in buf {
|
||||||
|
// TODO: This is termios-specific behavior and should be part of the TTY implementation
|
||||||
|
// instead of the serial console implementation. See the ONLCR flag for more details.
|
||||||
|
if *byte == b'\n' {
|
||||||
|
uart.send(b'\r');
|
||||||
|
}
|
||||||
|
uart.send(*byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recv(&self, buf: &mut [u8]) -> usize {
|
||||||
|
let mut uart = self.lock();
|
||||||
|
|
||||||
|
for (i, byte) in buf.iter_mut().enumerate() {
|
||||||
|
let Some(recv_byte) = uart.recv() else {
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
*byte = recv_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {
|
||||||
|
let mut uart = self.lock();
|
||||||
|
|
||||||
|
while uart.recv().is_some() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inherit_methods(from = "(**self)")]
|
||||||
|
impl<A: Ns16550aAccess> Uart for &SpinLock<Ns16550aUart<A>, LocalIrqDisabled> {
|
||||||
|
fn send(&self, buf: &[u8]);
|
||||||
|
fn recv(&self, buf: &mut [u8]) -> usize;
|
||||||
|
fn flush(&self);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! Universal asynchronous receiver-transmitter (UART).
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use component::{ComponentInitError, init_component};
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "x86_64", path = "arch/x86/mod.rs")]
|
||||||
|
#[cfg_attr(target_arch = "riscv64", path = "arch/riscv/mod.rs")]
|
||||||
|
#[cfg_attr(target_arch = "loongarch64", path = "arch/loongarch/mod.rs")]
|
||||||
|
mod arch;
|
||||||
|
|
||||||
|
mod console;
|
||||||
|
|
||||||
|
pub const CONSOLE_NAME: &str = "Uart-Console";
|
||||||
|
|
||||||
|
#[init_component]
|
||||||
|
fn init() -> Result<(), ComponentInitError> {
|
||||||
|
arch::init();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ mod io;
|
||||||
pub(crate) mod iommu;
|
pub(crate) mod iommu;
|
||||||
pub(crate) mod irq;
|
pub(crate) mod irq;
|
||||||
pub(crate) mod mm;
|
pub(crate) mod mm;
|
||||||
pub(crate) mod serial;
|
pub mod serial;
|
||||||
pub(crate) mod task;
|
pub(crate) mod task;
|
||||||
mod timer;
|
mod timer;
|
||||||
pub mod trap;
|
pub mod trap;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub mod irq;
|
||||||
pub mod kernel;
|
pub mod kernel;
|
||||||
pub(crate) mod mm;
|
pub(crate) mod mm;
|
||||||
mod power;
|
mod power;
|
||||||
pub(crate) mod serial;
|
pub mod serial;
|
||||||
pub(crate) mod task;
|
pub(crate) mod task;
|
||||||
mod timer;
|
mod timer;
|
||||||
pub mod trap;
|
pub mod trap;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue