From 012452dec1ac898224e3154148e5a0d6129743e0 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 10 Aug 2022 14:00:56 +0300 Subject: [PATCH] ATA device detection --- ableos/src/devices/pci/mod.rs | 16 +++ ableos/src/devices/pci/piix.rs | 237 +++++++++++++++++++++++++++++---- 2 files changed, 229 insertions(+), 24 deletions(-) diff --git a/ableos/src/devices/pci/mod.rs b/ableos/src/devices/pci/mod.rs index c97b349e..08b6e2ff 100644 --- a/ableos/src/devices/pci/mod.rs +++ b/ableos/src/devices/pci/mod.rs @@ -35,6 +35,8 @@ pub struct PciDeviceInfo { pub bus: u8, pub device_id: DeviceID, pub full_class: PciFullClass, + pub command: u16, + pub status: u16, pub prog_if: u8, pub rev_id: u8, pub header_type: u8, @@ -52,6 +54,10 @@ impl PciDeviceInfo { pub fn subclass(&self) -> PciClass { PciClass::from_u8((self.full_class.as_u16() & 0xFF) as u8) } + + pub unsafe fn config_read(&self, func: u8, offset: u8) -> u32 { + pci_config_read(self.bus, self.device, func, offset) + } } impl Display for PciDeviceInfo { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { @@ -63,6 +69,11 @@ impl Display for PciDeviceInfo { self.device, self.bus, vendor_name, device_id, )?; writeln!(f, "{}", self.full_class)?; + writeln!( + f, + " Command: {:b} | Status: {:b}", + self.command, self.status + )?; writeln!(f, " Revision ID: {}", self.rev_id)?; writeln!(f, " Prog IF: 0b{:b}", self.prog_if)?; writeln!(f, " Header type: 0x{:X}", self.header_type)?; @@ -118,7 +129,10 @@ fn check_device(bus: u8, device: u8) -> Option { return None; } + let reg1 = unsafe { pci_config_read(bus, device, 0, 0x4) }; let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) }; + let command = (reg1 & 0x0000FFFF) as u16; + let status = ((reg1 >> 16) & 0x0000FFFF) as u16; let rev_id = (reg2 & 0x000000FF) as u8; let prog_if = ((reg2 >> 8) & 0x000000FF) as u8; let class = ((reg2 >> 16) & 0x0000FFFF) as u16; @@ -158,6 +172,8 @@ fn check_device(bus: u8, device: u8) -> Option { }, // vendor_id, full_class: pci_class, + command, + status, prog_if, rev_id, header_type, diff --git a/ableos/src/devices/pci/piix.rs b/ableos/src/devices/pci/piix.rs index 1bf32e7b..606c58d6 100644 --- a/ableos/src/devices/pci/piix.rs +++ b/ableos/src/devices/pci/piix.rs @@ -13,8 +13,7 @@ use x86_64::VirtAddr; use crate::arch::memory::BootInfoFrameAllocator; -use super::port::Port; -use super::{check_device, PciDeviceInfo}; +use super::{check_device, port::Port}; const PRDT_START: u64 = 0x_ffff_ffff_0000_0000; const BUFFER_START: u64 = 0x_ffff_ffff_0000_1000; @@ -29,8 +28,32 @@ const BMIDTP_OFFSET: u16 = 4; /// Bus Master IDE Secondary Offset const BMI_SECONDARY: u16 = 8; +/// Primary command block offset +const PRIMARY_COMMAND: u16 = 0x01F0; + +/// Secondary command block offset +const SECONDARY_COMMAND: u16 = 0x0170; + +/// Drive/Head register offset +const DRIVE_HEAD_OFFSET: u16 = 6; + +/// Command/status register offset +const COMMAND_STATUS_OFFSET: u16 = 7; + +/// Secondary control block offset +const SECONDARY_CONTROL: u16 = 0x0374; + +/// Primary control block offset +const PRIMARY_CONTROL: u16 = 0x03F4; + +/// Alternative status offset +const ALT_STATUS_OFFSET: u16 = 2; + +/// ATA identification command +const CMD_IDENTIFY: u8 = 0xEC; + pub struct Piix { - device_info: PciDeviceInfo, + ide_devices: Vec, prdt_frame: Option, buffer_frames: Option>, bmiba: u16, @@ -41,10 +64,55 @@ impl Piix { pub fn new(bus: u8, device: u8) -> Option { let device_info = check_device(bus, device)?; trace!("device_info: {device_info}"); + let idetim = unsafe { device_info.config_read(0, 0x40) }; + trace!("idetim: {idetim:b}"); + // FIXME: enable the right bits in idetim (and sidetim) to use fast timings + + let mut ide_devices = Vec::with_capacity(4); + for ch in 0..2 { + let channel = if ch == 0 { + Channel::Primary + } else { + Channel::Secondary + }; + 'drive: for dr in 0..2 { + let drive = if dr == 0 { Drive::Master } else { Drive::Slave }; + + unsafe { + select_drive(drive, channel); + let status = ata_send_command(CMD_IDENTIFY, channel); + if status == 0 { + continue; // If status = 0, no device + } + + loop { + let status = { + let addr = if channel.secondary() { + SECONDARY_COMMAND + } else { + PRIMARY_COMMAND + } + COMMAND_STATUS_OFFSET; + Port::::new(addr).read() + }; + + if status & 1 == 1 { + // if error (bit 0), device is not ATA + // FIXME: ATAPI devices + continue 'drive; + } + if !((status >> 7) & 1 == 1) && (status >> 3) & 1 == 1 { + // BSY cleared, DRQ set, everything is right + break; + } + } + } + } + } + let bmiba = device_info.bars[4] & 0xFFFFFFFC; Some(Self { - device_info, + ide_devices, prdt_frame: None, buffer_frames: None, bmiba: bmiba.try_into().ok()?, @@ -130,49 +198,170 @@ impl Piix { } unsafe { - self.stop(false); - self.set_read(false); - self.clear_status(false); - // (*bmiba).bmidtpp = self.prdt_frame.unwrap().start_address().as_u64().try_into()?; + self.load_prdt(Channel::Primary); + self.stop(Channel::Primary); + self.set_read(Channel::Primary); + self.clear_status(Channel::Primary); + select_drive(Drive::Master, Channel::Primary); } Ok(()) } - unsafe fn stop(&self, secondary: bool) { - let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIC_OFFSET; - let mut bmic = Port::::new(addr).read(); + unsafe fn load_prdt(&self, channel: Channel) { + let addr = if channel.secondary() { + BMI_SECONDARY + } else { + 0 + } + self.bmiba + + BMIDTP_OFFSET; + Port::::new(addr).write( + self.prdt_frame + .unwrap() + .start_address() + .as_u64() + .try_into() + .unwrap(), + ); + } + + unsafe fn stop(&self, channel: Channel) { + let addr = if channel.secondary() { + BMI_SECONDARY + } else { + 0 + } + self.bmiba + + BMIC_OFFSET; + let port: Port = Port::new(addr); + let mut bmic = port.read(); // stop ongoing transfer bmic &= !1; // write the new bmic - Port::::new(addr).write(bmic); + port.write(bmic); } - unsafe fn set_read(&self, secondary: bool) { - let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIC_OFFSET; - let mut bmic = Port::::new(addr).read(); + unsafe fn set_read(&self, channel: Channel) { + let addr = if channel.secondary() { + BMI_SECONDARY + } else { + 0 + } + self.bmiba + + BMIC_OFFSET; + let port: Port = Port::new(addr); + let mut bmic = port.read(); // mark bit 3 as 0 (read) bmic &= !(1 << 3); // write the new bmic - Port::::new(addr).write(bmic); + port.write(bmic); } - unsafe fn set_write(&self, secondary: bool) { - let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIC_OFFSET; - let mut bmic = Port::::new(addr).read(); + unsafe fn set_write(&self, channel: Channel) { + let addr = if channel.secondary() { + BMI_SECONDARY + } else { + 0 + } + self.bmiba + + BMIC_OFFSET; + let port: Port = Port::new(addr); + let mut bmic = port.read(); // mark bit 3 as 1 (write) bmic |= 1 << 3; // write the new bmic - Port::::new(addr).write(bmic); + port.write(bmic); } - unsafe fn clear_status(&self, secondary: bool) { - let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIS_OFFSET; - let mut bmis = Port::::new(addr).read(); + unsafe fn clear_status(&self, channel: Channel) { + let addr = if channel.secondary() { + BMI_SECONDARY + } else { + 0 + } + self.bmiba + + BMIS_OFFSET; + let port: Port = Port::new(addr); + let mut bmis = port.read(); // write 1 to bits 1 (DMA error) and 2 (int status) which clears them bmis |= 1 << 1 | 1 << 2; // write the new bmis - Port::::new(addr).write(bmis); + port.write(bmis); + } +} + +unsafe fn select_drive(drive: Drive, channel: Channel) { + let addr = if channel.secondary() { + SECONDARY_COMMAND + } else { + PRIMARY_COMMAND + } + DRIVE_HEAD_OFFSET; + let port: Port = Port::new(addr); + let mut drive_command = port.read(); + if drive.slave() { + // mark bit 4 + drive_command |= 1 << 4; + } else { + // clear bit 4 + drive_command &= !(1 << 4); + } + // write the new drive/head register + port.write(drive_command); + ata_delay(channel); +} + +/// Send ATA command and read status afterwards +unsafe fn ata_send_command(command: u8, channel: Channel) -> u8 { + let addr = if channel.secondary() { + SECONDARY_COMMAND + } else { + PRIMARY_COMMAND + } + COMMAND_STATUS_OFFSET; + let port: Port = Port::new(addr); + port.write(command); + ata_delay(channel); + port.read() +} + +/// Read the alternate status register 14 times to create a ~420ns delay +unsafe fn ata_delay(channel: Channel) { + let addr = if channel.secondary() { + SECONDARY_CONTROL + } else { + PRIMARY_CONTROL + } + ALT_STATUS_OFFSET; + let port: Port = Port::new(addr); + for _ in 0..14 { + port.read(); + } +} + +struct IdeDevice { + pub channel: Channel, + pub drive: Drive, + pub signature: u16, + pub capabilities: u16, + pub command_sets: u32, + pub size: u64, +} + +#[derive(Copy, Clone, Debug)] +enum Channel { + Primary, + Secondary, +} + +impl Channel { + fn secondary(&self) -> bool { + matches!(self, Self::Secondary) + } +} + +#[derive(Copy, Clone, Debug)] +enum Drive { + Master, + Slave, +} + +impl Drive { + fn slave(&self) -> bool { + matches!(self, Self::Slave) } }