#![no_std] /// Implementation Courtesy of MOROS. /// Currently Only Supports ATA-PIO, with 24-bit LBA Addressing. extern crate alloc; use alloc::string::String; use alloc::vec::Vec; use bit_field::BitField; use core::hint::spin_loop; use lazy_static::lazy_static; use log::trace; use spin::Mutex; use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly}; pub type BlockIndex = u32; pub const ATA_BLOCK_SIZE: usize = 512; fn sleep_ticks(ticks: usize) { for _ in 0..=ticks { x86_64::instructions::hlt(); } } #[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); // Set SRST bit sleep_ticks(2); self.control_register.write(0); // Then clear it sleep_ticks(2); } } fn wait(&mut self) { for _ in 0..4 { // Wait about 4 x 100 ns unsafe { self.alternate_status_register.read(); } } } fn write_command(&mut self, cmd: Command) { unsafe { self.command_register.write(cmd as u8); } } 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 busy_loop(&mut self) { self.wait(); let start = 0; while self.is_busy() { if 0 - start > 1 { // Hanged return self.reset(); } spin_loop(); } } 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); } } 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; } self.busy_loop(); 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) } /// Read A single, 512-byte long slice from a given block /// panics if buf isn't EXACTLY 512 Bytes long; /// Example: /// ```rust /// // Read A Single block from a disk /// pub fn read_single() { /// use x86_ata::{init, ATA_BLOCK_SIZE, read}; /// // 1. Initialise ATA Subsystem. (Perform Once, on boot) /// init().expect("Failed To Start ATA..."); /// // 2. Create a temporary buffer of size 512. /// let mut buffer: [u8;ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE]; /// // 3. Pass the buffer over to the Subsystem, to be filled. /// read(0, 0, 0, &mut buffer); /// } pub fn read(&mut self, drive: u8, block: BlockIndex, buf: &mut [u8]) { assert!(buf.len() == 512); trace!("Reading Block 0x{:8X}", block); // trace!("{:?}", self); self.setup(drive, block); self.write_command(Command::Read); self.busy_loop(); for i in (0..256).step_by(2) { let data = self.read_data(); //log!("Read[{:08X}][{:02X}]: 0x{:04X}\n", block, i, data); buf[i + 0] = data.get_bits(0..8) as u8; buf[i + 1] = data.get_bits(8..16) as u8; } } /// Write A single, 512-byte long slice to a given block /// panics if buf isn't EXACTLY 512 Bytes long; /// Example: /// ```rust /// // Read A Single block from a disk /// pub fn write_single() { /// use x86_ata::{init, ATA_BLOCK_SIZE, write}; /// // 1. Initialise ATA Subsystem. (Perform Once, on boot) /// init().expect("Failed To Start ATA..."); /// // 2. Create a temporary buffer of size 512. /// let buffer: [u8;ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE]; /// // 3. Pass the buffer over to the Subsystem, to be filled. /// write(0, 0, 0, &buffer); /// } pub fn write(&mut self, drive: u8, block: BlockIndex, buf: &[u8]) { assert!(buf.len() == 512); self.setup(drive, block); self.write_command(Command::Write); self.busy_loop(); 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); //log!("Data: 0x{:04X} | {}{} \n", data, buf[i * 2] as char, buf[i * 2 + 1] as char); self.write_data(data); } self.busy_loop(); } } lazy_static! { pub static ref BUSES: Mutex> = Mutex::new(Vec::new()); } fn disk_size(sectors: u32) -> (u32, String) { let bytes = sectors * 512; if bytes >> 20 < 1000 { (bytes >> 20, String::from("MB")) } else { (bytes >> 30, String::from("GB")) } } pub fn list() -> Vec<(u8, u8, String, String, u32, String, u32)> { let mut buses = BUSES.lock(); let mut res = Vec::new(); for bus in 0..2 { for drive in 0..2 { if let Some(buf) = buses[bus as usize].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); } } serial = serial.trim().into(); let mut model = String::new(); for i in 27..47 { for &b in &buf[i].to_be_bytes() { model.push(b as char); } } model = model.trim().into(); let sectors = (buf[61] as u32) << 16 | (buf[60] as u32); let (size, unit) = disk_size(sectors); res.push((bus, drive, model, serial, size, unit, sectors)); } } } res } /// Identify a specific drive on a bus, format: (bus, drive, model, serial. size, unit, sectors) pub fn indentify_drive(bus: u8, drive: u8) -> Option<(u8, u8, String, String, u32, String, u32)> { let mut buses = BUSES.lock(); if let Some(buf) = buses[bus as usize].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); } } serial = serial.trim().into(); let mut model = String::new(); for i in 27..47 { for &b in &buf[i].to_be_bytes() { model.push(b as char); } } model = model.trim().into(); let sectors = (buf[61] as u32) << 16 | (buf[60] as u32); let (size, unit) = disk_size(sectors); Some((bus, drive, model, serial, size, unit, sectors)) } else { None } } pub fn read(bus: u8, drive: u8, block: BlockIndex, buf: &mut [u8]) { let mut buses = BUSES.lock(); trace!("Reading Block 0x{:08X}\n", block); buses[bus as usize].read(drive, block, buf); } pub fn write(bus: u8, drive: u8, block: BlockIndex, buf: &[u8]) { let mut buses = BUSES.lock(); //log!("Writing Block 0x{:08X}\n", block); buses[bus as usize].write(drive, block, buf); } pub fn drive_is_present(bus: usize) -> bool { unsafe { BUSES.lock()[bus].status_register.read() != 0xFF } } pub fn init() -> Result<(), ()> { { let mut buses = BUSES.lock(); buses.push(Bus::new(0, 0x1F0, 0x3F6, 14)); buses.push(Bus::new(1, 0x170, 0x376, 15)); } Ok(()) }