diff --git a/Cargo.lock b/Cargo.lock index e2753eda..b55e00b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,7 @@ dependencies = [ "wasmi", "watson", "x86_64", + "x86_ata", "y-compositor-protocol", ] @@ -1148,6 +1149,18 @@ dependencies = [ "volatile 0.4.4", ] +[[package]] +name = "x86_ata" +version = "0.1.0" +dependencies = [ + "bit_field", + "lazy_static", + "log", + "serde", + "spin 0.9.2", + "x86_64", +] + [[package]] name = "xshell" version = "0.1.17" diff --git a/ableos/Cargo.toml b/ableos/Cargo.toml index aadb93b5..f0d5c60a 100644 --- a/ableos/Cargo.toml +++ b/ableos/Cargo.toml @@ -29,6 +29,10 @@ run-args = [ # "-machine", "pcspk-audiodev=0", + "-hdb", + "../img.ext2", + + "-qmp", "unix:../qmp-sock,server,nowait", @@ -128,6 +132,7 @@ git = "https://git.ablecorp.us:443/able/externc-libm.git" riscv = "*" [target.'cfg(target_arch = "x86_64")'.dependencies] +x86_ata = { path = "../x86_ata" } bootloader = { version = "0.9.8", features = ["map_physical_memory"] } cpuio = { git = "https://git.ablecorp.us/ondra05/cpuio.git" } pic8259 = "0.10.1" diff --git a/ableos/src/arch/x86_64/interrupts.rs b/ableos/src/arch/x86_64/interrupts.rs index dde42a8f..30eb4dc1 100644 --- a/ableos/src/arch/x86_64/interrupts.rs +++ b/ableos/src/arch/x86_64/interrupts.rs @@ -247,6 +247,7 @@ pub fn bsod(src: BSODSource) -> ! { BSODSource::DoubleFault(_) => "https://git.ablecorp.us/able/ableos/wiki/Double-Faults", BSODSource::Panic(_) => { // + trace!("panic"); "https://git.ablecorp.us/able/ableos/wiki/" } }; diff --git a/ableos/src/kmain.rs b/ableos/src/kmain.rs index bd0afedd..88d5ae9f 100644 --- a/ableos/src/kmain.rs +++ b/ableos/src/kmain.rs @@ -47,6 +47,17 @@ pub fn kernel_main() -> ! { drop(ipc_service); + use x86_ata::{init, list, read, ATA_BLOCK_SIZE}; + // 1. Initialise ATA Subsystem. (Perform Once, on boot) + init().expect("Failed To Start ATA..."); + let mut buffer: [u8; ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE]; + // FIXME: Calls to read panic the kernel + // read(0, 0, 0, &mut buffer); + + // for abc in list() { + // trace!("{:?}", abc); + // } + x86_64::instructions::interrupts::without_interrupts(|| { let mut scheduler = SCHEDULER.lock(); // comment this out to resume normal use diff --git a/x86_ata/Cargo.toml b/x86_ata/Cargo.toml new file mode 100644 index 00000000..342b7d35 --- /dev/null +++ b/x86_ata/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "x86_ata" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = "*" +[dependencies.bit_field] +version = "0.10.0" + +[dependencies.lazy_static] +features = ["spin_no_std"] +version = "1.4.0" + +[dependencies.serde] +default-features = false +version = "1.0.126" + +[dependencies.spin] +version = "0.9.0" + +[dependencies.x86_64] +version = "0.14.3" diff --git a/x86_ata/src/lib.rs b/x86_ata/src/lib.rs new file mode 100644 index 00000000..15a9fcc8 --- /dev/null +++ b/x86_ata/src/lib.rs @@ -0,0 +1,380 @@ +#![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(()) +}