mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
Refactor console
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
0a17d90532
commit
eeac55e2e5
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -629,6 +629,7 @@ dependencies = [
|
||||
"acpi",
|
||||
"align_ext",
|
||||
"aml",
|
||||
"bit_field",
|
||||
"bitflags 1.3.2",
|
||||
"bitvec",
|
||||
"buddy_system_allocator",
|
||||
|
@ -21,6 +21,7 @@ inherit-methods-macro = { git = "https://github.com/jinzhao-dev/inherit-methods-
|
||||
tdx-guest = { path = "../libs/tdx-guest", optional = true }
|
||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
static_assertions = "1.1.0"
|
||||
bit_field = "0.10.1"
|
||||
|
||||
[target.x86_64-custom.dependencies]
|
||||
x86_64 = "0.14.2"
|
||||
|
112
framework/jinux-frame/src/arch/x86/console.rs
Normal file
112
framework/jinux-frame/src/arch/x86/console.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use core::fmt::Write;
|
||||
|
||||
use crate::sync::SpinLock;
|
||||
use crate::trap::IrqLine;
|
||||
use alloc::fmt;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use log::debug;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use super::device::serial::SerialPort;
|
||||
|
||||
#[inline]
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
pub type InputCallback = dyn Fn(u8) + Send + Sync + 'static;
|
||||
|
||||
pub fn register_console_input_callback(f: &'static InputCallback) {
|
||||
SERIAL_INPUT_CALLBACKS.lock_irq_disabled().push(Arc::new(f));
|
||||
}
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for &c in s.as_bytes() {
|
||||
send(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct LineSts: u8 {
|
||||
const INPUT_FULL = 1;
|
||||
const OUTPUT_EMPTY = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
static CONSOLE_COM1_PORT: SerialPort = unsafe { SerialPort::new(0x3F8) };
|
||||
|
||||
static CONSOLE_IRQ_CALLBACK: Once<SpinLock<IrqLine>> = Once::new();
|
||||
static SERIAL_INPUT_CALLBACKS: SpinLock<Vec<Arc<InputCallback>>> = SpinLock::new(Vec::new());
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub(crate) fn init() {
|
||||
CONSOLE_COM1_PORT.init();
|
||||
}
|
||||
|
||||
pub(crate) fn callback_init() {
|
||||
let mut irq = crate::arch::x86::kernel::pic::allocate_irq(4).unwrap();
|
||||
irq.on_active(handle_serial_input);
|
||||
CONSOLE_IRQ_CALLBACK.call_once(|| SpinLock::new(irq));
|
||||
}
|
||||
|
||||
pub(crate) fn register_console_callback<F>(callback: F)
|
||||
where
|
||||
F: Fn(&TrapFrame) + Sync + Send + 'static,
|
||||
{
|
||||
CONSOLE_IRQ_CALLBACK
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock_irq_disabled()
|
||||
.on_active(callback);
|
||||
}
|
||||
|
||||
fn handle_serial_input(trap_frame: &TrapFrame) {
|
||||
// debug!("keyboard interrupt was met");
|
||||
let lock = if let Some(lock) = SERIAL_INPUT_CALLBACKS.try_lock() {
|
||||
lock
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let received_char = receive_char().unwrap();
|
||||
debug!("receive char = {:?}", received_char);
|
||||
for callback in lock.iter() {
|
||||
callback(received_char);
|
||||
}
|
||||
}
|
||||
|
||||
fn line_sts() -> LineSts {
|
||||
LineSts::from_bits_truncate(CONSOLE_COM1_PORT.line_status.read())
|
||||
}
|
||||
|
||||
/// Sends a byte on the serial port.
|
||||
pub fn send(data: u8) {
|
||||
match data {
|
||||
8 | 0x7F => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.data.write(8);
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.data.write(b' ');
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.data.write(8);
|
||||
}
|
||||
_ => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
CONSOLE_COM1_PORT.data.write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a byte on the serial port. non-blocking
|
||||
pub fn receive_char() -> Option<u8> {
|
||||
if line_sts().contains(LineSts::INPUT_FULL) {
|
||||
Some(CONSOLE_COM1_PORT.data.read())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
@ -1,118 +1,63 @@
|
||||
//! A port-mapped UART. Copied from uart_16550.
|
||||
|
||||
use crate::arch::x86::device::io_port::{IoPort, ReadWriteAccess, WriteOnlyAccess};
|
||||
use crate::sync::SpinLock;
|
||||
use crate::trap::IrqLine;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use log::debug;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct LineSts: u8 {
|
||||
const INPUT_FULL = 1;
|
||||
const OUTPUT_EMPTY = 1 << 5;
|
||||
}
|
||||
/// A serial port.
|
||||
///
|
||||
/// Serial ports are a legacy communications port common on IBM-PC compatible computers.
|
||||
/// Ref: https://wiki.osdev.org/Serial_Ports
|
||||
pub struct SerialPort {
|
||||
pub data: IoPort<u8, ReadWriteAccess>,
|
||||
pub int_en: IoPort<u8, WriteOnlyAccess>,
|
||||
pub fifo_ctrl: IoPort<u8, WriteOnlyAccess>,
|
||||
pub line_ctrl: IoPort<u8, WriteOnlyAccess>,
|
||||
pub modem_ctrl: IoPort<u8, WriteOnlyAccess>,
|
||||
pub line_status: IoPort<u8, ReadWriteAccess>,
|
||||
pub modem_status: IoPort<u8, ReadWriteAccess>,
|
||||
}
|
||||
|
||||
const SERIAL_DATA_PORT: u16 = 0x3F8;
|
||||
|
||||
static SERIAL_DATA: IoPort<u8, ReadWriteAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT) };
|
||||
static SERIAL_INT_EN: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 1) };
|
||||
static SERIAL_FIFO_CTRL: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 2) };
|
||||
static SERIAL_LINE_CTRL: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 3) };
|
||||
static SERIAL_MODEM_CTRL: IoPort<u8, WriteOnlyAccess> =
|
||||
unsafe { IoPort::new(SERIAL_DATA_PORT + 4) };
|
||||
static SERIAL_LINE_STS: IoPort<u8, ReadWriteAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 5) };
|
||||
|
||||
static CONSOLE_IRQ_CALLBACK: Once<SpinLock<IrqLine>> = Once::new();
|
||||
#[allow(clippy::type_complexity)]
|
||||
static SERIAL_INPUT_CALLBACKS: SpinLock<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
|
||||
SpinLock::new(Vec::new());
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub(crate) fn init() {
|
||||
// Disable interrupts
|
||||
SERIAL_INT_EN.write(0x00);
|
||||
// Enable DLAB
|
||||
SERIAL_LINE_CTRL.write(0x80);
|
||||
// Set maximum speed to 38400 bps by configuring DLL and DLM
|
||||
SERIAL_DATA.write(0x03);
|
||||
SERIAL_INT_EN.write(0x00);
|
||||
// Disable DLAB and set data word length to 8 bits
|
||||
SERIAL_LINE_CTRL.write(0x03);
|
||||
// Enable FIFO, clear TX/RX queues and
|
||||
// set interrupt watermark at 14 bytes
|
||||
SERIAL_FIFO_CTRL.write(0xC7);
|
||||
// Mark data terminal ready, signal request to send
|
||||
// and enable auxilliary output #2 (used as interrupt line for CPU)
|
||||
SERIAL_MODEM_CTRL.write(0x0B);
|
||||
// Enable interrupts
|
||||
SERIAL_INT_EN.write(0x01);
|
||||
}
|
||||
|
||||
pub fn register_serial_input_callback(f: impl Fn(u8) + Send + Sync + 'static) {
|
||||
SERIAL_INPUT_CALLBACKS.lock_irq_disabled().push(Arc::new(f));
|
||||
}
|
||||
|
||||
pub(crate) fn callback_init() {
|
||||
let mut irq = crate::arch::x86::kernel::pic::allocate_irq(4).unwrap();
|
||||
irq.on_active(handle_serial_input);
|
||||
CONSOLE_IRQ_CALLBACK.call_once(|| SpinLock::new(irq));
|
||||
}
|
||||
|
||||
pub(crate) fn register_serial_input_irq_handler<F>(callback: F)
|
||||
where
|
||||
F: Fn(&TrapFrame) + Sync + Send + 'static,
|
||||
{
|
||||
CONSOLE_IRQ_CALLBACK
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock_irq_disabled()
|
||||
.on_active(callback);
|
||||
}
|
||||
|
||||
fn handle_serial_input(trap_frame: &TrapFrame) {
|
||||
// debug!("keyboard interrupt was met");
|
||||
let lock = if let Some(lock) = SERIAL_INPUT_CALLBACKS.try_lock() {
|
||||
lock
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let received_char = receive_char().unwrap();
|
||||
debug!("receive char = {:?}", received_char);
|
||||
for callback in lock.iter() {
|
||||
callback(received_char);
|
||||
}
|
||||
}
|
||||
|
||||
fn line_sts() -> LineSts {
|
||||
LineSts::from_bits_truncate(SERIAL_LINE_STS.read())
|
||||
}
|
||||
|
||||
/// Sends a byte on the serial port.
|
||||
pub fn send(data: u8) {
|
||||
match data {
|
||||
8 | 0x7F => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
SERIAL_DATA.write(8);
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
SERIAL_DATA.write(b' ');
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
SERIAL_DATA.write(8);
|
||||
}
|
||||
_ => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
SERIAL_DATA.write(data);
|
||||
impl SerialPort {
|
||||
/// Create a serial port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// User must ensure the `port` is valid serial port.
|
||||
pub const unsafe fn new(port: u16) -> Self {
|
||||
let data = IoPort::new(port);
|
||||
let int_en = IoPort::new(port + 1);
|
||||
let fifo_ctrl = IoPort::new(port + 2);
|
||||
let line_ctrl = IoPort::new(port + 3);
|
||||
let modem_ctrl = IoPort::new(port + 4);
|
||||
let line_status = IoPort::new(port + 5);
|
||||
let modem_status = IoPort::new(port + 6);
|
||||
Self {
|
||||
data,
|
||||
int_en,
|
||||
fifo_ctrl,
|
||||
line_ctrl,
|
||||
modem_ctrl,
|
||||
line_status,
|
||||
modem_status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a byte on the serial port. non-blocking
|
||||
pub fn receive_char() -> Option<u8> {
|
||||
if line_sts().contains(LineSts::INPUT_FULL) {
|
||||
Some(SERIAL_DATA.read())
|
||||
} else {
|
||||
None
|
||||
pub fn init(&self) {
|
||||
// Disable interrupts
|
||||
self.int_en.write(0x00);
|
||||
// Enable DLAB
|
||||
self.line_ctrl.write(0x80);
|
||||
// Set maximum speed to 38400 bps by configuring DLL and DLM
|
||||
self.data.write(0x03);
|
||||
self.int_en.write(0x00);
|
||||
// Disable DLAB and set data word length to 8 bits
|
||||
self.line_ctrl.write(0x03);
|
||||
// Enable FIFO, clear TX/RX queues and
|
||||
// set interrupt watermark at 14 bytes
|
||||
self.fifo_ctrl.write(0xC7);
|
||||
// Mark data terminal ready, signal request to send
|
||||
// and enable auxilliary output #2 (used as interrupt line for CPU)
|
||||
self.modem_ctrl.write(0x0B);
|
||||
// Enable interrupts
|
||||
self.int_en.write(0x01);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod boot;
|
||||
pub mod console;
|
||||
pub(crate) mod cpu;
|
||||
pub mod device;
|
||||
pub mod iommu;
|
||||
@ -10,20 +11,17 @@ pub(crate) mod pci;
|
||||
pub(crate) mod tdx_guest;
|
||||
pub(crate) mod timer;
|
||||
|
||||
use alloc::fmt;
|
||||
use core::fmt::Write;
|
||||
use kernel::apic::ioapic;
|
||||
use log::{info, warn};
|
||||
|
||||
pub(crate) fn before_all_init() {
|
||||
enable_common_cpu_features();
|
||||
device::serial::init();
|
||||
console::init();
|
||||
}
|
||||
|
||||
pub(crate) fn after_all_init() {
|
||||
irq::init();
|
||||
mm::init();
|
||||
device::serial::callback_init();
|
||||
kernel::acpi::init();
|
||||
match kernel::apic::init() {
|
||||
Ok(_) => {
|
||||
@ -34,6 +32,7 @@ pub(crate) fn after_all_init() {
|
||||
kernel::pic::enable();
|
||||
}
|
||||
}
|
||||
console::callback_init();
|
||||
timer::init();
|
||||
match iommu::init() {
|
||||
Ok(_) => {}
|
||||
@ -50,35 +49,6 @@ pub(crate) fn interrupts_ack() {
|
||||
}
|
||||
}
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for &c in s.as_bytes() {
|
||||
device::serial::send(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::arch::x86::print(format_args!($fmt $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::arch::x86::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_common_cpu_features() {
|
||||
use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags};
|
||||
let mut cr4 = x86_64::registers::control::Cr4::read();
|
||||
|
19
framework/jinux-frame/src/console.rs
Normal file
19
framework/jinux-frame/src/console.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use core::fmt::Arguments;
|
||||
|
||||
pub fn print(args: Arguments) {
|
||||
crate::arch::console::print(args);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!($fmt $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ pub mod arch;
|
||||
pub mod boot;
|
||||
pub mod bus;
|
||||
pub mod config;
|
||||
pub mod console;
|
||||
pub mod cpu;
|
||||
mod error;
|
||||
pub mod io_mem;
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use jinux_frame::arch::x86::device::serial::register_serial_input_callback;
|
||||
pub use jinux_frame::arch::console::register_console_input_callback;
|
||||
use spin::Once;
|
||||
|
||||
use crate::{
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
pub static TTY_DRIVER: Once<Arc<TtyDriver>> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
register_serial_input_callback(serial_input_callback);
|
||||
register_console_input_callback(&serial_input_callback);
|
||||
let tty_driver = Arc::new(TtyDriver::new());
|
||||
// FIXME: install n_tty into tty_driver?
|
||||
let n_tty = get_n_tty();
|
||||
|
Reference in New Issue
Block a user