From b77e02a472100870fdb3005607e47a16b78a8450 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Sun, 12 Jan 2020 08:56:29 +0100 Subject: [PATCH] Support disks in ATA PIO Mode (#3) * Introduce ATA PIO * Refactor drives detection * Refactor drive identification * Add read from disk * Add write to disk * Add Bus#setup to dry read/write code * Add hex viewer --- run/cool-retro-term.sh | 2 +- src/kernel/ata.rs | 301 +++++++++++++++++++++++++++++++++++++++++ src/kernel/mod.rs | 1 + src/lib.rs | 1 + src/user/hex.rs | 41 ++++++ src/user/mod.rs | 1 + src/user/shell.rs | 1 + 7 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 src/kernel/ata.rs create mode 100644 src/user/hex.rs diff --git a/run/cool-retro-term.sh b/run/cool-retro-term.sh index 0663e40..5222a8c 100755 --- a/run/cool-retro-term.sh +++ b/run/cool-retro-term.sh @@ -4,7 +4,7 @@ set -e dir=$(dirname "$0") image="target/x86_64-moros/release/bootimage-moros.bin" -qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -rtc base=localtime" +qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -rtc base=localtime -hdc disk.img" # Build image if needed cd "$dir/.." && cargo bootimage --release diff --git a/src/kernel/ata.rs b/src/kernel/ata.rs new file mode 100644 index 0000000..70d4888 --- /dev/null +++ b/src/kernel/ata.rs @@ -0,0 +1,301 @@ +use bit_field::BitField; +use crate::{print, kernel}; +use lazy_static::lazy_static; +use heapless::{String, Vec}; +use heapless::consts::*; +use spin::Mutex; +use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly}; + +#[repr(u16)] +enum Command { + Read = 0x20, + Write = 0x30, + Identify = 0xEC, +} + +#[allow(dead_code)] +#[repr(usize)] +enum Status { + ERR = 0, + IDX = 1, + CORR = 2, + DRQ = 3, + SRV = 4, + DF = 5, + RDY = 6, + BSY = 7 +} + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct Bus { + id: u8, + irq: u8, + + data_register: Port, + error_register: PortReadOnly, + features_register: PortWriteOnly, + sector_count_register: Port, + lba0_register: Port, + lba1_register: Port, + lba2_register: Port, + drive_register: Port, + status_register: PortReadOnly, + command_register: PortWriteOnly, + + alternate_status_register: PortReadOnly, + control_register: PortWriteOnly, + drive_blockess_register: PortReadOnly, +} + +impl Bus { + pub fn new(id: u8, io_base: u16, ctrl_base: u16, irq: u8) -> Self { + Self { + id, irq, + + data_register: Port::new(io_base + 0), + error_register: PortReadOnly::new(io_base + 1), + features_register: PortWriteOnly::new(io_base + 1), + sector_count_register: Port::new(io_base + 2), + lba0_register: Port::new(io_base + 3), + lba1_register: Port::new(io_base + 4), + lba2_register: Port::new(io_base + 5), + drive_register: Port::new(io_base + 6), + status_register: PortReadOnly::new(io_base + 7), + command_register: PortWriteOnly::new(io_base + 7), + + alternate_status_register: PortReadOnly::new(ctrl_base + 0), + control_register: PortWriteOnly::new(ctrl_base + 0), + drive_blockess_register: PortReadOnly::new(ctrl_base + 1), + } + } + + fn reset(&mut self) { + unsafe { + self.control_register.write(4); + self.control_register.write(0); + } + } + + fn wait(&mut self) { + unsafe { + self.status_register.read(); + self.status_register.read(); + self.status_register.read(); + self.status_register.read(); + } + } + + fn write_command(&mut self, cmd: Command) { + unsafe { self.command_register.write(cmd as u8); } + //unsafe { self.command_register.write(cmd as u32); } + } + + fn status(&mut self) -> u8 { + unsafe { self.status_register.read() } + } + + fn lba1(&mut self) -> u8 { + unsafe { self.lba1_register.read() } + } + + fn lba2(&mut self) -> u8 { + unsafe { self.lba2_register.read() } + } + + fn read_data(&mut self) -> u16 { + unsafe { self.data_register.read() } + } + + fn write_data(&mut self, data: u16) { + unsafe { self.data_register.write(data) } + } + + fn is_busy(&mut self) -> bool { + self.status().get_bit(Status::BSY as usize) + } + + fn is_error(&mut self) -> bool { + self.status().get_bit(Status::ERR as usize) + } + + fn is_ready(&mut self) -> bool { + self.status().get_bit(Status::RDY as usize) + } + + fn select_drive(&mut self, drive: u8) { + // Drive #0 (primary) = 0xA0 + // Drive #1 (secondary) = 0xB0 + let drive_id = 0xA0 | (drive << 4); + unsafe { + self.drive_register.write(drive_id); + } + } + + #[allow(dead_code)] + fn debug(&mut self) { + self.wait(); + unsafe { print!("drive register: 0b{:08b}\n", self.drive_register.read()); } + unsafe { print!("status: 0b{:08b}\n", self.status_register.read()); } + } + + fn setup(&mut self, drive: u8, block: u32) { + let drive_id = 0xE0 | (drive << 4); + unsafe { + self.drive_register.write(drive_id | ((block.get_bits(24..28) as u8) & 0x0F)); + self.sector_count_register.write(1); + self.lba0_register.write(block.get_bits(0..8) as u8); + self.lba1_register.write(block.get_bits(8..16) as u8); + self.lba2_register.write(block.get_bits(16..24) as u8); + } + } + + pub fn identify_drive(&mut self, drive: u8) -> Option<[u16; 256]> { + self.reset(); + self.wait(); + self.select_drive(drive); + unsafe { + self.sector_count_register.write(0); + self.lba0_register.write(0); + self.lba1_register.write(0); + self.lba2_register.write(0); + } + + self.write_command(Command::Identify); + + if self.status() == 0 { + return None; + } + + while self.is_busy() {} + + if self.lba1() != 0 || self.lba2() != 0 { + return None; + } + + for i in 0.. { + if i == 256 { + self.reset(); + return None; + } + if self.is_error() { + return None; + } + if self.is_ready() { + break + } + } + + let mut res = [0; 256]; + for i in 0..256 { + res[i] = self.read_data(); + } + Some(res) + } + + pub fn read(&mut self, drive: u8, block: u32, buf: &mut [u8]) { + self.setup(drive, block); + + self.write_command(Command::Read); + + while self.is_busy() {} + + for i in 0..256 { + let data = self.read_data(); + buf[i * 2] = data.get_bits(0..8) as u8; + buf[i * 2 + 1] = data.get_bits(8..16) as u8; + } + } + + pub fn write(&mut self, drive: u8, block: u32, buf: &mut [u8]) { + self.setup(drive, block); + + self.write_command(Command::Write); + + while self.is_busy() {} + + for i in 0..256 { + let mut data = 0 as u16; + data.set_bits(0..8, buf[i * 2] as u16); + data.set_bits(8..16, buf[i * 2 + 1] as u16); + self.write_data(data); + } + } +} + +lazy_static! { + pub static ref ATA_BUSES: Mutex> = Mutex::new(Vec::new()); +} + +pub fn init() { + let mut buses = ATA_BUSES.lock(); + buses.push(Bus::new(0, 0x1F0, 0x3F6, 14)).unwrap(); + buses.push(Bus::new(1, 0x170, 0x376, 15)).unwrap(); + + let bus = 1; + let drive = 0; + if let Some(buf) = buses[bus].identify_drive(drive) { + let mut serial = String::::new(); + for i in 10..20 { + for &b in &buf[i].to_be_bytes() { + serial.push(b as char).unwrap(); + } + } + let mut model = String::::new(); + for i in 27..47 { + for &b in &buf[i].to_be_bytes() { + model.push(b as char).unwrap(); + } + } + //let sectors = (buf[60] as u32) << 16 | (buf[61] as u32); + let uptime = kernel::clock::clock_monotonic(); + print!("[{:.6}] ATA {}:{} {} {}\n", uptime, bus, drive, model.trim(), serial.trim()); + } + + /* + let block = 1; + let mut buf = [0u8; 512]; + buses[1].read(drive, block, &mut buf); + for i in 0..256 { + if i % 8 == 0 { + print!("\n{:08X} ", i * 2); + } + print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]); + } + print!("\n"); + + buf[0x42] = 'H' as u8; + buf[0x43] = 'e' as u8; + buf[0x44] = 'l' as u8; + buf[0x45] = 'l' as u8; + buf[0x46] = 'o' as u8; + for i in 0..256 { + if i % 8 == 0 { + print!("\n{:08X} ", i * 2); + } + print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]); + } + print!("\n"); + buses[1].write(drive, block, &mut buf); + + let mut buf = [0u8; 512]; + buses[1].read(drive, block, &mut buf); + for i in 0..256 { + if i % 8 == 0 { + print!("\n{:08X} ", i * 2); + } + print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]); + } + print!("\n"); + */ +} + +pub fn read(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) { + let mut buses = ATA_BUSES.lock(); + buses[bus as usize].read(drive, block, &mut buf); +} + +pub fn write(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) { + let mut buses = ATA_BUSES.lock(); + buses[bus as usize].write(drive, block, &mut buf); +} diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 6274746..5f63530 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1,4 +1,5 @@ pub mod acpi; +pub mod ata; pub mod clock; pub mod cmos; pub mod console; diff --git a/src/lib.rs b/src/lib.rs index 5399504..7631cf6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,4 +12,5 @@ pub fn init() { x86_64::instructions::interrupts::enable(); kernel::cpu::init(); kernel::pci::init(); + kernel::ata::init(); } diff --git a/src/user/hex.rs b/src/user/hex.rs new file mode 100644 index 0000000..2d01381 --- /dev/null +++ b/src/user/hex.rs @@ -0,0 +1,41 @@ +use crate::{print, kernel, user}; +use heapless::Vec; +use heapless::consts::*; + +pub fn main(args: &[&str]) -> user::shell::ExitCode { + if args.len() != 2 { + return user::shell::ExitCode::CommandError; + } + let path: Vec<_, U16> = args[1].split("/").collect(); + if path.len() != 8 || path[1] != "dev" || path[2] != "ata" || path[4] != "dsk" || path[6] != "blk" { + return user::shell::ExitCode::CommandError; + } + let bus = path[3].parse().unwrap(); + let dsk = path[5].parse().unwrap(); + let blk = path[7].parse().unwrap(); + let mut buf = [0u8; 512]; + kernel::ata::read(bus, dsk, blk, &mut buf); + print_hex(&buf); + user::shell::ExitCode::CommandSuccessful +} + +pub fn print_hex(buf: &[u8]) { + let n = buf.len() / 2; + for i in 0..n { + if i % 8 == 0 { + print!("\n{:08X}: ", i * 2); + } + print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]); + if i % 8 == 7 { + for j in 0..16 { + let c = buf[(i * 2 + 1) - 15 + j] as char; + if c.is_ascii_graphic() || c.is_ascii_whitespace() { + print!("{}", c); + } else { + print!("."); + } + } + } + } + print!("\n"); +} diff --git a/src/user/mod.rs b/src/user/mod.rs index faf130c..a45443b 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -3,6 +3,7 @@ pub mod clear; pub mod date; pub mod editor; pub mod halt; +pub mod hex; pub mod login; pub mod print; pub mod r#move; diff --git a/src/user/shell.rs b/src/user/shell.rs index dda612d..e36af2d 100644 --- a/src/user/shell.rs +++ b/src/user/shell.rs @@ -219,6 +219,7 @@ impl Shell { "login" => user::login::main(&args), "base64" => user::base64::main(&args), "halt" => user::halt::main(&args), + "hex" => user::hex::main(&args), _ => ExitCode::CommandUnknown, } }