From 55c78c4fc39f6c7c026340e064e80b9f39fb0cda Mon Sep 17 00:00:00 2001 From: pjht Date: Wed, 21 Aug 2024 10:42:16 -0500 Subject: [PATCH] Add structures representing specific devices on a port --- src/ata_dev.rs | 99 ++++++++++++++++++++++++++++++++++++++ src/atapi_dev.rs | 24 ++++++++++ src/main.rs | 121 ++++++++++++++++++----------------------------- src/port.rs | 54 ++++++++++++++++++++- 4 files changed, 223 insertions(+), 75 deletions(-) create mode 100644 src/atapi_dev.rs diff --git a/src/ata_dev.rs b/src/ata_dev.rs index 8b13789..256239e 100644 --- a/src/ata_dev.rs +++ b/src/ata_dev.rs @@ -1 +1,100 @@ +use crate::{ + ata_command::ReadDmaExtCommand, + identify::IdentifyData, + port::{AhciPort, CommandIssueError}, +}; +pub struct AtaDevice<'a> { + port: &'a AhciPort, + #[allow(unused)] + identify_data: IdentifyData, + sector_size: usize, + capacity: usize, +} + +#[derive(Copy, Clone, Debug)] +pub enum AtaNewError { + DeviceTooBig, +} + +#[derive(Debug)] +pub enum AtaReadError { + NotAligned, + SizeNotSectMult, + SizeTooBig, + ReadOffEnd, + CommandError(#[allow(unused)] CommandIssueError), +} + +impl From for AtaReadError { + fn from(v: CommandIssueError) -> Self { + Self::CommandError(v) + } +} + +impl<'a> AtaDevice<'a> { + pub fn new(port: &'a AhciPort, identify_data: IdentifyData) -> Result { + let sect_sz_info_valid = identify_data.phys_log_sect_sz & 0xC000 == 0x4000; + let sector_size = if sect_sz_info_valid { + if identify_data.phys_log_sect_sz & 0x1000 == 0x1000 { + identify_data.log_sect_sz as usize + } else { + 512 + } + } else { + 512 + }; + + let num_sects = if identify_data.num_ua_log_sects != 0 { + identify_data.num_ua_log_sects as usize + } else { + identify_data.lba28_num_ua_log_sects as usize + }; + + let Some(capacity) = num_sects.checked_mul(sector_size) else { + return Err(AtaNewError::DeviceTooBig); + }; + + Ok(Self { + port, + identify_data, + sector_size, + capacity, + }) + } + + pub fn identify_data(&self) -> &IdentifyData { + &self.identify_data + } + + pub fn phys_no(&self) -> usize { + self.port.phys_no() + } + + pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<(), AtaReadError> { + if offset % self.sector_size != 0 { + return Err(AtaReadError::NotAligned); + }; + if buf.len() % self.sector_size != 0 { + return Err(AtaReadError::SizeNotSectMult); + }; + if buf.len() / self.sector_size > 65536 { + return Err(AtaReadError::SizeTooBig); + }; + if offset + buf.len() > self.capacity { + return Err(AtaReadError::ReadOffEnd); + }; + self.port.issue_data_in_command( + &ReadDmaExtCommand::new( + (offset / self.sector_size) as u64, + (buf.len() / self.sector_size) as u16, + ), + buf, + )?; + Ok(()) + } + + pub fn capacity(&self) -> usize { + self.capacity + } +} diff --git a/src/atapi_dev.rs b/src/atapi_dev.rs new file mode 100644 index 0000000..6f9eea1 --- /dev/null +++ b/src/atapi_dev.rs @@ -0,0 +1,24 @@ +use crate::{identify::IdentifyPacketData, port::AhciPort}; + +pub struct AtapiDevice<'a> { + port: &'a AhciPort, + #[allow(unused)] + identify_data: IdentifyPacketData, +} + +impl<'a> AtapiDevice<'a> { + pub fn new(port: &'a AhciPort, identify_data: IdentifyPacketData) -> Self { + Self { + port, + identify_data, + } + } + + pub fn identify_data(&self) -> &IdentifyPacketData { + &self.identify_data + } + + pub fn phys_no(&self) -> usize { + self.port.phys_no() + } +} diff --git a/src/main.rs b/src/main.rs index 7e8a8a9..3131fa8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,15 +22,16 @@ mod ahci_structs; mod ata_command; mod ata_dev; +mod atapi_dev; mod hba; mod identify; mod port; use std::os::mikros::{address_space::ACTIVE_SPACE, syscalls}; -use ata_command::{IdentifyCommand, IdentifyPacketCommand, ReadDmaExtCommand}; use hba::Hba; use itertools::Itertools; +use port::DeviceEnum; use uuid::Uuid; use x86_64::structures::paging::PageTableFlags; @@ -83,91 +84,68 @@ fn main() { let hba = unsafe { Hba::new(reg_base, syslog_client) }.unwrap(); for port in hba.ports() { - if !port.has_device() { + let Ok(device) = port.get_device() else { + syslog_client + .send_text_message( + "ahci", + format!("Port {}: Failed to identify", port.phys_no()), + ) + .unwrap(); + continue; + }; + + let Some(device) = device else { syslog_client .send_text_message("ahci", format!("Port {}: Empty", port.phys_no())) .unwrap(); continue; - } + }; - let mut ident_buf = [0; 512]; - - if let Ok(identify_data) = port.issue_data_in_command(&IdentifyCommand, &mut ident_buf) { - let sect_sz_info_valid = identify_data.phys_log_sect_sz & 0xC000 == 0x4000; - let sect_size = if sect_sz_info_valid { - if identify_data.phys_log_sect_sz & 0x1000 == 0x1000 { - identify_data.log_sect_sz as usize - } else { - 512 - } - } else { - 512 - }; - - let capacity = if identify_data.num_ua_log_sects != 0 { - identify_data.num_ua_log_sects as usize - } else { - identify_data.lba28_num_ua_log_sects as usize - }; - - syslog_client - .send_text_message( - "ahci", - format!( - "Port {}: {} {}, firmware {} ({})", - port.phys_no(), - identify_data.model, - identify_data.serial, - identify_data.firmware, - humansize::format_size(capacity * sect_size, humansize::BINARY) - ), - ) - .unwrap(); - } else { - let regs = port.regs().unwrap(); - - if regs.lba1 != 0x14 || regs.lba2 != 0xEB { + match device { + port::DeviceEnum::Ata(device) => { syslog_client .send_text_message( "ahci", - format!("Port {} failed to identify", port.phys_no()), + format!( + "Port {}: {} {}, firmware {} ({})", + device.phys_no(), + device.identify_data().model, + device.identify_data().serial, + device.identify_data().firmware, + humansize::format_size(device.capacity(), humansize::BINARY) + ), ) .unwrap(); - continue; } - - let Ok(identify_data) = - port.issue_data_in_command(&IdentifyPacketCommand, &mut ident_buf) - else { + port::DeviceEnum::Atapi(device) => { syslog_client .send_text_message( "ahci", - format!("Port {} failed to identify", port.phys_no()), + format!( + "Port {}: {} {}, firmware {}", + device.phys_no(), + device.identify_data().model, + device.identify_data().serial, + device.identify_data().firmware + ), ) .unwrap(); - continue; - }; - - syslog_client - .send_text_message( - "ahci", - format!( - "Port {}: {} {}, firmware {}", - port.phys_no(), - identify_data.model, - identify_data.serial, - identify_data.firmware - ), - ) - .unwrap(); + } + port::DeviceEnum::Other => { + syslog_client + .send_text_message("ahci", format!("Port {}: Unknown", port.phys_no())) + .unwrap(); + } } } let mut mbr = [0; 512]; - hba.ports()[0] - .issue_data_in_command(&ReadDmaExtCommand::new(0, 1), &mut mbr) - .unwrap(); + let DeviceEnum::Ata(ata_dev) = hba.ports()[0].get_device().unwrap().unwrap() else { + panic!(); + }; + + ata_dev.read(0, &mut mbr).unwrap(); let mbr_entries = (0..4) .map(|i| { @@ -206,9 +184,7 @@ fn main() { let mut gpt_header = [0; 512]; - hba.ports()[0] - .issue_data_in_command(&ReadDmaExtCommand::new(1, 1), &mut gpt_header) - .unwrap(); + ata_dev.read(512, &mut gpt_header).unwrap(); if &gpt_header[0..8] != b"EFI PART" { println!("Invalid GPT signature!"); @@ -239,13 +215,10 @@ fn main() { println!("Partition entries ending LBA: {part_table_end_lba}"); - let mut gpt_part_table = vec![0; part_table_num_lbas * 512]; + let mut gpt_part_table = vec![0; part_table_len.next_multiple_of(512)]; - hba.ports()[0] - .issue_data_in_command( - &ReadDmaExtCommand::new(part_table_start_lba as u64, part_table_num_lbas as u16), - gpt_part_table.as_mut_slice(), - ) + ata_dev + .read(part_table_start_lba * 512, &mut gpt_part_table) .unwrap(); for i in 0..num_parts { diff --git a/src/port.rs b/src/port.rs index 495c717..e7fbcc2 100644 --- a/src/port.rs +++ b/src/port.rs @@ -8,7 +8,9 @@ use crate::{ CommandHeader, CommandTableHeader, DeviceRegsRead, FisBuf, PortRegs, Prd, PxCMD, PxIS, PxSERR, PxSSTS, PxTFD, RegH2DFis, }, - ata_command::AtaCommandDataIn, + ata_command::{AtaCommandDataIn, IdentifyCommand, IdentifyPacketCommand}, + ata_dev::{AtaDevice, AtaNewError}, + atapi_dev::AtapiDevice, }; #[allow(clippy::module_name_repetitions)] @@ -30,6 +32,30 @@ pub enum CommandIssueError { DataTooSmall, } +#[derive(Copy, Clone, Debug)] +pub enum GetDeviceError { + CommandIssueError(CommandIssueError), + AtaNewError(AtaNewError), +} + +impl From for GetDeviceError { + fn from(v: AtaNewError) -> Self { + Self::AtaNewError(v) + } +} + +impl From for GetDeviceError { + fn from(v: CommandIssueError) -> Self { + Self::CommandIssueError(v) + } +} + +pub enum DeviceEnum<'a> { + Ata(AtaDevice<'a>), + Atapi(AtapiDevice<'a>), + Other, +} + impl AhciPort { pub fn new( regs: &'static PortRegs, @@ -213,6 +239,32 @@ impl AhciPort { Ok(command.process_data(buf)) } + pub fn get_device(&self) -> Result, GetDeviceError> { + if !self.has_device() { + return Ok(None); + } + + let mut ident_buf = [0; 512]; + + if let Ok(identify_data) = self.issue_data_in_command(&IdentifyCommand, &mut ident_buf) { + Ok(Some(DeviceEnum::Ata(AtaDevice::new(self, identify_data)?))) + } else { + let regs = self.regs().unwrap(); + + if regs.lba1 != 0x14 || regs.lba2 != 0xEB { + return Ok(Some(DeviceEnum::Other)); + } + + let identify_data = + self.issue_data_in_command(&IdentifyPacketCommand, &mut ident_buf)?; + + Ok(Some(DeviceEnum::Atapi(AtapiDevice::new( + self, + identify_data, + )))) + } + } + fn has_fatal_error(&self) -> bool { self.regs.PxIS.read(PxIS::HBFS) > 0 || self.regs.PxIS.read(PxIS::HBDS) > 0