Refactor IoPort

This commit is contained in:
Yuke Peng 2023-03-21 21:09:21 -07:00 committed by Tate, Hongliang Tian
parent ebfb199512
commit 8c7b5d84d1
7 changed files with 109 additions and 140 deletions

View File

@ -1,60 +1,55 @@
use spin::Mutex;
use x86_64::{
instructions::port::Port,
structures::port::{PortRead, PortWrite},
use core::marker::PhantomData;
pub use x86_64::instructions::port::{
PortReadAccess as IoPortReadAccess, PortWriteAccess as IoPortWriteAccess, ReadOnlyAccess,
ReadWriteAccess, WriteOnlyAccess,
};
pub use x86_64::structures::port::{PortRead, PortWrite};
/// An I/O port, representing a specific address in the I/O address of x86.
///
/// The following code shows and example to read and write u32 value to an I/O port:
///
/// ```rust
/// static PORT: IoPort<u32> = IoPort::new(0x12);
/// static PORT: IoPort<u32, ReadWriteAccess> = unsafe { IoPort::new(0x12) };
///
/// fn port_value_increase(){
/// PORT.write(PORT.read() + 1)
/// }
/// ```
///
pub struct IoPort<T> {
addr: u16,
port: Mutex<Port<T>>,
pub struct IoPort<T, A> {
port: u16,
value_marker: PhantomData<T>,
access_marker: PhantomData<A>,
}
impl<T> IoPort<T> {
impl<T, A> IoPort<T, A> {
/// Create an I/O port.
///
/// # Safety
///
/// This function is marked unsafe as creating an I/O port is considered
/// a privileged operation.
pub const unsafe fn new(addr: u16) -> Self {
pub const unsafe fn new(port: u16) -> Self {
Self {
addr,
port: Mutex::new(Port::new(addr)),
port: port,
value_marker: PhantomData,
access_marker: PhantomData,
}
}
}
impl<T> IoPort<T> {
/// Get the address of this I/O port.
pub fn addr(&self) -> u16 {
self.addr
}
}
impl<T: PortRead> IoPort<T> {
/// Reads from the port.
impl<T: PortRead, A: IoPortReadAccess> IoPort<T, A> {
#[inline]
pub fn read(&self) -> T {
unsafe { (*self.port.lock()).read() }
unsafe { PortRead::read_from_port(self.port) }
}
}
impl<T: PortWrite> IoPort<T> {
/// Writes to the port.
pub fn write(&self, val: T) {
unsafe {
self.port.lock().write(val);
}
impl<T: PortWrite, A: IoPortWriteAccess> IoPort<T, A> {
#[inline]
pub fn write(&self, value: T) {
unsafe { PortWrite::write_to_port(self.port, value) }
}
}

View File

@ -1,12 +1,10 @@
//! Device-related APIs.
pub mod framebuffer;
mod io_port;
pub mod framebuffer;
pub mod io_port;
pub mod pci;
pub mod serial;
pub use self::io_port::IoPort;
/// Call after the memory allocator init
pub(crate) fn init() {
framebuffer::init();

View File

@ -1,6 +1,7 @@
//! PCI bus io port
use super::io_port::IoPort;
use super::io_port::{ReadWriteAccess, WriteOnlyAccess};
pub static PCI_ADDRESS_PORT: IoPort<u32> = unsafe { IoPort::new(0x0CF8) };
pub static PCI_DATA_PORT: IoPort<u32> = unsafe { IoPort::new(0x0CFC) };
pub static PCI_ADDRESS_PORT: IoPort<u32, WriteOnlyAccess> = unsafe { IoPort::new(0x0CF8) };
pub static PCI_DATA_PORT: IoPort<u32, ReadWriteAccess> = unsafe { IoPort::new(0x0CFC) };

View File

@ -1,12 +1,13 @@
//! A port-mapped UART. Copied from uart_16550.
use super::io_port::{IoPort, ReadWriteAccess, WriteOnlyAccess};
use alloc::{sync::Arc, vec::Vec};
use core::fmt::{self, Write};
use log::debug;
use spin::{Mutex, Once};
use trapframe::TrapFrame;
use x86_64::instructions::port::{Port, PortWriteOnly};
use crate::{driver::pic_allocate_irq, trap::IrqAllocateHandle};
use core::fmt::{self, Write};
use trapframe::TrapFrame;
bitflags::bitflags! {
struct LineSts: u8 {
@ -17,16 +18,13 @@ bitflags::bitflags! {
const SERIAL_DATA_PORT: u16 = 0x3F8;
static SERIAL_DATA: Mutex<Port<u8>> = Mutex::new(Port::new(SERIAL_DATA_PORT));
static SERIAL_INT_EN: Mutex<PortWriteOnly<u8>> =
Mutex::new(PortWriteOnly::new(SERIAL_DATA_PORT + 1));
static SERIAL_FIFO_CTRL: Mutex<PortWriteOnly<u8>> =
Mutex::new(PortWriteOnly::new(SERIAL_DATA_PORT + 2));
static SERIAL_LINE_CTRL: Mutex<PortWriteOnly<u8>> =
Mutex::new(PortWriteOnly::new(SERIAL_DATA_PORT + 3));
static SERIAL_MODEM_CTRL: Mutex<PortWriteOnly<u8>> =
Mutex::new(PortWriteOnly::new(SERIAL_DATA_PORT + 4));
static SERIAL_LINE_STS: Mutex<Port<u8>> = Mutex::new(Port::new(SERIAL_DATA_PORT + 5));
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<Mutex<IrqAllocateHandle>> = Once::new();
static SERIAL_INPUT_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
@ -34,30 +32,23 @@ static SERIAL_INPUT_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>
/// Initializes the serial port.
pub(crate) fn init() {
let mut serial_line_ctrl_lock = SERIAL_LINE_CTRL.lock();
let mut serial_int_en_lock = SERIAL_INT_EN.lock();
let mut serial_data_lock = SERIAL_DATA.lock();
let mut serial_fifo_ctrl_lock = SERIAL_FIFO_CTRL.lock();
let mut serial_modem_ctrl_lock = SERIAL_MODEM_CTRL.lock();
unsafe {
// Disable interrupts
serial_int_en_lock.write(0x00);
// Enable DLAB
serial_line_ctrl_lock.write(0x80);
// Set maximum speed to 38400 bps by configuring DLL and DLM
serial_data_lock.write(0x03);
serial_int_en_lock.write(0x00);
// Disable DLAB and set data word length to 8 bits
serial_line_ctrl_lock.write(0x03);
// Enable FIFO, clear TX/RX queues and
// set interrupt watermark at 14 bytes
serial_fifo_ctrl_lock.write(0xC7);
// Mark data terminal ready, signal request to send
// and enable auxilliary output #2 (used as interrupt line for CPU)
serial_modem_ctrl_lock.write(0x0B);
// Enable interrupts
serial_int_en_lock.write(0x01);
}
// 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) {
@ -95,26 +86,23 @@ fn handle_serial_input(trap_frame: &TrapFrame) {
}
fn line_sts() -> LineSts {
LineSts::from_bits_truncate(unsafe { SERIAL_LINE_STS.lock().read() })
LineSts::from_bits_truncate(SERIAL_LINE_STS.read())
}
/// Sends a byte on the serial port.
pub fn send(data: u8) {
let mut lock = SERIAL_DATA.lock();
unsafe {
match data {
8 | 0x7F => {
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
lock.write(8);
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
lock.write(b' ');
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
lock.write(8);
}
_ => {
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
lock.write(data);
}
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);
}
}
}
@ -122,7 +110,7 @@ pub fn send(data: u8) {
/// Receives a byte on the serial port. non-blocking
pub fn receive_char() -> Option<u8> {
if line_sts().contains(LineSts::INPUT_FULL) {
unsafe { Some(SERIAL_DATA.lock().read()) }
Some(SERIAL_DATA.read())
} else {
None
}

View File

@ -1,12 +1,14 @@
use crate::device::io_port::{IoPort, WriteOnlyAccess};
use crate::trap::allocate_target_irq;
use crate::trap::IrqAllocateHandle;
use core::sync::atomic::Ordering::Relaxed;
use core::sync::atomic::{AtomicBool, AtomicU8};
static MASTER_CMD: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0x20));
static MASTER_DATA: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0x21));
static SLAVE_CMD: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0xA0));
static SLAVE_DATA: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0xA1));
static MASTER_CMD: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0x20) };
static MASTER_DATA: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0x21) };
static SLAVE_CMD: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0xA0) };
static SLAVE_DATA: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0xA1) };
const IRQ_OFFSET: u8 = 0x20;
@ -16,7 +18,6 @@ use alloc::vec::Vec;
use lazy_static::lazy_static;
use log::info;
use spin::Mutex;
use x86_64::instructions::port::PortWriteOnly;
lazy_static! {
/// store the irq, although we have APIC for manage interrupts
@ -98,36 +99,30 @@ pub(crate) fn disable_temp() {
#[inline(always)]
pub(crate) unsafe fn set_mask(master_mask: u8, slave_mask: u8) {
let mut master_data_lock = MASTER_DATA.lock();
let mut slave_data_lock = SLAVE_DATA.lock();
unsafe {
// Start initialization
MASTER_CMD.lock().write(0x11);
SLAVE_CMD.lock().write(0x11);
// Start initialization
MASTER_CMD.write(0x11);
SLAVE_CMD.write(0x11);
// Set offsets
// map master PIC vector 0x00~0x07 to 0x20~0x27 IRQ number
master_data_lock.write(IRQ_OFFSET);
// map slave PIC vector 0x00~0x07 to 0x28~0x2f IRQ number
slave_data_lock.write(IRQ_OFFSET + 0x08);
// Set offsets
// map master PIC vector 0x00~0x07 to 0x20~0x27 IRQ number
MASTER_DATA.write(IRQ_OFFSET);
// map slave PIC vector 0x00~0x07 to 0x28~0x2f IRQ number
SLAVE_DATA.write(IRQ_OFFSET + 0x08);
// Set up cascade, there is slave at IRQ2
master_data_lock.write(4);
slave_data_lock.write(2);
// Set up cascade, there is slave at IRQ2
MASTER_DATA.write(4);
SLAVE_DATA.write(2);
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
master_data_lock.write(1);
slave_data_lock.write(1);
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
MASTER_DATA.write(1);
SLAVE_DATA.write(1);
// mask interrupts
master_data_lock.write(master_mask);
slave_data_lock.write(slave_mask);
}
// mask interrupts
MASTER_DATA.write(master_mask);
SLAVE_DATA.write(slave_mask);
}
#[inline(always)]
pub(crate) fn ack() {
unsafe {
MASTER_CMD.lock().write(0x20);
}
MASTER_CMD.write(0x20);
}

View File

@ -2,20 +2,19 @@ use core::sync::atomic::AtomicU8;
use core::sync::atomic::Ordering::Relaxed;
use acpi::{fadt::Fadt, sdt::Signature};
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::port::{PortReadOnly, PortWriteOnly};
use crate::device::io_port::{IoPort, ReadOnlyAccess, WriteOnlyAccess};
use crate::time::Time;
use super::acpi::ACPI_TABLES;
static CMOS_ADDRESS: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0x70));
static CMOS_DATA: Mutex<PortReadOnly<u8>> = Mutex::new(PortReadOnly::new(0x71));
static CMOS_ADDRESS: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0x70) };
static CMOS_DATA: IoPort<u8, ReadOnlyAccess> = unsafe { IoPort::new(0x71) };
pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
lazy_static! {
lazy_static::lazy_static! {
static ref READ_TIME: Mutex<Time> = Mutex::new(Time::default());
}
@ -32,17 +31,13 @@ pub fn init() {
}
pub fn get_cmos(reg: u8) -> u8 {
unsafe {
CMOS_ADDRESS.lock().write(reg);
CMOS_DATA.lock().read()
}
CMOS_ADDRESS.write(reg);
CMOS_DATA.read()
}
pub fn is_updating() -> bool {
unsafe {
CMOS_ADDRESS.lock().write(0x0A);
CMOS_DATA.lock().read() & 0x80 != 0
}
CMOS_ADDRESS.write(0x0A);
CMOS_DATA.read() & 0x80 != 0
}
pub fn read() -> Time {

View File

@ -1,22 +1,19 @@
//! used for PIT Timer
use spin::Mutex;
use x86_64::instructions::port::PortWriteOnly;
use crate::config::TIMER_FREQ;
use crate::device::io_port::{IoPort, WriteOnlyAccess};
const TIMER_RATE: u32 = 1193182;
static TIMER_PERIOD: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0x40));
static TIMER_MOD: Mutex<PortWriteOnly<u8>> = Mutex::new(PortWriteOnly::new(0x43));
static TIMER_PERIOD: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0x40) };
static TIMER_MOD: IoPort<u8, WriteOnlyAccess> = unsafe { IoPort::new(0x43) };
static TIMER_SQUARE_WAVE: u8 = 0x36;
pub(crate) fn init() {
// Initialize timer.
let cycle = TIMER_RATE / TIMER_FREQ as u32;
unsafe {
TIMER_MOD.lock().write(TIMER_SQUARE_WAVE);
TIMER_PERIOD.lock().write((cycle & 0xFF) as _);
TIMER_PERIOD.lock().write((cycle >> 8) as _);
}
TIMER_MOD.write(TIMER_SQUARE_WAVE);
TIMER_PERIOD.write((cycle & 0xFF) as _);
TIMER_PERIOD.write((cycle >> 8) as _);
}