forked from AbleOS/ableos
ATA device detection
This commit is contained in:
parent
148c66eae2
commit
012452dec1
|
@ -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<PciDeviceInfo> {
|
|||
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<PciDeviceInfo> {
|
|||
},
|
||||
// vendor_id,
|
||||
full_class: pci_class,
|
||||
command,
|
||||
status,
|
||||
prog_if,
|
||||
rev_id,
|
||||
header_type,
|
||||
|
|
|
@ -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<IdeDevice>,
|
||||
prdt_frame: Option<PhysFrame>,
|
||||
buffer_frames: Option<Vec<PhysFrame>>,
|
||||
bmiba: u16,
|
||||
|
@ -41,10 +64,55 @@ impl Piix {
|
|||
pub fn new(bus: u8, device: u8) -> Option<Self> {
|
||||
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::<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;
|
||||
|
||||
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::<u8>::new(addr).read();
|
||||
unsafe fn load_prdt(&self, channel: Channel) {
|
||||
let addr = if channel.secondary() {
|
||||
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
|
||||
bmic &= !1;
|
||||
// write the new bmic
|
||||
Port::<u8>::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::<u8>::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<u8> = Port::new(addr);
|
||||
let mut bmic = port.read();
|
||||
// mark bit 3 as 0 (read)
|
||||
bmic &= !(1 << 3);
|
||||
// write the new bmic
|
||||
Port::<u8>::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::<u8>::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<u8> = Port::new(addr);
|
||||
let mut bmic = port.read();
|
||||
// mark bit 3 as 1 (write)
|
||||
bmic |= 1 << 3;
|
||||
// write the new bmic
|
||||
Port::<u8>::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::<u8>::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<u8> = 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::<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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue