204 lines
6.4 KiB
Rust
204 lines
6.4 KiB
Rust
use alloc::sync::Arc;
|
|
use mammoth::{
|
|
cap::Capability,
|
|
mem::{self, MemoryRegion},
|
|
sync::Mutex,
|
|
thread,
|
|
zion::ZError,
|
|
};
|
|
|
|
use crate::ahci::{
|
|
port::{AhciDeviceDetection, AhciInterfacePowerManagement},
|
|
Command,
|
|
};
|
|
|
|
use super::{hba::AhciHba, port::AhciPortHba, port_controller::PortController};
|
|
|
|
#[derive(Debug)]
|
|
#[repr(C, packed)]
|
|
pub struct PciDeviceHeader {
|
|
pub vendor_id: u16,
|
|
pub device_id: u16,
|
|
pub command_reg: u16,
|
|
pub status_reg: u16,
|
|
pub revision: u8,
|
|
pub prog_interface: u8,
|
|
pub subclass: u8,
|
|
pub class_code: u8,
|
|
pub cache_line_size: u8,
|
|
pub latency_timer: u8,
|
|
pub header_type: u8,
|
|
pub bist: u8,
|
|
pub bars: [u32; 5],
|
|
pub abar: u32,
|
|
__: u32,
|
|
pub subsystem_id: u32,
|
|
pub expansion_rom: u32,
|
|
pub cap_ptr: u8,
|
|
___: [u8; 7],
|
|
pub interrupt_line: u8,
|
|
pub interrupt_pin: u8,
|
|
pub min_grant: u8,
|
|
pub max_latency: u8,
|
|
}
|
|
|
|
pub struct AhciController {
|
|
pci_header: &'static mut PciDeviceHeader,
|
|
hba: Mutex<&'static mut AhciHba>,
|
|
ports: [Option<PortController>; 32],
|
|
hba_vaddr: u64,
|
|
}
|
|
|
|
impl AhciController {
|
|
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_header,
|
|
hba: Mutex::new(hba),
|
|
ports: [const { None }; 32],
|
|
hba_vaddr,
|
|
};
|
|
controller.init();
|
|
controller
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 {
|
|
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
|
|
),
|
|
}
|
|
}
|
|
|
|
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 (hba.interrupt_status.read() & int_offset) == int_offset {
|
|
if let Some(port) = &self.ports[i as usize] {
|
|
port.handle_interrupt();
|
|
hba.interrupt_status.update(|is| {
|
|
*is &= !int_offset;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn identify_ports(&self) -> Result<(), ZError> {
|
|
for port in self.ports.iter().flatten() {
|
|
let sig = port.get_signature();
|
|
if sig == 0x101 {
|
|
let mut command = Command::identify()?;
|
|
mammoth::debug!("IDENT!");
|
|
port.issue_command(&command)?.await;
|
|
let memory_region =
|
|
MemoryRegion::from_cap(Capability::take(command.release_mem_cap()))?;
|
|
let ident = memory_region.slice::<u16>();
|
|
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);
|
|
|
|
// We hardcode assumptions about sector size in a few places, better to panic if we
|
|
// are wrong.
|
|
assert_eq!(
|
|
new_sector_size, 512,
|
|
"Sector size {} differs from 512.",
|
|
new_sector_size
|
|
);
|
|
} else {
|
|
mammoth::debug!("Skipping non-sata sig: {:#0x}", sig);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn issue_command(&self, port_num: usize, command: &Command) -> Result<(), ZError> {
|
|
assert!(port_num < 32);
|
|
|
|
self.ports[port_num]
|
|
.as_ref()
|
|
.ok_or(ZError::INVALID_ARGUMENT)?
|
|
.issue_command(command)?
|
|
.await;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn spawn_irq_thread(controller: Arc<AhciController>) -> thread::JoinHandle {
|
|
let irq_thread = move || {
|
|
let irq_num = controller.irq_num();
|
|
let irq_port =
|
|
mammoth::port::PortServer::from_cap(mammoth::syscall::register_irq(irq_num).unwrap());
|
|
controller.hba.lock().global_host_control.update(|ghc| {
|
|
ghc.set_interrupt_enable(true);
|
|
});
|
|
loop {
|
|
irq_port.recv_null().unwrap();
|
|
controller.handle_irq();
|
|
}
|
|
};
|
|
thread::spawn(irq_thread)
|
|
}
|
|
|
|
pub async fn identify_ports(controller: Arc<AhciController>) {
|
|
controller.identify_ports().await.unwrap();
|
|
}
|