ATA device detection
This commit is contained in:
parent
148c66eae2
commit
012452dec1
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue