Files
CappuccinOS/src/drivers/storage/ide.rs

720 lines
20 KiB
Rust
Executable File

use core::mem::size_of;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use crate::{
arch::io::{inb, insw, inw, outb, outsw},
drivers::{
fs::{devfs::DeviceOperations, fat, vfs::add_vfs},
storage::{GPTHeader, GPTPartitionEntry, Partition, MBR},
},
libs::{sync::Mutex, uuid::Uuid},
};
use super::BlockDevice;
const ATA_SECTOR_SIZE: usize = 512;
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
enum ATADriveStatus {
Error = 0x01,
Index = 0x02,
Corrupt = 0x04,
DataReqReady = 0x08,
DriveSeekDone = 0x10,
WriteFault = 0x20,
Ready = 0x40,
Busy = 0x80,
NotPresent = 0xFF,
}
// u8 == ATADriveStatus
impl PartialEq<ATADriveStatus> for u8 {
fn eq(&self, other: &ATADriveStatus) -> bool {
return self & (*other as u8) != 0;
}
}
impl core::convert::From<u8> for ATADriveStatus {
fn from(value: u8) -> Self {
match value {
0x01 => Self::Error,
0x02 => Self::Index,
0x04 => Self::Corrupt,
0x08 => Self::DataReqReady,
0x10 => Self::DriveSeekDone,
0x20 => Self::WriteFault,
0x40 => Self::Ready,
0x80 => Self::Busy,
_ => Self::NotPresent,
}
}
}
// #[repr(u8)]
// enum ATADriveError {
// AddressMarkNotFound = 0x01,
// Track0NotFound = 0x02,
// CommandAborted = 0x04,
// MediaChangeReq = 0x08,
// IDNotFound = 0x10,
// MediaChanged = 0x20,
// UncorrectableData = 0x40,
// BadBlock = 0x80,
// }
#[repr(u8)]
#[allow(dead_code)]
enum ATADriveCommand {
ReadPIO = 0x20,
ReadPIOExt = 0x24,
ReadDMA = 0xC8,
ReadDMAExt = 0x25,
WritePIO = 0x30,
WritePIOExt = 0x34,
WriteDMA = 0xCA,
WriteDMAExt = 0x35,
CacheFlush = 0xE7,
CacheFlushExt = 0xEA,
Packet = 0xA0,
IdentifyPacket = 0xA1,
Identify = 0xEC,
}
// #[repr(u8)]
// enum ATADriveIdentifyResponse {
// DeviceType = 0x00,
// Cylinders = 0x02,
// Heads = 0x06,
// Sectors = 0x0C,
// Serial = 0x14,
// Model = 0x36,
// Capabilities = 0x62,
// FieldValid = 0x6A,
// MaxLBA = 0x78,
// CommandSets = 0xA4,
// MaxLBAExt = 0xC8,
// }
#[repr(u16)]
enum IDEDriveType {
Pata,
PataPi,
Sata,
SataPi,
}
impl IDEDriveType {
/// Determines the ATA device type based on the values of the LBA mid and LBA high
/// ports after an identify device command has been issued, but before the response has been read.
fn from_lba(lba_mid: u8, lba_high: u8) -> Option<IDEDriveType> {
match (lba_mid, lba_high) {
(0x00, 0x00) => Some(IDEDriveType::Pata),
(0x14, 0xEB) => Some(IDEDriveType::PataPi),
(0x3C, 0xC3) => Some(IDEDriveType::Sata),
(0x69, 0x96) => Some(IDEDriveType::SataPi),
_ => None,
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
enum ATADriveType {
Parent = 0 << 4,
Child = 1 << 4,
}
#[repr(u8)]
#[allow(dead_code)]
enum ATADriveDataRegister {
Data = 0x00,
ErrorAndFeatures = 0x01,
// Features = 0x01,
SectorCount0 = 0x02,
LBA0 = 0x03,
LBA1 = 0x04,
LBA2 = 0x05,
DeviceSelect = 0x06,
CommandAndStatus = 0x07,
// Status = 0x07,
SectorCount1 = 0x08,
LBA3 = 0x09,
LBA4 = 0x0A,
LBA5 = 0x0B,
}
#[repr(u8)]
#[allow(dead_code)]
enum ATADriveControlRegister {
ControlAndAltStatus = 0x02,
DeviceAddress = 0x03,
}
#[repr(u8)]
#[allow(dead_code)]
enum ATADriveChannels {
Primary = 0x00,
Secondary = 0x01,
}
#[repr(u8)]
enum ATADriveDirection {
Read = 0x00,
Write = 0x01,
}
pub fn init() {
ide_initialize(0x1F0, 0x3F6, 0x170, 0x376, 0x000);
}
#[derive(Debug)]
struct ATABus {
io_bar: u16,
control_bar: u16,
}
impl ATABus {
fn new(io_bar: u16, control_bar: u16) -> Arc<Self> {
let io_bar = io_bar & 0xFFFC;
let control_bar = control_bar & 0xFFFC;
return Arc::from(Self {
io_bar,
control_bar,
});
}
pub fn select(&self, drive: u8) {
outb(
self.io_bar + ATADriveDataRegister::DeviceSelect as u16,
drive,
);
}
pub fn send_command(&self, command: ATADriveCommand) {
outb(
self.io_bar + ATADriveDataRegister::CommandAndStatus as u16,
command as u8,
);
}
pub fn status(&self) -> u8 {
// Waste 400ns
for _ in 0..4 {
inb(self.control_bar + ATADriveControlRegister::ControlAndAltStatus as u16);
}
return inb(self.io_bar + ATADriveDataRegister::CommandAndStatus as u16);
}
fn wait_for_drive_ready(&self) -> Result<(), ()> {
loop {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
crate::arch::pause();
let status = self.status();
if status == ATADriveStatus::Error || status == ATADriveStatus::WriteFault {
return Err(());
}
if status == ATADriveStatus::Busy {
continue;
}
if status == ATADriveStatus::DataReqReady {
return Ok(());
}
}
}
pub fn await_busy(&self) {
while self.status() == ATADriveStatus::Busy {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
crate::arch::pause();
}
}
pub fn identify(&self, drive: ATADriveType) -> Result<Arc<[u8; ATA_SECTOR_SIZE]>, ()> {
self.select(0xA0 | drive as u8);
outb(self.io_bar + ATADriveDataRegister::SectorCount0 as u16, 0);
outb(self.io_bar + ATADriveDataRegister::LBA0 as u16, 0);
outb(self.io_bar + ATADriveDataRegister::LBA1 as u16, 0);
outb(self.io_bar + ATADriveDataRegister::LBA2 as u16, 0);
// disable interrupts by setting bit 2 in the control port
outb(self.control_bar + 0x0C - 0x0A, 2);
self.send_command(ATADriveCommand::Identify);
if self.status() == 0x00 {
// drive did not respond to identify command
// therefore, the drive is not present
return Err(());
}
while self.status() == ATADriveStatus::Busy {
let lba_mid = inb(self.io_bar + ATADriveDataRegister::LBA1 as u16);
let lba_high = inb(self.io_bar + ATADriveDataRegister::LBA2 as u16);
if lba_mid != 0 || lba_high != 0 {
return Err(());
}
}
let lba_mid = inb(self.io_bar + ATADriveDataRegister::LBA1 as u16);
let lba_high = inb(self.io_bar + ATADriveDataRegister::LBA2 as u16);
match IDEDriveType::from_lba(lba_mid, lba_high) {
Some(IDEDriveType::Pata) => {
// The only type we support, for now :tm:
}
_ => return Err(()),
};
let mut buffer = [0u8; ATA_SECTOR_SIZE];
self.wait_for_drive_ready()
.map_err(|_| crate::log_error!("Error before issuing Identify command."))?;
for chunk in buffer.chunks_exact_mut(core::mem::size_of::<u16>()) {
let word = inw(self.io_bar + ATADriveDataRegister::Data as u16);
chunk.copy_from_slice(&word.to_le_bytes());
}
return Ok(Arc::from(buffer));
}
pub fn read(
&self,
drive: ATADriveType,
sector: u64,
sector_count: usize,
) -> Result<Arc<[u8]>, ()> {
let mut buffer: Vec<u8> = Vec::with_capacity(ATA_SECTOR_SIZE * sector_count);
unsafe { buffer.set_len(buffer.capacity()) };
self.ide_access(
drive,
sector,
sector_count,
ATADriveDirection::Read,
&mut buffer,
)?;
return Ok(Arc::from(buffer));
}
pub fn write(
&self,
drive: ATADriveType,
sector: u64,
sector_count: usize,
buffer: &[u8],
) -> Result<(), ()> {
if buffer.len() < ATA_SECTOR_SIZE * sector_count {
return Err(());
}
let mut mut_buf: Vec<u8> = Vec::new();
mut_buf.extend(buffer);
self.ide_access(
drive,
sector,
sector_count,
ATADriveDirection::Write,
&mut mut_buf,
)?;
return Ok(());
}
fn ide_access(
&self,
drive: ATADriveType,
sector: u64,
sector_count: usize,
direction: ATADriveDirection,
buffer: &mut [u8],
) -> Result<(), ()> {
self.await_busy();
let using_lba48 = sector >= (1 << 28) - 1;
if using_lba48 {
self.select(0x40 | (drive as u8));
// High bytes
outb(
self.io_bar + ATADriveDataRegister::SectorCount0 as u16,
(sector_count >> 8) as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA0 as u16,
(sector >> 24) as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA1 as u16,
(sector >> 32) as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA2 as u16,
(sector >> 40) as u8,
);
// Low bytes
outb(
self.io_bar + ATADriveDataRegister::SectorCount0 as u16,
sector_count as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA0 as u16,
sector as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA1 as u16,
(sector >> 8) as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA2 as u16,
(sector >> 16) as u8,
);
match direction {
ATADriveDirection::Read => self.send_command(ATADriveCommand::ReadPIOExt),
ATADriveDirection::Write => self.send_command(ATADriveCommand::WritePIOExt),
}
} else {
self.select(0xE0 | (drive as u8) | ((sector >> 24) as u8 & 0x0F));
outb(
self.io_bar + ATADriveDataRegister::SectorCount0 as u16,
sector_count as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA0 as u16,
sector as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA1 as u16,
(sector >> 8) as u8,
);
outb(
self.io_bar + ATADriveDataRegister::LBA2 as u16,
(sector >> 16) as u8,
);
match direction {
ATADriveDirection::Read => self.send_command(ATADriveCommand::ReadPIO),
ATADriveDirection::Write => self.send_command(ATADriveCommand::WritePIO),
}
}
// sector count * 512 = bytes in array
let array_size = (sector_count) * ATA_SECTOR_SIZE;
// Since this is an internal function, this should never fail
assert!(buffer.len() >= array_size);
let mut buffer_offset = 0;
for _ in 0..sector_count {
self.wait_for_drive_ready()
.map_err(|_| crate::log_error!("Error reading IDE Device"))?;
// # Safety
//
// We know that buffer is the exact size of count, so it will never panic:tm:
match direction {
ATADriveDirection::Read => unsafe {
insw(
self.io_bar + ATADriveDataRegister::Data as u16,
(buffer.as_mut_ptr() as *mut u16).add(buffer_offset),
ATA_SECTOR_SIZE / size_of::<u16>(),
);
},
ATADriveDirection::Write => unsafe {
outsw(
self.io_bar + ATADriveDataRegister::Data as u16,
(buffer.as_mut_ptr() as *mut u16).add(buffer_offset),
ATA_SECTOR_SIZE / size_of::<u16>(),
)
},
}
buffer_offset += ATA_SECTOR_SIZE / size_of::<u16>();
}
return Ok(());
}
fn software_reset(&self) {
// Procedure is (1) set the SRST bit, (2) wait 5us, (3) clear the SRST bit.
outb(
self.io_bar + ATADriveControlRegister::ControlAndAltStatus as u16,
0x04,
);
// We wait 5us by reading the status port 50 times (each read takes 100ns)
for _ in 0..10 {
self.status(); // reads status port 5 times.
}
outb(
self.io_bar + ATADriveControlRegister::ControlAndAltStatus as u16,
0x00,
);
}
}
#[derive(Debug)]
struct ATADrive {
bus: Arc<ATABus>,
identify_data: Arc<[u8; ATA_SECTOR_SIZE]>,
drive_type: ATADriveType,
}
impl ATADrive {
pub fn new(bus: Arc<ATABus>, drive: ATADriveType) -> Result<Self, ()> {
let identify_data = bus.identify(drive)?;
let capabilities_bytes = &identify_data[98..100];
assert_eq!(capabilities_bytes.len(), 2);
let capabilities = (capabilities_bytes[0] as u16) | ((capabilities_bytes[1] as u16) << 8);
if capabilities & 0x200 == 0 {
// Old AF CHS Drive, just ignore it
// for now:tm:
return Err(());
}
return Ok(Self {
bus,
identify_data,
drive_type: drive,
});
}
fn sector_count(&self) -> u64 {
let sectors = self.identify_data[120..].as_ptr();
return unsafe { *(sectors as *const u32) } as u64;
}
pub fn as_ptr(&self) -> *const ATADrive {
return core::ptr::addr_of!(*self);
}
}
impl BlockDevice for ATADrive {
fn read(&self, sector: u64, sector_count: usize) -> Result<Arc<[u8]>, ()> {
if (sector + sector_count as u64) > self.sector_count() {
return Err(());
}
self.bus.software_reset();
return self.bus.read(self.drive_type, sector, sector_count);
}
fn sector_count(&self) -> u64 {
let sectors = self.identify_data[120..].as_ptr();
return unsafe { *(sectors as *const u32) } as u64;
}
fn write(&self, sector: u64, buffer: &[u8]) -> Result<(), ()> {
let sector_count = buffer.len() / 512;
self.bus.software_reset();
return self
.bus
.write(self.drive_type, sector, sector_count, buffer);
}
}
static DRIVES: Mutex<Vec<ATADrive>> = Mutex::new(Vec::new());
// TODO: This code is pretty much just the C from @Moldytzu's mOS
// This code could probably be made better and more device agnostic
// But that's TODO obviously
fn ide_initialize(bar0: u32, bar1: u32, _bar2: u32, _bar3: u32, _bar4: u32) {
let mut drives_lock = DRIVES.lock();
let io_port_base = bar0 as u16;
let control_port_base = bar1 as u16;
let bus = ATABus::new(io_port_base, control_port_base);
for i in 0..2 {
let drive_type = if i == 0 {
ATADriveType::Parent
} else {
ATADriveType::Child
};
let drive = ATADrive::new(bus.clone(), drive_type);
if let Ok(drive) = drive {
drives_lock.push(drive);
}
}
crate::log_info!(
"ATA: Detected {} drive{}",
drives_lock.len(),
match drives_lock.len() {
1 => "",
_ => "s",
}
);
for drive in drives_lock.iter() {
let sectors = drive.sector_count();
crate::log_info!(
"ATA: Drive 0 has {} sectors ({} MB)",
sectors,
(sectors * ATA_SECTOR_SIZE as u64) / 1024 / 1024
);
let mbr_sector: MBR = (*drive.read(0, 1).expect("Failed to read first sector")).into();
if u16::from_le_bytes(mbr_sector.signature) != 0xAA55 {
panic!("MBR is corrupted!");
}
let mbr_partitions = mbr_sector.partitions();
if mbr_partitions[0].partition_type != 0xEE {
panic!("MBR disks are unsupported")
}
let gpt_sector = drive.read(1, 1).expect("Failed to read sector 2");
let mut array = [0u8; 512];
array.copy_from_slice(&gpt_sector[..512]);
let gpt = GPTHeader::new(&array);
let mut partitions: Vec<Partition> = Vec::with_capacity(gpt.partition_entry_count as usize);
let partition_sector = drive
.read(
2,
(gpt.partition_entry_count * gpt.partition_entry_size) as usize / ATA_SECTOR_SIZE,
)
.expect("Failed to read partition table");
crate::println!(
"{}, {}, {}, {:X?}",
(gpt.partition_entry_count * gpt.partition_entry_size) as usize / ATA_SECTOR_SIZE,
gpt.partition_entry_count,
gpt.partition_entry_size,
gpt.guid
);
for i in 0..gpt.partition_entry_count {
let entry_offset = (i * gpt.partition_entry_size) as usize;
let partition_type_guid_bytes: [u8; 16] = partition_sector
[entry_offset..entry_offset + 16]
.try_into()
.unwrap();
let partition_type_guid = Uuid::from(partition_type_guid_bytes);
if partition_type_guid == "00000000-0000-0000-0000-000000000000 " {
continue;
}
let unique_partition_guid_bytes: [u8; 16] = partition_sector
[entry_offset + 16..entry_offset + 32]
.try_into()
.unwrap();
let unique_partition_guid = Uuid::from(unique_partition_guid_bytes);
let start_sector = u64::from_le_bytes(
partition_sector[entry_offset + 32..entry_offset + 40]
.try_into()
.unwrap(),
);
let end_sector = u64::from_le_bytes(
partition_sector[entry_offset + 40..entry_offset + 48]
.try_into()
.unwrap(),
);
let attributes = u64::from_le_bytes(
partition_sector[entry_offset + 48..entry_offset + 56]
.try_into()
.unwrap(),
);
let partition_name = partition_sector[entry_offset + 56..entry_offset + 128]
.try_into()
.unwrap();
// Store the parsed information in the partition_entries array
partitions.push(Partition::GPTPartition((
GPTPartitionEntry {
partition_type_guid,
unique_partition_guid,
start_sector,
end_sector,
attributes,
partition_name,
},
drive.as_ptr(),
)));
}
for &partition in partitions.iter() {
match partition {
Partition::GPTPartition(gpt_partition) => {
if gpt_partition.0.partition_type_guid != "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
{
continue;
}
let fat_fs = fat::FatFs::new(partition);
if fat_fs.is_err() {
continue;
}
let fat_fs = fat_fs.unwrap();
// TODO
let _ = add_vfs("/", Box::new(fat_fs));
// let vfs = crate::drivers::fs::vfs::Vfs::new(
// Box::new(fat_fs),
// &format!("{}", gpt_partition.0.partition_type_guid),
// );
// crate::drivers::fs::vfs::VFS_INSTANCES.lock().push(vfs);
// crate::println!(
// "{:?}",
// crate::drivers::fs::vfs::VFS_INSTANCES
// .lock()
// .read()
// .last()
// .unwrap()
// .open("/example.txt")
// .unwrap()
// .read()
// );
}
_ => todo!("Handle MBR!"),
}
}
}
}