|
|
|
@ -1,9 +1,11 @@
|
|
|
|
|
use core::ops::DerefMut;
|
|
|
|
|
use core::ptr::addr_of_mut;
|
|
|
|
|
|
|
|
|
|
use alloc::boxed::Box;
|
|
|
|
|
use alloc::sync::Arc;
|
|
|
|
|
use mammoth::cap::Capability;
|
|
|
|
|
use mammoth::sync::Mutex;
|
|
|
|
|
use mammoth::thread;
|
|
|
|
|
use mammoth::{mem, thread};
|
|
|
|
|
|
|
|
|
|
use mammoth::{mem::MemoryRegion, zion::ZError};
|
|
|
|
|
|
|
|
|
@ -15,10 +17,7 @@ use crate::ahci::port::{
|
|
|
|
|
use super::command::{
|
|
|
|
|
CommandList, CommandTable, HostToDeviceRegisterFis, ReceivedFis, SataCommand,
|
|
|
|
|
};
|
|
|
|
|
use super::{
|
|
|
|
|
hba::AhciHba,
|
|
|
|
|
port::{AhciPortHba, AhciPortInterruptEnable, AhciSataError},
|
|
|
|
|
};
|
|
|
|
|
use super::{hba::AhciHba, port::AhciPortHba};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[repr(C, packed)]
|
|
|
|
@ -49,58 +48,56 @@ pub struct PciDeviceHeader {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct AhciController {
|
|
|
|
|
pci_memory: MemoryRegion,
|
|
|
|
|
hba_memory: MemoryRegion,
|
|
|
|
|
ports: [Option<PortController<'static>>; 32],
|
|
|
|
|
pci_header: &'static mut PciDeviceHeader,
|
|
|
|
|
hba: Mutex<&'static mut AhciHba>,
|
|
|
|
|
ports: [Option<PortController>; 32],
|
|
|
|
|
hba_vaddr: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AhciController {
|
|
|
|
|
pub fn new(pci_memory: MemoryRegion) -> Self {
|
|
|
|
|
let pci_device_header = unsafe {
|
|
|
|
|
pci_memory
|
|
|
|
|
.mut_slice::<u8>()
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<PciDeviceHeader>()
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
};
|
|
|
|
|
let hba_memory =
|
|
|
|
|
MemoryRegion::direct_physical(pci_device_header.abar as u64, 0x1100).unwrap();
|
|
|
|
|
pub fn new(pci_memory: Capability) -> Self {
|
|
|
|
|
let pci_vaddr = mem::map_cap_and_leak(pci_memory);
|
|
|
|
|
let pci_header = unsafe { (pci_vaddr as *mut PciDeviceHeader).as_mut().unwrap() };
|
|
|
|
|
|
|
|
|
|
let hba_vaddr = mem::map_direct_physical_and_leak(pci_header.abar as u64, 0x1100);
|
|
|
|
|
let hba = unsafe { (hba_vaddr as *mut AhciHba).as_mut().unwrap() };
|
|
|
|
|
let mut controller = Self {
|
|
|
|
|
pci_memory,
|
|
|
|
|
hba_memory,
|
|
|
|
|
pci_header,
|
|
|
|
|
hba: Mutex::new(hba),
|
|
|
|
|
ports: [const { None }; 32],
|
|
|
|
|
hba_vaddr,
|
|
|
|
|
};
|
|
|
|
|
controller.init();
|
|
|
|
|
controller
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn init(&mut self) {
|
|
|
|
|
self.ahci_hba().init();
|
|
|
|
|
self.hba.lock().init();
|
|
|
|
|
|
|
|
|
|
self.init_ports().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn irq_num(&self) -> u64 {
|
|
|
|
|
match self.pci_header().interrupt_pin {
|
|
|
|
|
match self.pci_header.interrupt_pin {
|
|
|
|
|
1 => mammoth::zion::kZIrqPci1,
|
|
|
|
|
2 => mammoth::zion::kZIrqPci2,
|
|
|
|
|
3 => mammoth::zion::kZIrqPci3,
|
|
|
|
|
4 => mammoth::zion::kZIrqPci4,
|
|
|
|
|
_ => panic!(
|
|
|
|
|
"Unrecognized pci interrupt pin {}",
|
|
|
|
|
self.pci_header().interrupt_pin
|
|
|
|
|
self.pci_header.interrupt_pin
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_irq(&mut self) {
|
|
|
|
|
for i in 0..self.ahci_hba().capabilities.read().num_ports() {
|
|
|
|
|
fn handle_irq(&self) {
|
|
|
|
|
let mut hba = self.hba.lock();
|
|
|
|
|
for i in 0..hba.capabilities.read().num_ports() {
|
|
|
|
|
let int_offset = 1 << i;
|
|
|
|
|
if (self.ahci_hba().interrupt_status.read() & int_offset) == int_offset {
|
|
|
|
|
if let Some(port) = &mut self.ports[i as usize] {
|
|
|
|
|
if (hba.interrupt_status.read() & int_offset) == int_offset {
|
|
|
|
|
if let Some(port) = &self.ports[i as usize] {
|
|
|
|
|
port.handle_interrupt();
|
|
|
|
|
self.ahci_hba().interrupt_status.update(|is| {
|
|
|
|
|
hba.interrupt_status.update(|is| {
|
|
|
|
|
*is &= !int_offset;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@ -109,20 +106,17 @@ impl AhciController {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn init_ports(&mut self) -> Result<(), ZError> {
|
|
|
|
|
for i in 0..(self.ahci_hba().capabilities.read().num_ports() as usize) {
|
|
|
|
|
let hba = self.hba.lock();
|
|
|
|
|
for i in 0..(hba.capabilities.read().num_ports() as usize) {
|
|
|
|
|
let port_index = 1 << i;
|
|
|
|
|
if (self.ahci_hba().port_implemented.read() & port_index) != port_index {
|
|
|
|
|
if (hba.port_implemented.read() & port_index) != port_index {
|
|
|
|
|
mammoth::debug!("Skipping port {}, not implemented", i);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let port_offset: usize = 0x100 + (0x80 * i);
|
|
|
|
|
let port_size = size_of::<AhciPortHba>();
|
|
|
|
|
let port_limit = port_offset + port_size;
|
|
|
|
|
let port = unsafe {
|
|
|
|
|
self.hba_memory.mut_slice::<u8>()[port_offset..port_limit]
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<AhciPortHba>()
|
|
|
|
|
((self.hba_vaddr as usize + port_offset) as *mut AhciPortHba)
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
};
|
|
|
|
@ -140,50 +134,24 @@ impl AhciController {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.ports[i] = Some(PortController::new(port)?);
|
|
|
|
|
self.ports[i] = Some(PortController::new(port));
|
|
|
|
|
self.ports[i].as_mut().unwrap().identify()?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn pci_header(&self) -> &mut PciDeviceHeader {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.pci_memory
|
|
|
|
|
.mut_slice::<u8>()
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<PciDeviceHeader>()
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn ahci_hba(&self) -> &mut AhciHba {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.hba_memory
|
|
|
|
|
.mut_slice::<u8>()
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<AhciHba>()
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn spawn_irq_thread(controller: Arc<Mutex<AhciController>>) -> thread::JoinHandle {
|
|
|
|
|
pub fn spawn_irq_thread(controller: Arc<AhciController>) -> thread::JoinHandle {
|
|
|
|
|
let irq_thread = move || {
|
|
|
|
|
let irq_num = controller.lock().irq_num();
|
|
|
|
|
let irq_num = controller.irq_num();
|
|
|
|
|
let irq_port =
|
|
|
|
|
mammoth::port::PortServer::from_cap(mammoth::syscall::register_irq(irq_num).unwrap());
|
|
|
|
|
controller
|
|
|
|
|
.lock()
|
|
|
|
|
.ahci_hba()
|
|
|
|
|
.global_host_control
|
|
|
|
|
.update(|ghc| {
|
|
|
|
|
ghc.set_interrupt_enable(true);
|
|
|
|
|
});
|
|
|
|
|
controller.hba.lock().global_host_control.update(|ghc| {
|
|
|
|
|
ghc.set_interrupt_enable(true);
|
|
|
|
|
});
|
|
|
|
|
loop {
|
|
|
|
|
irq_port.recv_null().unwrap();
|
|
|
|
|
controller.lock().handle_irq();
|
|
|
|
|
controller.handle_irq();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
thread::spawn(irq_thread)
|
|
|
|
@ -226,40 +194,56 @@ impl From<&Command> for HostToDeviceRegisterFis {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct PortController<'a> {
|
|
|
|
|
ahci_port_hba: &'a mut AhciPortHba,
|
|
|
|
|
command_slots: [Option<Arc<Command>>; 32],
|
|
|
|
|
command_structures: MemoryRegion,
|
|
|
|
|
struct PortController {
|
|
|
|
|
ahci_port_hba: Mutex<&'static mut AhciPortHba>,
|
|
|
|
|
command_list: &'static mut CommandList,
|
|
|
|
|
received_fis: &'static mut ReceivedFis,
|
|
|
|
|
command_tables: &'static mut [CommandTable; 32],
|
|
|
|
|
|
|
|
|
|
command_slots: Mutex<[Option<Arc<Command>>; 32]>,
|
|
|
|
|
|
|
|
|
|
// FIXME: These should probably be something like a OnceCell (or OnceLock).
|
|
|
|
|
sector_size: Arc<Mutex<Option<u64>>>,
|
|
|
|
|
sector_cnt: Arc<Mutex<Option<u64>>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> PortController<'a> {
|
|
|
|
|
fn new(ahci_port_hba: &'a mut AhciPortHba) -> Result<Self, ZError> {
|
|
|
|
|
let (command_structures, command_paddr) = MemoryRegion::contiguous_physical(0x2500)?;
|
|
|
|
|
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 mut controller = Self {
|
|
|
|
|
ahci_port_hba,
|
|
|
|
|
command_slots: [const { None }; 32],
|
|
|
|
|
command_structures,
|
|
|
|
|
sector_size: Arc::new(Mutex::new(None)),
|
|
|
|
|
sector_cnt: Arc::new(Mutex::new(None)),
|
|
|
|
|
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 {
|
|
|
|
|
controller.command_list()[i].command_table_base_addr =
|
|
|
|
|
command_list[i].command_table_base_addr =
|
|
|
|
|
(command_paddr + 0x500) + (0x100 * (i as u64));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(controller)
|
|
|
|
|
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)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn identify(&mut self) -> Result<(), ZError> {
|
|
|
|
|
if self.ahci_port_hba.signature.read() == 0x101 {
|
|
|
|
|
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| {
|
|
|
|
@ -286,11 +270,10 @@ impl<'a> PortController<'a> {
|
|
|
|
|
.lock()
|
|
|
|
|
.deref_mut()
|
|
|
|
|
.insert(new_sector_size as u64);
|
|
|
|
|
let _ = sector_cnt.lock().deref_mut().insert(lba_count as u64);
|
|
|
|
|
let _ = sector_cnt.lock().deref_mut().insert(lba_count);
|
|
|
|
|
};
|
|
|
|
|
self.issue_command(Arc::from(Command::identify(Box::new(callback))?))?;
|
|
|
|
|
} else {
|
|
|
|
|
let sig = self.ahci_port_hba.signature.read();
|
|
|
|
|
mammoth::debug!("Skipping non-sata sig: {:#0x}", sig);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
@ -298,88 +281,57 @@ impl<'a> PortController<'a> {
|
|
|
|
|
|
|
|
|
|
fn issue_command(&mut self, command: Arc<Command>) -> Result<(), ZError> {
|
|
|
|
|
let slot = self.select_slot()?;
|
|
|
|
|
self.command_slots[slot] = Some(command.clone());
|
|
|
|
|
self.command_slots.lock()[slot] = Some(command.clone());
|
|
|
|
|
|
|
|
|
|
self.command_tables()[slot].command_fis.host_to_device = command.clone().as_ref().into();
|
|
|
|
|
self.command_tables[slot].command_fis.host_to_device = command.clone().as_ref().into();
|
|
|
|
|
|
|
|
|
|
self.command_tables()[slot].prdt[0].region_address = command.paddr;
|
|
|
|
|
self.command_tables()[slot].prdt[0].byte_count = 512 * (command.sector_cnt as u32);
|
|
|
|
|
self.command_tables[slot].prdt[0].region_address = command.paddr;
|
|
|
|
|
self.command_tables[slot].prdt[0].byte_count = 512 * (command.sector_cnt as u32);
|
|
|
|
|
|
|
|
|
|
self.command_list()[slot].prd_table_length = 1;
|
|
|
|
|
self.command_list[slot].prd_table_length = 1;
|
|
|
|
|
|
|
|
|
|
self.command_list()[slot].command =
|
|
|
|
|
(size_of::<HostToDeviceRegisterFis>() as u16 / 4) & 0x1F;
|
|
|
|
|
self.command_list()[slot].command |= 1 << 7;
|
|
|
|
|
self.ahci_port_hba.issue_command(slot);
|
|
|
|
|
self.command_list[slot].command = (size_of::<HostToDeviceRegisterFis>() as u16 / 4) & 0x1F;
|
|
|
|
|
self.command_list[slot].command |= 1 << 7;
|
|
|
|
|
self.ahci_port_hba.lock().issue_command(slot);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn select_slot(&self) -> Result<usize, ZError> {
|
|
|
|
|
for i in 0..self.command_slots.len() {
|
|
|
|
|
match self.command_slots[i] {
|
|
|
|
|
None => return Ok(i),
|
|
|
|
|
_ => {}
|
|
|
|
|
let slots = self.command_slots.lock();
|
|
|
|
|
for i in 0..slots.len() {
|
|
|
|
|
if slots[i].is_none() {
|
|
|
|
|
return Ok(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Err(ZError::EXHAUSTED);
|
|
|
|
|
Err(ZError::EXHAUSTED)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn command_list(&mut self) -> &mut CommandList {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.command_structures
|
|
|
|
|
.mut_slice::<u8>()
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<CommandList>()
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn recieved_fis(&mut self) -> &mut ReceivedFis {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.command_structures.mut_slice::<u8>()[0x400..]
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<ReceivedFis>()
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn command_tables(&mut self) -> &mut [CommandTable; 32] {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.command_structures.mut_slice::<u8>()[0x500..]
|
|
|
|
|
.as_mut_ptr()
|
|
|
|
|
.cast::<[CommandTable; 32]>()
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_interrupt(&mut self) {
|
|
|
|
|
let int_status = self.ahci_port_hba.interrupt_status.read();
|
|
|
|
|
fn handle_interrupt(&self) {
|
|
|
|
|
let int_status = self.ahci_port_hba.lock().interrupt_status.read();
|
|
|
|
|
if int_status.device_to_host_register_fis_interrupt() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
self.recieved_fis().device_to_host_register_fis.fis_type as u8,
|
|
|
|
|
self.received_fis.device_to_host_register_fis.fis_type as u8,
|
|
|
|
|
FisType::RegisterDeviceToHost as u8
|
|
|
|
|
);
|
|
|
|
|
if self.recieved_fis().device_to_host_register_fis.error != 0 {
|
|
|
|
|
if self.received_fis.device_to_host_register_fis.error != 0 {
|
|
|
|
|
mammoth::debug!(
|
|
|
|
|
"D2H err: {:#0x}",
|
|
|
|
|
self.recieved_fis().device_to_host_register_fis.error
|
|
|
|
|
self.received_fis.device_to_host_register_fis.error
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
mammoth::debug!(
|
|
|
|
|
"Status: {:#0x}",
|
|
|
|
|
self.recieved_fis().device_to_host_register_fis.status
|
|
|
|
|
self.received_fis.device_to_host_register_fis.status
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.ahci_port_hba.interrupt_status.write(
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
@ -392,16 +344,16 @@ impl<'a> PortController<'a> {
|
|
|
|
|
// 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.command_issue.read() & int_offset) != int_offset {
|
|
|
|
|
if let Some(_) = &self.command_slots[i] {
|
|
|
|
|
self.finish_command(i);
|
|
|
|
|
self.command_slots[i] = None;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn finish_command(&self, slot: usize) {
|
|
|
|
|
self.command_slots[slot].as_ref().unwrap().callback()
|
|
|
|
|
self.command_slots.lock()[slot].as_ref().unwrap().callback()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|