ATA device detection

This commit is contained in:
TheOddGarlic 2022-08-10 14:00:56 +03:00
parent 148c66eae2
commit 012452dec1
2 changed files with 229 additions and 24 deletions

View file

@ -35,6 +35,8 @@ pub struct PciDeviceInfo {
pub bus: u8, pub bus: u8,
pub device_id: DeviceID, pub device_id: DeviceID,
pub full_class: PciFullClass, pub full_class: PciFullClass,
pub command: u16,
pub status: u16,
pub prog_if: u8, pub prog_if: u8,
pub rev_id: u8, pub rev_id: u8,
pub header_type: u8, pub header_type: u8,
@ -52,6 +54,10 @@ impl PciDeviceInfo {
pub fn subclass(&self) -> PciClass { pub fn subclass(&self) -> PciClass {
PciClass::from_u8((self.full_class.as_u16() & 0xFF) as u8) 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 { impl Display for PciDeviceInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
@ -63,6 +69,11 @@ impl Display for PciDeviceInfo {
self.device, self.bus, vendor_name, device_id, self.device, self.bus, vendor_name, device_id,
)?; )?;
writeln!(f, "{}", self.full_class)?; writeln!(f, "{}", self.full_class)?;
writeln!(
f,
" Command: {:b} | Status: {:b}",
self.command, self.status
)?;
writeln!(f, " Revision ID: {}", self.rev_id)?; writeln!(f, " Revision ID: {}", self.rev_id)?;
writeln!(f, " Prog IF: 0b{:b}", self.prog_if)?; writeln!(f, " Prog IF: 0b{:b}", self.prog_if)?;
writeln!(f, " Header type: 0x{:X}", self.header_type)?; writeln!(f, " Header type: 0x{:X}", self.header_type)?;
@ -118,7 +129,10 @@ fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
return None; return None;
} }
let reg1 = unsafe { pci_config_read(bus, device, 0, 0x4) };
let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) }; 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 rev_id = (reg2 & 0x000000FF) as u8;
let prog_if = ((reg2 >> 8) & 0x000000FF) as u8; let prog_if = ((reg2 >> 8) & 0x000000FF) as u8;
let class = ((reg2 >> 16) & 0x0000FFFF) as u16; let class = ((reg2 >> 16) & 0x0000FFFF) as u16;
@ -158,6 +172,8 @@ fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
}, },
// vendor_id, // vendor_id,
full_class: pci_class, full_class: pci_class,
command,
status,
prog_if, prog_if,
rev_id, rev_id,
header_type, header_type,

View file

@ -13,8 +13,7 @@ use x86_64::VirtAddr;
use crate::arch::memory::BootInfoFrameAllocator; use crate::arch::memory::BootInfoFrameAllocator;
use super::port::Port; use super::{check_device, port::Port};
use super::{check_device, PciDeviceInfo};
const PRDT_START: u64 = 0x_ffff_ffff_0000_0000; const PRDT_START: u64 = 0x_ffff_ffff_0000_0000;
const BUFFER_START: u64 = 0x_ffff_ffff_0000_1000; const BUFFER_START: u64 = 0x_ffff_ffff_0000_1000;
@ -29,8 +28,32 @@ const BMIDTP_OFFSET: u16 = 4;
/// Bus Master IDE Secondary Offset /// Bus Master IDE Secondary Offset
const BMI_SECONDARY: u16 = 8; 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 { pub struct Piix {
device_info: PciDeviceInfo, ide_devices: Vec<IdeDevice>,
prdt_frame: Option<PhysFrame>, prdt_frame: Option<PhysFrame>,
buffer_frames: Option<Vec<PhysFrame>>, buffer_frames: Option<Vec<PhysFrame>>,
bmiba: u16, bmiba: u16,
@ -41,10 +64,55 @@ impl Piix {
pub fn new(bus: u8, device: u8) -> Option<Self> { pub fn new(bus: u8, device: u8) -> Option<Self> {
let device_info = check_device(bus, device)?; let device_info = check_device(bus, device)?;
trace!("device_info: {device_info}"); 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::<u8>::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; let bmiba = device_info.bars[4] & 0xFFFFFFFC;
Some(Self { Some(Self {
device_info, ide_devices,
prdt_frame: None, prdt_frame: None,
buffer_frames: None, buffer_frames: None,
bmiba: bmiba.try_into().ok()?, bmiba: bmiba.try_into().ok()?,
@ -130,49 +198,170 @@ impl Piix {
} }
unsafe { unsafe {
self.stop(false); self.load_prdt(Channel::Primary);
self.set_read(false); self.stop(Channel::Primary);
self.clear_status(false); self.set_read(Channel::Primary);
// (*bmiba).bmidtpp = self.prdt_frame.unwrap().start_address().as_u64().try_into()?; self.clear_status(Channel::Primary);
select_drive(Drive::Master, Channel::Primary);
} }
Ok(()) Ok(())
} }
unsafe fn stop(&self, secondary: bool) { unsafe fn load_prdt(&self, channel: Channel) {
let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIC_OFFSET; let addr = if channel.secondary() {
let mut bmic = Port::<u8>::new(addr).read(); BMI_SECONDARY
} else {
0
} + self.bmiba
+ BMIDTP_OFFSET;
Port::<u32>::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<u8> = Port::new(addr);
let mut bmic = port.read();
// stop ongoing transfer // stop ongoing transfer
bmic &= !1; bmic &= !1;
// write the new bmic // write the new bmic
Port::<u8>::new(addr).write(bmic); port.write(bmic);
} }
unsafe fn set_read(&self, secondary: bool) { unsafe fn set_read(&self, channel: Channel) {
let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIC_OFFSET; let addr = if channel.secondary() {
let mut bmic = Port::<u8>::new(addr).read(); BMI_SECONDARY
} else {
0
} + self.bmiba
+ BMIC_OFFSET;
let port: Port<u8> = Port::new(addr);
let mut bmic = port.read();
// mark bit 3 as 0 (read) // mark bit 3 as 0 (read)
bmic &= !(1 << 3); bmic &= !(1 << 3);
// write the new bmic // write the new bmic
Port::<u8>::new(addr).write(bmic); port.write(bmic);
} }
unsafe fn set_write(&self, secondary: bool) { unsafe fn set_write(&self, channel: Channel) {
let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIC_OFFSET; let addr = if channel.secondary() {
let mut bmic = Port::<u8>::new(addr).read(); BMI_SECONDARY
} else {
0
} + self.bmiba
+ BMIC_OFFSET;
let port: Port<u8> = Port::new(addr);
let mut bmic = port.read();
// mark bit 3 as 1 (write) // mark bit 3 as 1 (write)
bmic |= 1 << 3; bmic |= 1 << 3;
// write the new bmic // write the new bmic
Port::<u8>::new(addr).write(bmic); port.write(bmic);
} }
unsafe fn clear_status(&self, secondary: bool) { unsafe fn clear_status(&self, channel: Channel) {
let addr = if secondary { BMI_SECONDARY } else { 0 } + self.bmiba + BMIS_OFFSET; let addr = if channel.secondary() {
let mut bmis = Port::<u8>::new(addr).read(); BMI_SECONDARY
} else {
0
} + self.bmiba
+ BMIS_OFFSET;
let port: Port<u8> = Port::new(addr);
let mut bmis = port.read();
// write 1 to bits 1 (DMA error) and 2 (int status) which clears them // write 1 to bits 1 (DMA error) and 2 (int status) which clears them
bmis |= 1 << 1 | 1 << 2; bmis |= 1 << 1 | 1 << 2;
// write the new bmis // write the new bmis
Port::<u8>::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<u8> = 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<u8> = 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<u8> = 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)
} }
} }