From 15a5e210ef92dcea500ec2c88f14c785235c5fe1 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sat, 1 Feb 2025 14:42:45 -0800 Subject: [PATCH] Move port controller to separate file. --- rust/sys/denali/src/ahci/controller.rs | 277 +------------------- rust/sys/denali/src/ahci/mod.rs | 3 +- rust/sys/denali/src/ahci/port_controller.rs | 265 +++++++++++++++++++ rust/sys/denali/src/denali_server.rs | 2 +- 4 files changed, 279 insertions(+), 268 deletions(-) create mode 100644 rust/sys/denali/src/ahci/port_controller.rs diff --git a/rust/sys/denali/src/ahci/controller.rs b/rust/sys/denali/src/ahci/controller.rs index 1508574..d0656a6 100644 --- a/rust/sys/denali/src/ahci/controller.rs +++ b/rust/sys/denali/src/ahci/controller.rs @@ -1,28 +1,18 @@ -use core::array; -use core::future::Future; -use core::ops::Deref; -use core::ops::DerefMut; -use core::pin::Pin; -use core::task::{Context, Poll, Waker}; - -use alloc::boxed::Box; use alloc::sync::Arc; -use mammoth::cap::Capability; -use mammoth::sync::Mutex; -use mammoth::syscall; -use mammoth::{mem, thread}; - -use mammoth::{mem::MemoryRegion, zion::ZError}; - -use crate::ahci::command::FisType; -use crate::ahci::port::{ - AhciDeviceDetection, AhciInterfacePowerManagement, AhciPortInterruptStatus, +use mammoth::{ + cap::Capability, + mem::{self, MemoryRegion}, + sync::Mutex, + thread, + zion::ZError, }; -use super::command::{ - CommandList, CommandTable, HostToDeviceRegisterFis, ReceivedFis, SataCommand, +use crate::ahci::{ + port::{AhciDeviceDetection, AhciInterfacePowerManagement}, + port_controller::Command, }; -use super::{hba::AhciHba, port::AhciPortHba}; + +use super::{hba::AhciHba, port::AhciPortHba, port_controller::PortController}; #[derive(Debug)] #[repr(C, packed)] @@ -206,248 +196,3 @@ pub fn spawn_irq_thread(controller: Arc) -> thread::JoinHandle { pub async fn identify_ports(controller: Arc) { controller.identify_ports().await.unwrap(); } - -enum CommandStatus { - Empty, - NotSent, - Pending(Waker), - Complete, -} - -struct CommandFuture { - status: Arc>, - trigger: Box, -} - -impl CommandFuture { - fn new(status: Arc>, trigger: Box) -> Self { - Self { status, trigger } - } -} - -impl Drop for CommandFuture { - fn drop(&mut self) { - *self.status.lock() = CommandStatus::Empty; - } -} - -impl Future for CommandFuture { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let s = self.deref_mut(); - let mut status = s.status.lock(); - match status.deref() { - CommandStatus::NotSent => { - *status = CommandStatus::Pending(cx.waker().clone()); - (s.trigger)(); - Poll::Pending - } - CommandStatus::Pending(_) => Poll::Pending, - CommandStatus::Complete => Poll::Ready(()), - CommandStatus::Empty => panic!("Polling empty command slot"), - } - } -} - -pub struct Command { - command: SataCommand, - lba: u64, - sector_cnt: u16, - paddr: u64, - - memory_region: Option, -} - -impl Command { - pub fn identify() -> Result { - let (memory_region, paddr) = syscall::memory_object_contiguous_physical(512)?; - - Ok(Self { - command: SataCommand::IdentifyDevice, - lba: 0, - sector_cnt: 1, - paddr, - memory_region: Some(memory_region), - }) - } - - pub fn read(lba: u64, lba_count: u16) -> Result { - let (memory_region, paddr) = - syscall::memory_object_contiguous_physical(512 * (lba_count as u64))?; - - Ok(Self { - command: SataCommand::DmaReadExt, - lba, - sector_cnt: lba_count, - paddr, - memory_region: Some(memory_region), - }) - } - - pub fn read_manual(lba: u64, lba_count: u16, paddr: u64) -> Self { - Self { - command: SataCommand::DmaReadExt, - lba, - sector_cnt: lba_count, - paddr: paddr, - memory_region: None, - } - } - - pub fn release_mem_cap(&mut self) -> u64 { - self.memory_region.take().unwrap().release() - } -} - -impl From<&Command> for HostToDeviceRegisterFis { - fn from(val: &Command) -> Self { - HostToDeviceRegisterFis::new_command(val.command, val.lba, val.sector_cnt) - } -} - -struct CommandStructures { - command_list: &'static mut CommandList, - received_fis: &'static mut ReceivedFis, - command_tables: &'static mut [CommandTable; 32], -} - -struct PortController { - ahci_port_hba: Arc>, - command_structures: Mutex, - - command_slots: [Arc>; 32], -} - -impl PortController { - fn new(ahci_port_hba: &'static mut AhciPortHba) -> Self { - let (command_vaddr, command_paddr) = mem::map_physical_and_leak(0x2500); - ahci_port_hba.init(command_paddr, command_paddr + 0x400); - - let command_list = unsafe { (command_vaddr as *mut CommandList).as_mut().unwrap() }; - let received_fis = unsafe { - ((command_vaddr + 0x400) as *mut ReceivedFis) - .as_mut() - .unwrap() - }; - let command_tables = unsafe { - ((command_vaddr + 0x500) as *mut [CommandTable; 32]) - .as_mut() - .unwrap() - }; - - // This leaves space for 8 prdt entries. - for i in 0..32 { - command_list[i].command_table_base_addr = - (command_paddr + 0x500) + (0x100 * (i as u64)); - } - - let command_slots = array::from_fn(|_| Arc::new(Mutex::new(CommandStatus::Empty))); - - Self { - ahci_port_hba: Arc::new(Mutex::new(ahci_port_hba)), - command_structures: Mutex::new(CommandStructures { - command_list, - received_fis, - command_tables, - }), - command_slots, - } - } - - fn get_signature(&self) -> u32 { - self.ahci_port_hba.lock().signature.read() - } - - fn issue_command(&self, command: &Command) -> Result { - let slot = self.select_slot()?; - let command_slot = self.command_slots[slot].clone(); - - let ahci_port_hba_clone = self.ahci_port_hba.clone(); - let slot_clone = slot; - let future = CommandFuture::new( - command_slot, - Box::new(move || { - ahci_port_hba_clone.lock().issue_command(slot_clone); - }), - ); - - let mut command_structures = self.command_structures.lock(); - - command_structures.command_tables[slot] - .command_fis - .host_to_device = command.into(); - - command_structures.command_tables[slot].prdt[0].region_address = command.paddr; - command_structures.command_tables[slot].prdt[0].byte_count = - 512 * (command.sector_cnt as u32); - - command_structures.command_list[slot].prd_table_length = 1; - - command_structures.command_list[slot].command = - (size_of::() as u16 / 4) & 0x1F; - command_structures.command_list[slot].command |= 1 << 7; - - Ok(future) - } - - fn select_slot(&self) -> Result { - // TODO: We have a race condition here. - for i in 0..self.command_slots.len() { - let mut slot = self.command_slots[i].lock(); - if matches!(*slot, CommandStatus::Empty) { - *slot = CommandStatus::NotSent; - return Ok(i); - } - } - Err(ZError::EXHAUSTED) - } - - fn handle_interrupt(&self) { - let int_status = self.ahci_port_hba.lock().interrupt_status.read(); - if int_status.device_to_host_register_fis_interrupt() { - let received_fis = self - .command_structures - .lock() - .received_fis - .device_to_host_register_fis; - assert_eq!( - received_fis.fis_type as u8, - FisType::RegisterDeviceToHost as u8 - ); - if received_fis.error != 0 { - mammoth::debug!("D2H err: {:#0x}", received_fis.error); - mammoth::debug!("Status: {:#0x}", received_fis.status); - } - - self.ahci_port_hba.lock().interrupt_status.write( - AhciPortInterruptStatus::new().with_device_to_host_register_fis_interrupt(true), - ); - } - if int_status.pio_setup_fis_interrupt() { - self.ahci_port_hba - .lock() - .interrupt_status - .write(AhciPortInterruptStatus::new().with_pio_setup_fis_interrupt(true)); - } - - for i in 0..32 { - let int_offset = 1 << i; - - // If there is no longer a command issued on a slot and we have something in - // the command list that is pending we know that this is the command that finished. - if (self.ahci_port_hba.lock().command_issue.read() & int_offset) != int_offset { - let mut command_status = self.command_slots[i].lock(); - let mut did_complete = false; - if let CommandStatus::Pending(ref waker) = command_status.deref() { - waker.wake_by_ref(); - did_complete = true; - } - - if did_complete { - *command_status = CommandStatus::Complete; - } - } - } - } -} diff --git a/rust/sys/denali/src/ahci/mod.rs b/rust/sys/denali/src/ahci/mod.rs index a38f81f..f154971 100644 --- a/rust/sys/denali/src/ahci/mod.rs +++ b/rust/sys/denali/src/ahci/mod.rs @@ -2,8 +2,9 @@ mod command; mod controller; mod hba; mod port; +mod port_controller; pub use controller::identify_ports; pub use controller::spawn_irq_thread; pub use controller::AhciController; -pub use controller::Command; +pub use port_controller::Command; diff --git a/rust/sys/denali/src/ahci/port_controller.rs b/rust/sys/denali/src/ahci/port_controller.rs new file mode 100644 index 0000000..47ce356 --- /dev/null +++ b/rust/sys/denali/src/ahci/port_controller.rs @@ -0,0 +1,265 @@ +use core::{ + array, + future::Future, + ops::Deref, + ops::DerefMut, + pin::Pin, + task::{Context, Poll, Waker}, +}; + +use alloc::boxed::Box; +use alloc::sync::Arc; +use mammoth::{cap::Capability, mem, sync::Mutex, syscall, zion::ZError}; + +use crate::ahci::command::FisType; +use crate::ahci::port::AhciPortInterruptStatus; + +use super::command::{ + CommandList, CommandTable, HostToDeviceRegisterFis, ReceivedFis, SataCommand, +}; +use super::port::AhciPortHba; + +enum CommandStatus { + Empty, + NotSent, + Pending(Waker), + Complete, +} + +pub struct CommandFuture { + status: Arc>, + trigger: Box, +} + +impl CommandFuture { + fn new(status: Arc>, trigger: Box) -> Self { + Self { status, trigger } + } +} + +impl Drop for CommandFuture { + fn drop(&mut self) { + *self.status.lock() = CommandStatus::Empty; + } +} + +impl Future for CommandFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let s = self.deref_mut(); + let mut status = s.status.lock(); + match status.deref() { + CommandStatus::NotSent => { + *status = CommandStatus::Pending(cx.waker().clone()); + (s.trigger)(); + Poll::Pending + } + CommandStatus::Pending(_) => Poll::Pending, + CommandStatus::Complete => Poll::Ready(()), + CommandStatus::Empty => panic!("Polling empty command slot"), + } + } +} + +pub struct Command { + command: SataCommand, + lba: u64, + sector_cnt: u16, + paddr: u64, + + memory_region: Option, +} + +impl Command { + pub fn identify() -> Result { + let (memory_region, paddr) = syscall::memory_object_contiguous_physical(512)?; + + Ok(Self { + command: SataCommand::IdentifyDevice, + lba: 0, + sector_cnt: 1, + paddr, + memory_region: Some(memory_region), + }) + } + + pub fn read(lba: u64, lba_count: u16) -> Result { + let (memory_region, paddr) = + syscall::memory_object_contiguous_physical(512 * (lba_count as u64))?; + + Ok(Self { + command: SataCommand::DmaReadExt, + lba, + sector_cnt: lba_count, + paddr, + memory_region: Some(memory_region), + }) + } + + pub fn read_manual(lba: u64, lba_count: u16, paddr: u64) -> Self { + Self { + command: SataCommand::DmaReadExt, + lba, + sector_cnt: lba_count, + paddr: paddr, + memory_region: None, + } + } + + pub fn release_mem_cap(&mut self) -> u64 { + self.memory_region.take().unwrap().release() + } +} + +impl From<&Command> for HostToDeviceRegisterFis { + fn from(val: &Command) -> Self { + HostToDeviceRegisterFis::new_command(val.command, val.lba, val.sector_cnt) + } +} + +struct CommandStructures { + command_list: &'static mut CommandList, + received_fis: &'static mut ReceivedFis, + command_tables: &'static mut [CommandTable; 32], +} + +pub struct PortController { + ahci_port_hba: Arc>, + command_structures: Mutex, + + command_slots: [Arc>; 32], +} + +impl PortController { + pub fn new(ahci_port_hba: &'static mut AhciPortHba) -> Self { + let (command_vaddr, command_paddr) = mem::map_physical_and_leak(0x2500); + ahci_port_hba.init(command_paddr, command_paddr + 0x400); + + let command_list = unsafe { (command_vaddr as *mut CommandList).as_mut().unwrap() }; + let received_fis = unsafe { + ((command_vaddr + 0x400) as *mut ReceivedFis) + .as_mut() + .unwrap() + }; + let command_tables = unsafe { + ((command_vaddr + 0x500) as *mut [CommandTable; 32]) + .as_mut() + .unwrap() + }; + + // This leaves space for 8 prdt entries. + for i in 0..32 { + command_list[i].command_table_base_addr = + (command_paddr + 0x500) + (0x100 * (i as u64)); + } + + let command_slots = array::from_fn(|_| Arc::new(Mutex::new(CommandStatus::Empty))); + + Self { + ahci_port_hba: Arc::new(Mutex::new(ahci_port_hba)), + command_structures: Mutex::new(CommandStructures { + command_list, + received_fis, + command_tables, + }), + command_slots, + } + } + + pub fn get_signature(&self) -> u32 { + self.ahci_port_hba.lock().signature.read() + } + + pub fn issue_command(&self, command: &Command) -> Result { + let slot = self.select_slot()?; + let command_slot = self.command_slots[slot].clone(); + + let ahci_port_hba_clone = self.ahci_port_hba.clone(); + let slot_clone = slot; + let future = CommandFuture::new( + command_slot, + Box::new(move || { + ahci_port_hba_clone.lock().issue_command(slot_clone); + }), + ); + + let mut command_structures = self.command_structures.lock(); + + command_structures.command_tables[slot] + .command_fis + .host_to_device = command.into(); + + command_structures.command_tables[slot].prdt[0].region_address = command.paddr; + command_structures.command_tables[slot].prdt[0].byte_count = + 512 * (command.sector_cnt as u32); + + command_structures.command_list[slot].prd_table_length = 1; + + command_structures.command_list[slot].command = + (size_of::() as u16 / 4) & 0x1F; + command_structures.command_list[slot].command |= 1 << 7; + + Ok(future) + } + + fn select_slot(&self) -> Result { + // TODO: We have a race condition here. + for i in 0..self.command_slots.len() { + let mut slot = self.command_slots[i].lock(); + if matches!(*slot, CommandStatus::Empty) { + *slot = CommandStatus::NotSent; + return Ok(i); + } + } + Err(ZError::EXHAUSTED) + } + + pub fn handle_interrupt(&self) { + let int_status = self.ahci_port_hba.lock().interrupt_status.read(); + if int_status.device_to_host_register_fis_interrupt() { + let received_fis = self + .command_structures + .lock() + .received_fis + .device_to_host_register_fis; + assert_eq!( + received_fis.fis_type as u8, + FisType::RegisterDeviceToHost as u8 + ); + if received_fis.error != 0 { + mammoth::debug!("D2H err: {:#0x}", received_fis.error); + mammoth::debug!("Status: {:#0x}", received_fis.status); + } + + self.ahci_port_hba.lock().interrupt_status.write( + AhciPortInterruptStatus::new().with_device_to_host_register_fis_interrupt(true), + ); + } + if int_status.pio_setup_fis_interrupt() { + self.ahci_port_hba + .lock() + .interrupt_status + .write(AhciPortInterruptStatus::new().with_pio_setup_fis_interrupt(true)); + } + + for i in 0..32 { + let int_offset = 1 << i; + + // If there is no longer a command issued on a slot and we have something in + // the command list that is pending we know that this is the command that finished. + if (self.ahci_port_hba.lock().command_issue.read() & int_offset) != int_offset { + let mut command_status = self.command_slots[i].lock(); + let mut did_complete = false; + if let CommandStatus::Pending(ref waker) = command_status.deref() { + waker.wake_by_ref(); + did_complete = true; + } + + if did_complete { + *command_status = CommandStatus::Complete; + } + } + } + } +} diff --git a/rust/sys/denali/src/denali_server.rs b/rust/sys/denali/src/denali_server.rs index 54bc7a0..511ff0f 100644 --- a/rust/sys/denali/src/denali_server.rs +++ b/rust/sys/denali/src/denali_server.rs @@ -47,7 +47,7 @@ impl AsyncDenaliServerHandler for DenaliServerImpl { // TODO: We should actually be able to read all of these in parallel. self.ahci_controller .issue_command(req.device_id as usize, &command) - .await; + .await?; paddr += block.size * sector_size; }