Move port controller to separate file.
This commit is contained in:
parent
440de700f9
commit
15a5e210ef
|
@ -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<AhciController>) -> thread::JoinHandle {
|
|||
pub async fn identify_ports(controller: Arc<AhciController>) {
|
||||
controller.identify_ports().await.unwrap();
|
||||
}
|
||||
|
||||
enum CommandStatus {
|
||||
Empty,
|
||||
NotSent,
|
||||
Pending(Waker),
|
||||
Complete,
|
||||
}
|
||||
|
||||
struct CommandFuture {
|
||||
status: Arc<Mutex<CommandStatus>>,
|
||||
trigger: Box<dyn Fn() + Sync + Send>,
|
||||
}
|
||||
|
||||
impl CommandFuture {
|
||||
fn new(status: Arc<Mutex<CommandStatus>>, trigger: Box<dyn Fn() + Sync + Send>) -> 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<Self::Output> {
|
||||
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<Capability>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn identify() -> Result<Self, ZError> {
|
||||
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<Self, ZError> {
|
||||
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<Mutex<&'static mut AhciPortHba>>,
|
||||
command_structures: Mutex<CommandStructures>,
|
||||
|
||||
command_slots: [Arc<Mutex<CommandStatus>>; 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<CommandFuture, ZError> {
|
||||
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::<HostToDeviceRegisterFis>() as u16 / 4) & 0x1F;
|
||||
command_structures.command_list[slot].command |= 1 << 7;
|
||||
|
||||
Ok(future)
|
||||
}
|
||||
|
||||
fn select_slot(&self) -> Result<usize, ZError> {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Mutex<CommandStatus>>,
|
||||
trigger: Box<dyn Fn() + Sync + Send>,
|
||||
}
|
||||
|
||||
impl CommandFuture {
|
||||
fn new(status: Arc<Mutex<CommandStatus>>, trigger: Box<dyn Fn() + Sync + Send>) -> 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<Self::Output> {
|
||||
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<Capability>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn identify() -> Result<Self, ZError> {
|
||||
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<Self, ZError> {
|
||||
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<Mutex<&'static mut AhciPortHba>>,
|
||||
command_structures: Mutex<CommandStructures>,
|
||||
|
||||
command_slots: [Arc<Mutex<CommandStatus>>; 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<CommandFuture, ZError> {
|
||||
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::<HostToDeviceRegisterFis>() as u16 / 4) & 0x1F;
|
||||
command_structures.command_list[slot].command |= 1 << 7;
|
||||
|
||||
Ok(future)
|
||||
}
|
||||
|
||||
fn select_slot(&self) -> Result<usize, ZError> {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue