x86: make com-port usable as a serial console
This commit is contained in:
parent
1ad90ce181
commit
0b2822cea1
@ -5,6 +5,7 @@ use abi::error::Error;
|
|||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use boot::multiboot::MultibootInfo;
|
use boot::multiboot::MultibootInfo;
|
||||||
use device_api::{
|
use device_api::{
|
||||||
|
device::Device,
|
||||||
interrupt::{IpiDeliveryTarget, IpiMessage, Irq},
|
interrupt::{IpiDeliveryTarget, IpiMessage, Irq},
|
||||||
ResetDevice,
|
ResetDevice,
|
||||||
};
|
};
|
||||||
@ -159,14 +160,9 @@ impl I686 {
|
|||||||
phys::init_from_iter(multiboot_info.memory_map_iter(), |_, _, _| Ok(()))?;
|
phys::init_from_iter(multiboot_info.memory_map_iter(), |_, _, _| Ok(()))?;
|
||||||
|
|
||||||
debug::init();
|
debug::init();
|
||||||
|
devfs::init();
|
||||||
|
|
||||||
let com1_3 = Arc::new(ComPort::new(
|
let com1_3 = ComPort::setup(0x3F8, 0x3E8, Irq::External(ISA_IRQ_OFFSET + 4))?;
|
||||||
0x3F8,
|
|
||||||
0x3E8,
|
|
||||||
Irq::External(ISA_IRQ_OFFSET + 4),
|
|
||||||
));
|
|
||||||
|
|
||||||
debug::add_sink(com1_3.port_a().clone(), LogLevel::Debug);
|
|
||||||
|
|
||||||
unsafe { init_gdt() };
|
unsafe { init_gdt() };
|
||||||
unsafe { exception::init_exceptions() };
|
unsafe { exception::init_exceptions() };
|
||||||
@ -190,7 +186,6 @@ impl I686 {
|
|||||||
Cpu::init_local(Some(0), cpu_data);
|
Cpu::init_local(Some(0), cpu_data);
|
||||||
|
|
||||||
runtime::init_task_queue();
|
runtime::init_task_queue();
|
||||||
devfs::init();
|
|
||||||
|
|
||||||
// Register the PCI drivers
|
// Register the PCI drivers
|
||||||
// TODO make this implicit init
|
// TODO make this implicit init
|
||||||
@ -243,6 +238,9 @@ impl I686 {
|
|||||||
if let Err(error) = Rtc::setup() {
|
if let Err(error) = Rtc::setup() {
|
||||||
log::error!("RTC init error: {error:?}");
|
log::error!("RTC init error: {error:?}");
|
||||||
}
|
}
|
||||||
|
if let Err(error) = unsafe { com1_3.port_a().clone().init_irq() } {
|
||||||
|
log::error!("COM port IRQ init error: {error:?}");
|
||||||
|
}
|
||||||
|
|
||||||
// Setup text framebuffer
|
// Setup text framebuffer
|
||||||
// TODO check if video mode is set from boot info
|
// TODO check if video mode is set from boot info
|
||||||
|
@ -16,6 +16,10 @@ pub trait IoPortAccess<T> {
|
|||||||
fn read(&self) -> T;
|
fn read(&self) -> T;
|
||||||
/// Writes a value to the port
|
/// Writes a value to the port
|
||||||
fn write(&self, value: T);
|
fn write(&self, value: T);
|
||||||
|
|
||||||
|
fn modify<F: FnOnce(T) -> T>(&self, f: F) {
|
||||||
|
self.write(f(self.read()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IoPort<T> {
|
impl<T> IoPort<T> {
|
||||||
|
@ -1,21 +1,35 @@
|
|||||||
//! Driver for x86 COM ports
|
//! Driver for x86 COM ports
|
||||||
use abi::error::Error;
|
use abi::{error::Error, io::TerminalOptions};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use device_api::{device::Device, interrupt::Irq, serial::SerialDevice};
|
use device_api::{
|
||||||
use libk::debug::DebugSink;
|
device::Device,
|
||||||
|
interrupt::{InterruptHandler, Irq},
|
||||||
|
};
|
||||||
|
use libk::{
|
||||||
|
debug::{DebugSink, LogLevel},
|
||||||
|
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||||
|
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||||
|
};
|
||||||
use libk_util::sync::IrqSafeSpinlock;
|
use libk_util::sync::IrqSafeSpinlock;
|
||||||
|
|
||||||
use crate::arch::x86::intrinsics::{IoPort, IoPortAccess};
|
use crate::arch::x86::intrinsics::{IoPort, IoPortAccess};
|
||||||
|
|
||||||
// Single port
|
// Single port
|
||||||
struct Inner {
|
struct Regs {
|
||||||
dr: IoPort<u8>,
|
dr: IoPort<u8>,
|
||||||
lsr: IoPort<u8>,
|
lsr: IoPort<u8>,
|
||||||
|
ier: IoPort<u8>,
|
||||||
|
isr: IoPort<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PortInner {
|
||||||
|
regs: IrqSafeSpinlock<Regs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Single port of the COM port pair
|
/// Single port of the COM port pair
|
||||||
pub struct Port {
|
pub struct Port {
|
||||||
inner: IrqSafeSpinlock<Inner>,
|
terminal: Arc<Terminal<PortInner>>,
|
||||||
|
irq: Irq,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// COM port pair
|
/// COM port pair
|
||||||
@ -23,28 +37,9 @@ pub struct Port {
|
|||||||
pub struct ComPort {
|
pub struct ComPort {
|
||||||
port_a: Arc<Port>,
|
port_a: Arc<Port>,
|
||||||
port_b: Arc<Port>,
|
port_b: Arc<Port>,
|
||||||
irq: Irq,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugSink for Port {
|
impl Regs {
|
||||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
|
||||||
self.send_byte(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn puts(&self, s: &str) -> Result<(), Error> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
for b in s.bytes() {
|
|
||||||
inner.write(b)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supports_control_sequences(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inner {
|
|
||||||
fn write(&mut self, byte: u8) -> Result<(), Error> {
|
fn write(&mut self, byte: u8) -> Result<(), Error> {
|
||||||
while self.lsr.read() & Port::LSR_THRE == 0 {
|
while self.lsr.read() & Port::LSR_THRE == 0 {
|
||||||
core::hint::spin_loop();
|
core::hint::spin_loop();
|
||||||
@ -55,48 +50,144 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerialDevice for Port {
|
impl PortInner {
|
||||||
fn send_byte(&self, byte: u8) -> Result<(), Error> {
|
fn handle_irq(&self) -> Option<u8> {
|
||||||
let mut inner = self.inner.lock();
|
let (status, value) = {
|
||||||
inner.write(byte)
|
let inner = self.regs.lock();
|
||||||
|
let status = inner.isr.read();
|
||||||
|
let value = inner.dr.read();
|
||||||
|
(status, value)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status & Port::ISR_IRQ_MASK != 0 {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_terminal(&self) -> bool {
|
impl TerminalOutput for PortInner {
|
||||||
|
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||||
|
self.regs.lock().write(byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||||
|
let mut regs = self.regs.lock();
|
||||||
|
for &b in bytes {
|
||||||
|
regs.write(b)?;
|
||||||
|
}
|
||||||
|
Ok(bytes.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebugSink for Port {
|
||||||
|
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||||
|
self.terminal.putc_to_output(c).ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn puts(&self, s: &str) -> Result<(), Error> {
|
||||||
|
self.terminal.write_to_output(s.as_bytes()).ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supports_control_sequences(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl SerialDevice for Port {
|
||||||
|
// fn send_byte(&self, byte: u8) -> Result<(), Error> {
|
||||||
|
// let mut inner = self.inner.lock();
|
||||||
|
// inner.write(byte)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn is_terminal(&self) -> bool {
|
||||||
|
// false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl InterruptHandler for Port {
|
||||||
|
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
|
||||||
|
let inner = self.terminal.output();
|
||||||
|
if let Some(byte) = inner.handle_irq() {
|
||||||
|
self.terminal.write_to_input(byte);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display_name(&self) -> &str {
|
||||||
|
"x86 COM port IRQ"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Device for Port {
|
impl Device for Port {
|
||||||
fn display_name(&self) -> &'static str {
|
fn display_name(&self) -> &'static str {
|
||||||
"COM port"
|
"COM port"
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
||||||
|
DEVICE_REGISTRY
|
||||||
|
.serial_terminal
|
||||||
|
.register(self.terminal.clone(), Some((self.clone(), LogLevel::Debug)))
|
||||||
|
.ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||||
|
let intc = external_interrupt_controller()?;
|
||||||
|
|
||||||
|
// TODO check that the same IRQ is not bound already for another port
|
||||||
|
intc.register_irq(self.irq, Default::default(), self.clone())?;
|
||||||
|
intc.enable_irq(self.irq)?;
|
||||||
|
|
||||||
|
let regs = self.terminal.output().regs.lock();
|
||||||
|
|
||||||
|
regs.ier.modify(|v| v | Self::IER_RXDA);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Port {
|
impl Port {
|
||||||
const LSR_THRE: u8 = 1 << 5;
|
const LSR_THRE: u8 = 1 << 5;
|
||||||
|
const IER_RXDA: u8 = 1 << 0;
|
||||||
|
const ISR_IRQ_MASK: u8 = 3 << 1;
|
||||||
|
|
||||||
const fn new(base: u16) -> Self {
|
fn new(base: u16, irq: Irq) -> Result<Self, Error> {
|
||||||
Self {
|
let input = TerminalInput::with_capacity(64)?;
|
||||||
inner: IrqSafeSpinlock::new(Inner {
|
let output = PortInner {
|
||||||
|
regs: IrqSafeSpinlock::new(Regs {
|
||||||
dr: IoPort::new(base),
|
dr: IoPort::new(base),
|
||||||
lsr: IoPort::new(base + 5),
|
lsr: IoPort::new(base + 5),
|
||||||
|
ier: IoPort::new(base + 1),
|
||||||
|
isr: IoPort::new(base + 2),
|
||||||
}),
|
}),
|
||||||
}
|
};
|
||||||
|
let terminal = Terminal::from_parts(TerminalOptions::const_default(), input, output);
|
||||||
|
Ok(Self {
|
||||||
|
terminal: Arc::new(terminal),
|
||||||
|
irq,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComPort {
|
impl ComPort {
|
||||||
/// Constructs a COM port pair
|
/// Constructs a COM port pair
|
||||||
pub fn new(port_a: u16, port_b: u16, irq: Irq) -> Self {
|
fn new(port_a: u16, port_b: u16, irq: Irq) -> Result<Self, Error> {
|
||||||
Self {
|
Ok(Self {
|
||||||
port_a: Arc::new(Port::new(port_a)),
|
port_a: Arc::new(Port::new(port_a, irq)?),
|
||||||
port_b: Arc::new(Port::new(port_b)),
|
port_b: Arc::new(Port::new(port_b, irq)?),
|
||||||
irq,
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup(port_a: u16, port_b: u16, irq: Irq) -> Result<Arc<Self>, Error> {
|
||||||
|
let this = Arc::new(Self::new(port_a, port_b, irq)?);
|
||||||
|
unsafe { this.port_a().clone().init() }?;
|
||||||
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the A port of this COM pair
|
/// Returns a reference to the A port of this COM pair
|
||||||
|
@ -6,7 +6,7 @@ use abi::error::Error;
|
|||||||
use acpi::{AcpiAllocator, AcpiHandlerImpl};
|
use acpi::{AcpiAllocator, AcpiHandlerImpl};
|
||||||
use alloc::{boxed::Box, sync::Arc};
|
use alloc::{boxed::Box, sync::Arc};
|
||||||
use apic::{ioapic::IoApic, local::LocalApic};
|
use apic::{ioapic::IoApic, local::LocalApic};
|
||||||
use device_api::interrupt::Irq;
|
use device_api::{device::Device, interrupt::Irq};
|
||||||
use git_version::git_version;
|
use git_version::git_version;
|
||||||
use kernel_arch_x86::{
|
use kernel_arch_x86::{
|
||||||
cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures},
|
cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures},
|
||||||
@ -336,13 +336,7 @@ impl X86_64 {
|
|||||||
|
|
||||||
debug::init();
|
debug::init();
|
||||||
|
|
||||||
let com1_3 = Arc::new(ComPort::new(
|
let com1_3 = ComPort::setup(0x3F8, 0x3E8, Irq::External(ISA_IRQ_OFFSET + 4))?;
|
||||||
0x3F8,
|
|
||||||
0x3E8,
|
|
||||||
Irq::External(ISA_IRQ_OFFSET + 4),
|
|
||||||
));
|
|
||||||
|
|
||||||
debug::add_sink(com1_3.port_a().clone(), LogLevel::Debug);
|
|
||||||
// TODO register platform devices
|
// TODO register platform devices
|
||||||
|
|
||||||
DEVICE_REGISTRY.display.set_callback(|device, node| {
|
DEVICE_REGISTRY.display.set_callback(|device, node| {
|
||||||
@ -391,6 +385,9 @@ impl X86_64 {
|
|||||||
if let Err(error) = Rtc::setup() {
|
if let Err(error) = Rtc::setup() {
|
||||||
log::error!("RTC init error: {error:?}");
|
log::error!("RTC init error: {error:?}");
|
||||||
}
|
}
|
||||||
|
if let Err(error) = unsafe { com1_3.port_a().clone().init_irq() } {
|
||||||
|
log::error!("COM port IRQ init error: {error:?}");
|
||||||
|
}
|
||||||
|
|
||||||
PciBusManager::setup_bus_devices()?;
|
PciBusManager::setup_bus_devices()?;
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,4 @@ init:1:wait:/sbin/rc default
|
|||||||
logd:1:once:/sbin/logd
|
logd:1:once:/sbin/logd
|
||||||
|
|
||||||
user:1:once:/sbin/login /dev/tty0
|
user:1:once:/sbin/login /dev/tty0
|
||||||
|
user:1:once:/sbin/login /dev/ttyS0
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
init:1:wait:/sbin/rc default
|
init:1:wait:/sbin/rc default
|
||||||
logd:1:once:/sbin/logd
|
logd:1:once:/sbin/logd
|
||||||
|
|
||||||
|
user:1:once:/sbin/login /dev/ttyS0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user