diff --git a/rust/sys/denali/src/ahci/controller.rs b/rust/sys/denali/src/ahci/controller.rs index 6869c63..5c6849a 100644 --- a/rust/sys/denali/src/ahci/controller.rs +++ b/rust/sys/denali/src/ahci/controller.rs @@ -1,7 +1,10 @@ +use core::array; +use core::future::Future; +use core::ops::Deref; use core::ops::DerefMut; -use core::ptr::addr_of_mut; +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; @@ -73,8 +76,36 @@ impl AhciController { fn init(&mut self) { self.hba.lock().init(); + let hba = self.hba.lock(); + for i in 0..(hba.capabilities.read().num_ports() as usize) { + let port_index = 1 << i; + if (hba.port_implemented.read() & port_index) != port_index { + mammoth::debug!("Skipping port {}, not implemented", i); + continue; + } - self.init_ports().unwrap(); + let port_offset: usize = 0x100 + (0x80 * i); + let port = unsafe { + ((self.hba_vaddr as usize + port_offset) as *mut AhciPortHba) + .as_mut() + .unwrap() + }; + + let sata_status = port.sata_status.read(); + if (sata_status.device_detection() != AhciDeviceDetection::CommunicationEstablished) + || (sata_status.interface_power_management() + != AhciInterfacePowerManagement::Active) + { + mammoth::debug!( + "Skipping port {}, no communcation. Status: {:?}", + i, + sata_status + ); + continue; + } + + self.ports[i] = Some(PortController::new(port)); + } } fn irq_num(&self) -> u64 { @@ -105,37 +136,36 @@ impl AhciController { } } - fn init_ports(&mut self) -> Result<(), ZError> { - let hba = self.hba.lock(); - for i in 0..(hba.capabilities.read().num_ports() as usize) { - let port_index = 1 << i; - if (hba.port_implemented.read() & port_index) != port_index { - mammoth::debug!("Skipping port {}, not implemented", i); - continue; + pub async fn identify_ports(&self) -> Result<(), ZError> { + for port in self.ports.iter().flatten() { + let sig = port.get_signature(); + if sig == 0x101 { + let command = Command::identify()?; + mammoth::debug!("IDENT!"); + port.issue_command(&command)?.await; + let ident = command.memory_region.slice::(); + let new_sector_size = if ident[106] & (1 << 12) != 0 { + ident[117] as u32 | ((ident[118] as u32) << 16) + } else { + 512 + }; + + let lba_count = if ident[83] & (1 << 10) != 0 { + ident[100] as u64 + | (ident[101] as u64) << 16 + | (ident[102] as u64) << 32 + | (ident[103] as u64) << 48 + } else { + ident[60] as u64 | (ident[61] as u64) << 16 + }; + mammoth::debug!("Sector size: {:#0x}", new_sector_size); + mammoth::debug!("LBA Count: {:#0x}", lba_count); + + //self.sector_size = Some(new_sector_size as u64); + //self.sector_cnt = Some(lba_count); + } else { + mammoth::debug!("Skipping non-sata sig: {:#0x}", sig); } - - let port_offset: usize = 0x100 + (0x80 * i); - let port = unsafe { - ((self.hba_vaddr as usize + port_offset) as *mut AhciPortHba) - .as_mut() - .unwrap() - }; - - let sata_status = port.sata_status.read(); - if (sata_status.device_detection() != AhciDeviceDetection::CommunicationEstablished) - || (sata_status.interface_power_management() - != AhciInterfacePowerManagement::Active) - { - mammoth::debug!( - "Skipping port {}, no communcation. Status: {:?}", - i, - sata_status - ); - continue; - } - - self.ports[i] = Some(PortController::new(port)); - self.ports[i].as_mut().unwrap().identify()?; } Ok(()) } @@ -151,12 +181,57 @@ pub fn spawn_irq_thread(controller: Arc) -> thread::JoinHandle { }); loop { irq_port.recv_null().unwrap(); + mammoth::debug!("Interrupt!"); controller.handle_irq(); } }; thread::spawn(irq_thread) } +pub async fn identify_ports(controller: Arc) { + controller.identify_ports().await.unwrap(); +} + +enum CommandStatus { + Empty, + NotSent, + Pending(Waker), + Complete, +} + +struct CommandFuture { + status: Arc>, +} + +impl CommandFuture { + fn new(status: Arc>) -> Self { + Self { status } + } +} + +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 mut status = self.deref_mut().status.lock(); + match status.deref() { + CommandStatus::NotSent => { + *status = CommandStatus::Pending(cx.waker().clone()); + Poll::Pending + } + CommandStatus::Pending(_) => Poll::Pending, + CommandStatus::Complete => Poll::Ready(()), + CommandStatus::Empty => panic!("Polling empty command slot"), + } + } +} + struct Command { command: SataCommand, lba: u64, @@ -165,12 +240,10 @@ struct Command { #[allow(dead_code)] // We need to own this even if we never access it. memory_region: MemoryRegion, - - callback_internal: Box, } impl Command { - pub fn identify(callback: Box) -> Result { + pub fn identify() -> Result { let (memory_region, paddr) = MemoryRegion::contiguous_physical(512)?; Ok(Self { @@ -179,13 +252,8 @@ impl Command { sector_cnt: 1, paddr, memory_region, - callback_internal: callback, }) } - - pub fn callback(&self) { - (self.callback_internal)(self); - } } impl From<&Command> for HostToDeviceRegisterFis { @@ -194,17 +262,17 @@ impl From<&Command> for HostToDeviceRegisterFis { } } -struct PortController { - ahci_port_hba: Mutex<&'static mut AhciPortHba>, +struct CommandStructures { command_list: &'static mut CommandList, received_fis: &'static mut ReceivedFis, command_tables: &'static mut [CommandTable; 32], +} - command_slots: Mutex<[Option>; 32]>, +struct PortController { + ahci_port_hba: Mutex<&'static mut AhciPortHba>, + command_structures: Mutex, - // FIXME: These should probably be something like a OnceCell (or OnceLock). - sector_size: Arc>>, - sector_cnt: Arc>>, + command_slots: [Arc>; 32], } impl PortController { @@ -230,76 +298,57 @@ impl PortController { (command_paddr + 0x500) + (0x100 * (i as u64)); } + let command_slots = array::from_fn(|_| Arc::new(Mutex::new(CommandStatus::Empty))); + Self { ahci_port_hba: Mutex::new(ahci_port_hba), - command_list, - received_fis, - command_tables, - command_slots: Mutex::new([const { None }; 32]), - sector_size: Arc::new(Mutex::new(None)), - sector_cnt: Arc::new(Mutex::new(None)), + command_structures: Mutex::new(CommandStructures { + command_list, + received_fis, + command_tables, + }), + command_slots, } } - pub fn identify(&mut self) -> Result<(), ZError> { - let sig = self.ahci_port_hba.lock().signature.read(); - if sig == 0x101 { - let sector_size = self.sector_size.clone(); - let sector_cnt = self.sector_cnt.clone(); - let callback = move |c: &Command| { - mammoth::debug!("TESTING"); - let ident = c.memory_region.slice::(); - let new_sector_size = if ident[106] & (1 << 12) != 0 { - ident[117] as u32 | ((ident[118] as u32) << 16) - } else { - 512 - }; - - let lba_count = if ident[83] & (1 << 10) != 0 { - ident[100] as u64 - | (ident[101] as u64) << 16 - | (ident[102] as u64) << 32 - | (ident[103] as u64) << 48 - } else { - ident[60] as u64 | (ident[61] as u64) << 16 - }; - mammoth::debug!("Sector size: {:#0x}", new_sector_size); - mammoth::debug!("LBA Count: {:#0x}", lba_count); - - let _ = sector_size - .lock() - .deref_mut() - .insert(new_sector_size as u64); - let _ = sector_cnt.lock().deref_mut().insert(lba_count); - }; - self.issue_command(Arc::from(Command::identify(Box::new(callback))?))?; - } else { - mammoth::debug!("Skipping non-sata sig: {:#0x}", sig); - } - Ok(()) + fn get_signature(&self) -> u32 { + self.ahci_port_hba.lock().signature.read() } - fn issue_command(&mut self, command: Arc) -> Result<(), ZError> { + fn issue_command(&self, command: &Command) -> Result { let slot = self.select_slot()?; - self.command_slots.lock()[slot] = Some(command.clone()); + let command_slot = self.command_slots[slot].clone(); + let future = CommandFuture::new(command_slot); - self.command_tables[slot].command_fis.host_to_device = command.clone().as_ref().into(); + let mut command_structures = self.command_structures.lock(); - self.command_tables[slot].prdt[0].region_address = command.paddr; - self.command_tables[slot].prdt[0].byte_count = 512 * (command.sector_cnt as u32); + command_structures.command_tables[slot] + .command_fis + .host_to_device = command.into(); - self.command_list[slot].prd_table_length = 1; + 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); - self.command_list[slot].command = (size_of::() as u16 / 4) & 0x1F; - self.command_list[slot].command |= 1 << 7; + 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; + + // FIXME: This is technically a poor future implementation since it starts work before it + // is polled. self.ahci_port_hba.lock().issue_command(slot); - Ok(()) + + Ok(future) } fn select_slot(&self) -> Result { - let slots = self.command_slots.lock(); - for i in 0..slots.len() { - if slots[i].is_none() { + // 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); } } @@ -309,20 +358,18 @@ impl PortController { 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!( - self.received_fis.device_to_host_register_fis.fis_type as u8, + received_fis.fis_type as u8, FisType::RegisterDeviceToHost as u8 ); - if self.received_fis.device_to_host_register_fis.error != 0 { - mammoth::debug!( - "D2H err: {:#0x}", - self.received_fis.device_to_host_register_fis.error - ); - - mammoth::debug!( - "Status: {:#0x}", - self.received_fis.device_to_host_register_fis.status - ); + 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( @@ -340,20 +387,25 @@ impl PortController { let int_offset = 1 << i; // If there is no longer a command issued on a slot and we have something in - // the command list we know that this is the command that finished. - // FIXME: This could cause a race condition when issuing a command if a different - // interrupt triggers between us setting the command in the command slot and - // actually issuing the command. - if (self.ahci_port_hba.lock().command_issue.read() & int_offset) != int_offset - && self.command_slots.lock()[i].is_some() - { - self.finish_command(i); - self.command_slots.lock()[i] = None; + // 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; + match command_status.deref() { + CommandStatus::NotSent => { + did_complete = true; + } + CommandStatus::Pending(ref waker) => { + waker.wake_by_ref(); + did_complete = true; + } + _ => {} + } + + if did_complete { + *command_status = CommandStatus::Complete; + } } } } - - fn finish_command(&self, slot: usize) { - self.command_slots.lock()[slot].as_ref().unwrap().callback() - } } diff --git a/rust/sys/denali/src/ahci/mod.rs b/rust/sys/denali/src/ahci/mod.rs index 2fec39b..e91e29c 100644 --- a/rust/sys/denali/src/ahci/mod.rs +++ b/rust/sys/denali/src/ahci/mod.rs @@ -3,5 +3,6 @@ mod controller; mod hba; mod port; +pub use controller::identify_ports; pub use controller::spawn_irq_thread; pub use controller::AhciController; diff --git a/rust/sys/denali/src/bin/denali.rs b/rust/sys/denali/src/bin/denali.rs index 5c2370e..5adafc0 100644 --- a/rust/sys/denali/src/bin/denali.rs +++ b/rust/sys/denali/src/bin/denali.rs @@ -4,9 +4,14 @@ extern crate alloc; use alloc::sync::Arc; -use mammoth::{define_entry, sync::Mutex, zion::z_err_t}; +use mammoth::{ + define_entry, + sync::Mutex, + task::{Executor, Task}, + zion::z_err_t, +}; -use denali::ahci::{spawn_irq_thread, AhciController}; +use denali::ahci::{identify_ports, spawn_irq_thread, AhciController}; define_entry!(); @@ -24,8 +29,14 @@ extern "C" fn main() -> z_err_t { ahci_info.ahci_region, ))); + let mut executor = Executor::new(); + + executor.spawn(Task::new(identify_ports(ahci_controller.clone()))); + let thread = spawn_irq_thread(ahci_controller.clone()); + executor.run(); + thread.join().expect("Failed to wait on irq thread."); 0 }