Refactor console

This commit is contained in:
Yuke Peng
2023-09-08 11:54:01 +08:00
committed by Tate, Hongliang Tian
parent 0a17d90532
commit eeac55e2e5
8 changed files with 191 additions and 142 deletions

1
Cargo.lock generated
View File

@ -629,6 +629,7 @@ dependencies = [
"acpi",
"align_ext",
"aml",
"bit_field",
"bitflags 1.3.2",
"bitvec",
"buddy_system_allocator",

View File

@ -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"

View 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
}
}

View File

@ -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>,
}
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,
}
}
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() {
pub fn init(&self) {
// Disable interrupts
SERIAL_INT_EN.write(0x00);
self.int_en.write(0x00);
// Enable DLAB
SERIAL_LINE_CTRL.write(0x80);
self.line_ctrl.write(0x80);
// Set maximum speed to 38400 bps by configuring DLL and DLM
SERIAL_DATA.write(0x03);
SERIAL_INT_EN.write(0x00);
self.data.write(0x03);
self.int_en.write(0x00);
// Disable DLAB and set data word length to 8 bits
SERIAL_LINE_CTRL.write(0x03);
self.line_ctrl.write(0x03);
// Enable FIFO, clear TX/RX queues and
// set interrupt watermark at 14 bytes
SERIAL_FIFO_CTRL.write(0xC7);
self.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);
self.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);
}
}
}
/// 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
self.int_en.write(0x01);
}
}

View File

@ -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();

View 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)+)?))
}
}

View File

@ -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;

View File

@ -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();