diff --git a/src/ps2/controller.rs b/src/ps2/controller.rs index d1aa04f..b01cfd3 100755 --- a/src/ps2/controller.rs +++ b/src/ps2/controller.rs @@ -1,15 +1,12 @@ /// https://wiki.osdev.org/%228042%22_PS/2_Controller -use core::arch::asm; - -use super::{Key, scancodes::SCANCODE_TO_KEY}; - -const DATA_PORT: u16 = 0x60; -const STATUS_PORT: u16 = 0x64; -const COMMAND_PORT: u16 = 0x64; -const OUTPUT_BUFFER_STATUS_BIT: u8 = 1; +use crate::ps2::{ + DATA_PORT, + io::{flush_output_buffer, read, send_command, send_data, wait_for_data}, +}; #[repr(u8)] -enum Command { +#[derive(PartialEq)] +pub enum Command { DisableFirstPort = 0xAD, DisableSecondPort = 0xA7, EnableFirstPort = 0xAE, @@ -22,11 +19,14 @@ enum Command { } #[repr(u8)] -enum Status { +pub enum Status { OutputFull = 0x01, InputFull = 0x02, } +/// [Root System Description Pointer](https://wiki.osdev.org/RSDP). +/// +/// Stores a pointer to the [Root System Description Table](https://wiki.osdev.org/RSDT). #[repr(C, packed)] struct Rsdp { signature: [u8; 8], // "RSD PTR " @@ -36,9 +36,9 @@ struct Rsdp { rsdt_address: u32, } -/// https://wiki.osdev.org/RSDT -/// -/// No need for handling 2.0 since we are building for 32-bit. +/// Header used by [RSDT](https://wiki.osdev.org/RSDT) entries. The first 4 bytes +/// hold the [signature](https://wiki.osdev.org/RSDT#Defined_by_ACPI) used to +/// identify which system desciptor entry we are dealing with. #[repr(C, packed)] struct SDTHeader { signature: [u8; 4], @@ -116,70 +116,45 @@ struct GenericAddressStructure { address: u64, } -/// Searches for the Root System Description Pointer, first in the Extended BIOS Data Area, -/// then in the main BIOS area. -/// -/// https://wiki.osdev.org/RSDP#Detecting_the_RSDP -/// https://wiki.osdev.org/Memory_Map_(x86)#Extended_BIOS_Data_Area_(EBDA) -fn get_rsdp() -> *mut Rsdp { - let ebda_addr: usize = unsafe { *(0x40E as *const u16) as usize } << 4; +fn validate_checksum(ptr: *const u8, len: u32) -> bool { + let mut sum: u8 = 0; - let target = b"RSD PTR "; + for i in 0..len { + sum = sum.wrapping_add(unsafe { *ptr.add(i as usize) }); + } + + sum == 0 +} + +/// Searches for the [RSDP](https://wiki.osdev.org/RSDP#Detecting_the_RSDP), +/// first in the [EBDA](https://wiki.osdev.org/Memory_Map_(x86)#Extended_BIOS_Data_Area_(EBDA)), +/// then in the main BIOS area (`0x000E0000..0x000FFFFF`). +fn get_rsdp() -> Result<*mut Rsdp, &'static str> { + let ebda_addr: usize = unsafe { *(0x40E as *const u16) as usize } << 4; for loc in (ebda_addr..(ebda_addr + 0x400)).step_by(16) { - let loc_ptr = loc as *const u8; - let mut matches = true; - - for (i, _) in target.iter().enumerate() { - if unsafe { *loc_ptr.add(i) } != target[i] { - matches = false; - break; - } - } + let rsdp = unsafe { &*(loc as *const Rsdp) }; - if matches { - return loc as *mut Rsdp; + if &rsdp.signature == b"RSD PTR " { + return Ok(loc as *mut Rsdp); } } for loc in (0x000E0000..0x000FFFFF).step_by(16) { - let loc_ptr = loc as *const u8; - let mut matches = true; - - for (i, _) in target.iter().enumerate() { - if unsafe { *loc_ptr.add(i) } != target[i] { - matches = false; - break; - } - } + let rsdp = unsafe { &*(loc as *const Rsdp) }; - if matches { - return loc as *mut Rsdp; + if &rsdp.signature == b"RSD PTR " { + return Ok(loc as *mut Rsdp); } } - panic!("RSDP not found"); -} - -fn validate_table(header: &SDTHeader) -> bool { - let ptr = header as *const SDTHeader as *const u8; - let mut sum: u8 = 0; - - for i in 0..header.length { - sum = sum.wrapping_add(unsafe { *ptr.add(i as usize) }); - } - - sum == 0 + Err("RSDP not found") } -fn get_fadt(rsdt_address: u32) -> *mut Fadt { - let rsdt = rsdt_address as *const Rsdt; - let header = unsafe { &(*rsdt).h }; - - if !validate_table(header) { - panic!("STD header is not valid") - } - +/// Searches for the [FADT](https://wiki.osdev.org/FADT) in the +/// [RSDT](https://wiki.osdev.org/RSDT), which we can then use to verify the +/// PS/2 configuration. +fn get_fadt(header: &SDTHeader, rsdt: *const Rsdt) -> Result<*mut Fadt, &'static str> { let entries = (header.length as usize - size_of::()) / 4; let entries_ptr = (rsdt as usize + size_of::()) as *const u32; @@ -188,35 +163,11 @@ fn get_fadt(rsdt_address: u32) -> *mut Fadt { let entry_hdr = unsafe { &*(entry_addr as *const SDTHeader) }; if &entry_hdr.signature == b"FACP" { - if validate_table(entry_hdr) { - return entry_addr as *mut Fadt; - } else { - panic!("FADT checsum invalid") - } + return Ok(entry_addr as *mut Fadt); } } - panic!("FADT not found") -} - -/// If we are under ACPI 1.0, the PS/2 controller is assumed to be here and does not need any -/// further configuration. -/// Else if we are under 2.0+ and the "8042" flag is not set, we can assume the PS/2 controller -/// is not present. -/// -/// https://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS/2_Controller -fn has_ps2_controller(fadt: &Fadt, rsdp: &Rsdp) -> bool { - rsdp.revision < 2 || (fadt.boot_architecture_flags & 0x2) != 0 -} - -fn validate_rsdp(rsdp_ptr: *mut Rsdp) -> bool { - let mut sum: u8 = 0; - - for i in 0..size_of::() { - sum = sum.wrapping_add(unsafe { *(rsdp_ptr as *const u8).add(i) }); - } - - sum == 0 + Err("FADT not found") } /// Checks whether we have a dual channel PS/2 controller by trying to enable the second port @@ -231,54 +182,13 @@ fn is_dual_channel_controller() -> bool { config >> 5 & 1 == 1 } -/// Initializes the PS/2 buffer. +/// Performs interface tests to check the PS/2 ports. /// /// https://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS/2_Controller -/// https://wiki.osdev.org/%228042%22_PS/2_Controller#PS/2_Controller_Configuration_Byte -/// https://wiki.osdev.org/ACPI -pub fn init() -> Result<(), &'static str> { - let rsdp_ptr = get_rsdp(); - let rsdp: &mut Rsdp = unsafe { &mut *rsdp_ptr }; - - assert!(validate_rsdp(rsdp_ptr)); - assert_eq!(&rsdp.signature, b"RSD PTR "); - - let fadt_ptr = get_fadt(rsdp.rsdt_address); - let fadt = unsafe { &*fadt_ptr }; - if !has_ps2_controller(fadt, rsdp) { - return Err("no PS/2 controller found"); - } - - send_command(Command::DisableFirstPort); - send_command(Command::DisableSecondPort); - - flush_output_buffer(); - - send_command(Command::ReadConfig); - let config = unsafe { read(DATA_PORT) }; - - let new_config = config & 0b10101110; +fn test_port(cmd: Command) -> Result<(), &'static str> { + assert!(cmd == Command::TestFirstPort || cmd == Command::TestSecondPort); - send_command(Command::WriteConfig); - send_data(new_config); - - send_command(Command::SelfTest); - let test_result = wait_for_data(); - if test_result != 0x55 { - return Err("PS/2 controller self test failed"); - } - - let is_dual_controller = is_dual_channel_controller(); - if is_dual_controller { - send_command(Command::DisableSecondPort); - send_command(Command::ReadConfig); - let config = unsafe { read(DATA_PORT) }; - let new_config = config & 0b10001100; - send_command(Command::WriteConfig); - send_data(new_config); - } - - send_command(Command::TestFirstPort); + send_command(cmd); match wait_for_data() { 0x01 => return Err("clock line stuck low"), 0x02 => return Err("clock line stuck high"), @@ -287,133 +197,111 @@ pub fn init() -> Result<(), &'static str> { _ => {} } - if is_dual_controller { - send_command(Command::TestSecondPort); - match wait_for_data() { - 0x01 => return Err("clock line stuck low"), - 0x02 => return Err("clock line stuck high"), - 0x03 => return Err("data line stuck low"), - 0x04 => return Err("data line stuck high"), - _ => {} - } - } + Ok(()) +} - send_command(Command::EnableFirstPort); - send_command(Command::EnableSecondPort); +/// Determines whether a PS/2 controller is present by checking the +/// [Fixed ACPI Description Table](https://wiki.osdev.org/FADT). +/// +/// https://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS/2_Controller +fn has_ps2_controller() -> Result<(), &'static str> { + let rsdp_ptr: *mut Rsdp = get_rsdp()?; + let rsdp: &mut Rsdp = unsafe { &mut *rsdp_ptr }; + assert_eq!(&rsdp.signature, b"RSD PTR "); + if !validate_checksum(rsdp_ptr as *const u8, size_of::() as u32) { + return Err("RSDP checksum verification failed"); + } - send_data(0xFF); - for _ in 0..2 { - if wait_for_data() == 0xFC { - return Err("PS/2 controller self test failed"); - } + let rsdt = rsdp.rsdt_address as *const Rsdt; + let header = unsafe { &(*rsdt).h }; + if !validate_checksum(header as *const SDTHeader as *const u8, header.length) { + return Err("SDTHeader checksum verification failed"); } - Ok(()) -} + let fadt_ptr = get_fadt(header, rsdt)?; + let fadt = unsafe { &*fadt_ptr }; + if !validate_checksum(fadt_ptr as *const u8, unsafe { (*(fadt as *const Fadt as *const SDTHeader)).length }) { + return Err("FADT checsum verification failed"); + } -fn send_command(cmd: Command) { - while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} + // If we are under ACPI 1.0, a PS/2 controller is assumed to be present. + // Else if we are under 2.0+ and the "8042" flag is not set, no PS/2 controller + // is present. + if rsdp.revision < 2 || (fadt.boot_architecture_flags & 0x2) != 0 { + return Ok(()); + } - unsafe { write(COMMAND_PORT, cmd as u8) }; + Err("no PS/2 controller found") } -fn send_data(data: u8) { - while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} - - unsafe { write(DATA_PORT, data) }; -} +/// `AND`s the existing [config](https://wiki.osdev.org/%228042%22_PS/2_Controller#PS/2_Controller_Configuration_Byte) +/// with `new_config` and updates it. +fn update_config(new_config: u8) { + send_command(Command::ReadConfig); + let config = unsafe { read(DATA_PORT) }; -fn wait_for_data() -> u8 { - while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 == 0 {} + let new_config = config & new_config; - unsafe { read(DATA_PORT) } + send_command(Command::WriteConfig); + send_data(new_config); } -/// Reads all data from the output buffer, flushing it. Note that this will -/// go into an endless loop if called without disabling the ports first. -fn flush_output_buffer() { - while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 != 0 { - unsafe { read(DATA_PORT) }; +/// Sends `0xFF` to the PS/2 controller, resetting it, and verifies that it returns `0xAA` and `0xFA` +/// (order irrelevant), indicating success. +fn reset_controller() -> Result<(), &'static str> { + let (mut got_0xfa, mut got_0xaa) = (false, false); + send_data(0xFF); + for _ in 0..2 { + match wait_for_data() { + 0xFA => got_0xfa = true, + 0xAA => got_0xaa = true, + _ => return Err("PS/2 controller self test failed"), + } } -} -static mut LAST_KEY: Option = None; - -/// Reads from the PS2 data port if the PS2 status port is ready. Returns `Some(KeyScanCode)` -/// if the converted scancode is a supported character. -/// -/// /// ### Example Usage: -/// ``` -/// let mut v = Vga::new(); -/// -/// if let Some(c) = read_if_ready() == KeyScanCode::A { -/// v.write_char(b'a'); -/// } -pub fn read_if_ready() -> Option { - if !is_ps2_data_available() { - return None; + if !got_0xaa || !got_0xfa { + return Err("PS/2 controller self test failed"); } - let code = unsafe { read(DATA_PORT) }; - - if code == 0xF0 || code == 0xE0 { - while !is_ps2_data_available() {} - let _ = unsafe { read(DATA_PORT) }; - unsafe { LAST_KEY = None }; - return None; - } + Ok(()) +} - // if code == 0xE0 { - // while !is_ps2_data_available() {} - // let _ = unsafe { read(DATA_PORT) }; - // unsafe { LAST_KEY = None }; - // return None; - // } +/// Initializes the PS/2 controller. Note that verifying the existence of the PS/2 controller +/// is not strictly necessary, since it is assumed to be there in i386, but it was fun. +/// +/// https://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS/2_Controller +/// https://wiki.osdev.org/ACPI +pub fn init() -> Result<(), &'static str> { + has_ps2_controller()?; - unsafe { LAST_KEY = Some(code) }; - SCANCODE_TO_KEY[code as usize].1 -} + send_command(Command::DisableFirstPort); + send_command(Command::DisableSecondPort); -/// Returns `true` if the PS2 input buffer has data ready to be read, -/// meaning the least significant bit of the PS2 status port is set. -fn is_ps2_data_available() -> bool { - status() & OUTPUT_BUFFER_STATUS_BIT != 0 -} + flush_output_buffer(); -/// Reads from `STATUS_PORT` and returns the extracted value. -fn status() -> u8 { - let res: u8; + update_config(0b10101110); - unsafe { - res = read(STATUS_PORT); + send_command(Command::SelfTest); + let test_result = wait_for_data(); + if test_result != 0x55 { + return Err("PS/2 controller self test failed"); } - res -} - -/// Reads from `port` and returns the extracted value. -unsafe fn read(port: u16) -> u8 { - assert!(port == DATA_PORT || port == STATUS_PORT); + let is_dual_controller = is_dual_channel_controller(); + if is_dual_controller { + update_config(0b10001100); + } - let res: u8; + test_port(Command::TestFirstPort)?; - unsafe { - asm!( - "in al, dx", - in("dx") port, - out("al") res, - ); + if is_dual_controller { + test_port(Command::TestSecondPort)?; } - res -} + send_command(Command::EnableFirstPort); + send_command(Command::EnableSecondPort); -unsafe fn write(port: u16, val: u8) { - unsafe { - asm!( - "out dx, al", - in("dx") port, - in("al") val, - ); - } + reset_controller()?; + + Ok(()) } diff --git a/src/ps2/io.rs b/src/ps2/io.rs new file mode 100644 index 0000000..31b1ef1 --- /dev/null +++ b/src/ps2/io.rs @@ -0,0 +1,114 @@ +use core::arch::asm; + +use super::{ + COMMAND_PORT, DATA_PORT, Key, OUTPUT_BUFFER_STATUS_BIT, STATUS_PORT, + controller::{Command, Status}, + scancodes::SCANCODE_TO_KEY, +}; + +pub fn send_command(cmd: Command) { + while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} + + unsafe { write(COMMAND_PORT, cmd as u8) }; +} + +pub fn send_data(data: u8) { + while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} + + unsafe { write(DATA_PORT, data) }; +} + +pub fn wait_for_data() -> u8 { + while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 == 0 {} + + unsafe { read(DATA_PORT) } +} + +/// Reads all data from the output buffer, flushing it. Note that this will +/// go into an endless loop if called without disabling the ports first. +pub fn flush_output_buffer() { + while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 != 0 { + unsafe { read(DATA_PORT) }; + } +} + +static mut LAST_KEY: Option = None; + +/// Reads from the PS2 data port if the PS2 status port is ready. Returns `Some(KeyScanCode)` +/// if the converted scancode is a supported character. +/// +/// /// ### Example Usage: +/// ``` +/// let mut v = Vga::new(); +/// +/// if let Some(c) = read_if_ready() == KeyScanCode::A { +/// v.write_char(b'a'); +/// } +pub fn read_if_ready() -> Option { + if !is_ps2_data_available() { + return None; + } + + let code = unsafe { read(DATA_PORT) }; + + if code == 0xF0 { + while !is_ps2_data_available() {} + let _ = unsafe { read(DATA_PORT) }; + unsafe { LAST_KEY = None }; + return None; + } + + if code == 0xE0 { + while !is_ps2_data_available() {} + let extended_code = unsafe { read(DATA_PORT) }; + unsafe { LAST_KEY = Some(extended_code) }; + return SCANCODE_TO_KEY[extended_code as usize].1; + } + + unsafe { LAST_KEY = Some(code) }; + SCANCODE_TO_KEY[code as usize].1 +} + +/// Returns `true` if the PS2 input buffer has data ready to be read, +/// meaning the least significant bit of the PS2 status port is set. +fn is_ps2_data_available() -> bool { + status() & OUTPUT_BUFFER_STATUS_BIT != 0 +} + +/// Reads from `STATUS_PORT` and returns the extracted value. +fn status() -> u8 { + let res: u8; + + unsafe { + res = read(STATUS_PORT); + } + + res +} + +/// Reads from `port` and returns the extracted value. +pub unsafe fn read(port: u16) -> u8 { + assert!(port == DATA_PORT || port == STATUS_PORT); + + let res: u8; + + unsafe { + asm!( + "in al, dx", + in("dx") port, + out("al") res, + ); + } + + res +} + +unsafe fn write(port: u16, val: u8) { + unsafe { + asm!( + "out dx, al", + in("dx") port, + in("al") val, + ); + } +} diff --git a/src/ps2/mod.rs b/src/ps2/mod.rs index 63dcda8..8f1da67 100644 --- a/src/ps2/mod.rs +++ b/src/ps2/mod.rs @@ -1,5 +1,12 @@ mod controller; +mod io; mod scancodes; -pub use controller::{init, read_if_ready}; +pub const DATA_PORT: u16 = 0x60; +pub const STATUS_PORT: u16 = 0x64; +pub const COMMAND_PORT: u16 = 0x64; +pub const OUTPUT_BUFFER_STATUS_BIT: u8 = 1; + +pub use controller::init; +pub use io::read_if_ready; pub use scancodes::Key; diff --git a/src/terminal/vga.rs b/src/terminal/vga.rs index f25715e..f909db2 100755 --- a/src/terminal/vga.rs +++ b/src/terminal/vga.rs @@ -288,24 +288,6 @@ mod test { } } - #[test] - fn hitting_enter() { - let mut s = Screen::default(); - s.write_str("A"); - for i in 0..VIEW_HEIGHT as u16 { - if i % 2 == 0 { - s.write_str("\n"); - } else { - s.handle_key(Key::Enter); - } - } - let b = Buffer::from_screen(&s); - assert_eq!(b.buffer[0], Entry::new(b' ').to_u16()); - - assert_eq!(b.cursor.unwrap().x, 0); - assert_eq!(b.cursor.unwrap().y, (VIEW_HEIGHT - 1) as u16) - } - #[test] fn lines_of_coke() { let mut s = Screen::default();