diff --git a/kernel/src/drivers/block/ahci.rs b/kernel/src/drivers/block/ahci.rs index 69a8f44..2d445f1 100644 --- a/kernel/src/drivers/block/ahci.rs +++ b/kernel/src/drivers/block/ahci.rs @@ -7,18 +7,18 @@ use alloc::boxed::Box; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; -use core::cmp::min; use core::mem::size_of; use core::slice; +use core::sync::atomic::spin_loop_hint; -use crate::sync::FlagsGuard; +use bit_field::*; use bitflags::*; use log::*; -use rcore_memory::paging::PageTable; -use rcore_memory::{PhysAddr, VirtAddr, PAGE_SIZE}; +use rcore_fs::dev::BlockDevice; use volatile::Volatile; -use rcore_fs::dev::BlockDevice; +use rcore_memory::paging::PageTable; +use rcore_memory::{PhysAddr, VirtAddr, PAGE_SIZE}; use crate::drivers::BlockDriver; use crate::memory::active_table; @@ -29,7 +29,7 @@ use super::super::{DeviceType, Driver, BLK_DRIVERS, DRIVERS}; pub struct AHCI { header: usize, size: usize, - rfis: usize, + received_fis: &'static mut AHCIReceivedFIS, cmd_list: &'static mut [AHCICommandHeader], cmd_table: &'static mut AHCICommandTable, data: &'static mut [u8], @@ -38,21 +38,31 @@ pub struct AHCI { pub struct AHCIDriver(Mutex); -// AHCI -// 3.1 Generic Host Control +/// AHCI Generic Host Control (3.1) #[repr(C)] pub struct AHCIGHC { - cap: Volatile, - ghc: Volatile, - is: Volatile, - pi: Volatile, - vs: Volatile, - ccc_ctl: Volatile, + /// Host capability + capability: Volatile, + /// Global host control + global_host_control: Volatile, + /// Interrupt status + interrupt_status: Volatile, + /// Port implemented + port_implemented: Volatile, + /// Version + version: Volatile, + /// Command completion coalescing control + ccc_control: Volatile, + /// Command completion coalescing ports ccc_ports: Volatile, - em_loc: Volatile, - em_ctl: Volatile, - cap2: Volatile, - bohc: Volatile, + /// Enclosure management location + em_location: Volatile, + /// Enclosure management control + em_control: Volatile, + /// Host capabilities extended + capabilities2: Volatile, + /// BIOS/OS handoff control and status + bios_os_handoff_control: Volatile, } bitflags! { @@ -78,42 +88,63 @@ bitflags! { const EMS = 1 << 6; const SXS = 1 << 5; // number of ports = 1 - const NP_1 = 1 << 0; - const NP_2 = 1 << 1; - const NP_4 = 1 << 2; - const NP_8 = 1 << 3; - const NP_16 = 1 << 4; + const NUM_MASK = 0b11111; } } -// AHCI -// 3.3 Port Registers (one set per port) +impl AHCIGHC { + fn enable(&mut self) { + self.global_host_control.update(|v| { + v.set_bit(13, true); + }); + } + fn num_ports(&self) -> usize { + (self.capability.read() & AHCICap::NUM_MASK).bits() as usize + 1 + } + fn has_port(&self, port_num: usize) -> bool { + self.port_implemented.read().get_bit(port_num) + } +} + +/// AHCI Port Registers (3.3) (one set per port) #[repr(C)] pub struct AHCIPort { - clb: Volatile, - clbu: Volatile, - fb: Volatile, - fbu: Volatile, - is: Volatile, - ie: Volatile, - cmd: Volatile, + command_list_base_address: Volatile, + fis_base_address: Volatile, + interrupt_status: Volatile, + interrupt_enable: Volatile, + command: Volatile, reserved: Volatile, - tfd: Volatile, - sig: Volatile, - ssts: Volatile, - sctl: Volatile, - serr: Volatile, - sact: Volatile, - ci: Volatile, - sntf: Volatile, - fbs: Volatile, - devslp: Volatile, + task_file_data: Volatile, + signature: Volatile, + sata_status: Volatile, + sata_control: Volatile, + sata_error: Volatile, + sata_active: Volatile, + command_issue: Volatile, + sata_notification: Volatile, + fis_based_switch_control: Volatile, +} + +impl AHCIPort { + fn spin_on_slot(&mut self, slot: usize) { + loop { + let ci = self.command_issue.read(); + if !ci.get_bit(slot) { + break; + } + spin_loop_hint(); + } + } + fn issue_command(&mut self, slot: usize) { + assert!(slot < 32); + self.command_issue.write(1 << (slot as u32)); + } } -// AHCi -// 4.2.1 Received FIS Structure +/// AHCI Received FIS Structure (4.2.1) #[repr(C)] -pub struct AHCIRFIS { +pub struct AHCIReceivedFIS { dma: [u8; 0x20], pio: [u8; 0x20], d2h: [u8; 0x18], @@ -122,35 +153,74 @@ pub struct AHCIRFIS { reserved: [u8; 0x60], } -// AHCI -// 4.2.2 Command List Structure +/// # AHCI Command List Structure (4.2.2) +/// +/// Host sends commands to the device through Command List. +/// +/// Command List consists of 1 to 32 command headers, each one is called a slot. +/// +/// Each command header describes an ATA or ATAPI command, including a +/// Command FIS, an ATAPI command buffer and a bunch of Physical Region +/// Descriptor Tables specifying the data payload address and size. +/// +/// https://wiki.osdev.org/images/e/e8/Command_list.jpg #[repr(C)] pub struct AHCICommandHeader { - pwa_cfl: u8, - pmp_cbr: u8, - prdtl: u16, - prdbc: u32, - ctba0: u32, - ctba_u0: u32, - reservec: [u32; 4], + /// + flags: CommandHeaderFlags, + /// Physical region descriptor table length in entries + prdt_length: u16, + /// Physical region descriptor byte count transferred + prd_byte_count: u32, + /// Command table descriptor base address + command_table_base_address: u64, + /// Reserved + reserved: [u32; 4], +} + +bitflags! { + pub struct CommandHeaderFlags: u16 { + /// Command FIS length in DWORDS, 2 ~ 16 + const CFL_MASK = 0b11111; + /// ATAPI + const ATAPI = 1 << 5; + /// Write, 1: H2D, 0: D2H + const WRITE = 1 << 6; + /// Prefetchable + const PREFETCHABLE = 1 << 7; + /// Reset + const RESET = 1 << 8; + /// BIST + const BIST = 1 << 9; + /// Clear busy upon R_OK + const CLEAR = 1 << 10; + /// Port multiplier port + const PORT_MULTIPLIER_PORT_MASK = 0b1111 << 12; + } } -// AHCI -// 4.2.3 Command Table +/// AHCI Command Table (4.2.3) #[repr(C)] pub struct AHCICommandTable { - cfis: [u8; 64], + /// Command FIS + cfis: SATAFISRegH2D, + /// ATAPI command, 12 or 16 bytes acmd: [u8; 16], + /// Reserved reserved: [u8; 48], - prdt: [AHCIPRD; 1], + /// Physical region descriptor table entries, 0 ~ 65535 + prdt: [AHCIPrdtEntry; 1], } -// 4.2.3 Command Table +/// Physical region descriptor table entry #[repr(C)] -pub struct AHCIPRD { - dba: u32, - dbau: u32, +pub struct AHCIPrdtEntry { + /// Data base address + data_base_address: u64, + /// Reserved reserved: u32, + /// Bit 21-0: Byte count, 4M max + /// Bit 31: Interrupt on completion dbc_i: u32, } @@ -160,32 +230,47 @@ const CMD_READ_DMA_EXT: u8 = 0x25; const CMD_WRITE_DMA_EXT: u8 = 0x35; const CMD_IDENTIFY_DEVICE: u8 = 0xec; -// https://wiki.osdev.org/AHCI -// SATA -// Figure 5-2: Register FIS - Host to Device +/// SATA Register FIS - Host to Device +/// +/// https://wiki.osdev.org/AHCI Figure 5-2 #[repr(C)] -#[derive(Default)] pub struct SATAFISRegH2D { fis_type: u8, cflags: u8, command: u8, feature_lo: u8, + lba_0: u8, // LBA 7:0 lba_1: u8, // LBA 15:8 lba_2: u8, // LBA 23:16 dev_head: u8, + lba_3: u8, // LBA 31:24 lba_4: u8, // LBA 39:32 lba_5: u8, // LBA 47:40 feature_hi: u8, - sector_count_lo: u8, - sector_count_hi: u8, + + sector_count: u16, reserved: u8, control: u8, + + _padding: [u8; 48], +} + +impl SATAFISRegH2D { + fn set_lba(&mut self, lba: u64) { + self.lba_0 = (lba >> 0) as u8; + self.lba_1 = (lba >> 8) as u8; + self.lba_2 = (lba >> 16) as u8; + self.lba_3 = (lba >> 24) as u8; + self.lba_4 = (lba >> 32) as u8; + self.lba_5 = (lba >> 40) as u8; + } } -// ATA8-ACS -// Table 29 IDENTIFY DEVICE data +/// IDENTIFY DEVICE data +/// +/// ATA8-ACS Table 29 #[repr(C)] pub struct ATAIdentifyPacket { _1: [u16; 10], @@ -199,101 +284,80 @@ pub struct ATAIdentifyPacket { lba48_sectors: u64, // words 100-103 } -impl Driver for AHCIDriver { - fn try_handle_interrupt(&self, _irq: Option) -> bool { - false - } - - fn device_type(&self) -> DeviceType { - DeviceType::Block - } - - fn get_id(&self) -> String { - format!("ahci") - } - - fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool { - let mut driver = self.0.lock(); - - driver.cmd_list[0].prdbc = 0; - driver.cmd_list[0].pwa_cfl = 0; - - let len = min(BLOCK_SIZE, buf.len()); +impl AHCI { + fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> usize { + self.cmd_list[0].flags = CommandHeaderFlags::empty(); - let fis = unsafe { &mut *(driver.cmd_table.cfis.as_ptr() as *mut SATAFISRegH2D) }; + let fis = &mut self.cmd_table.cfis; // Register FIS from HBA to device fis.fis_type = FIS_REG_H2D; fis.cflags = 1 << 7; - // 7.25 READ DMA EXT - 25h, DMA fis.command = CMD_READ_DMA_EXT; - fis.sector_count_lo = 1; - fis.sector_count_hi = 0; + fis.sector_count = 1; fis.dev_head = 0x40; // LBA fis.control = 0x80; // LBA48 + fis.set_lba(block_id as u64); - let block_id = block_id as u64; // avoid problems on riscv32 - fis.lba_0 = block_id as u8; - fis.lba_1 = (block_id >> 8) as u8; - fis.lba_2 = (block_id >> 16) as u8; - fis.lba_3 = (block_id >> 24) as u8; - fis.lba_4 = (block_id >> 32) as u8; - fis.lba_5 = (block_id >> 40) as u8; + self.port.issue_command(0); + self.port.spin_on_slot(0); - driver.port.ci.write(1 << 0); - - loop { - let ci = driver.port.ci.read(); - if (ci & (1 << 0)) == 0 { - break; - } - } - - buf[..len].clone_from_slice(&driver.data[0..len]); - return true; + let len = buf.len().min(BLOCK_SIZE); + buf[..len].clone_from_slice(&self.data[0..len]); + len } - fn write_block(&self, block_id: usize, buf: &[u8]) -> bool { - if buf.len() < BLOCK_SIZE { - return false; - } - let mut driver = self.0.lock(); - - driver.cmd_list[0].prdbc = 0; - driver.cmd_list[0].pwa_cfl = 1 << 6; // devic write + fn write_block(&mut self, block_id: usize, buf: &[u8]) -> usize { + self.cmd_list[0].flags = CommandHeaderFlags::WRITE; // device write - driver.data[0..BLOCK_SIZE].clone_from_slice(&buf[..BLOCK_SIZE]); + let len = buf.len().min(BLOCK_SIZE); + self.data[0..len].clone_from_slice(&buf[..len]); - let fis = unsafe { &mut *(driver.cmd_table.cfis.as_ptr() as *mut SATAFISRegH2D) }; + let fis = &mut self.cmd_table.cfis; // Register FIS from HBA to device fis.fis_type = FIS_REG_H2D; fis.cflags = 1 << 7; - // ATA8-ACS // 7.63 WRITE DMA EXT - 35h, DMA fis.command = CMD_WRITE_DMA_EXT; - fis.sector_count_lo = 1; - fis.sector_count_hi = 0; + fis.sector_count = 1; fis.dev_head = 0x40; // LBA fis.control = 0x80; // LBA48 + fis.set_lba(block_id as u64); - let block_id = block_id as u64; // avoid problems on riscv32 - fis.lba_0 = block_id as u8; - fis.lba_1 = (block_id >> 8) as u8; - fis.lba_2 = (block_id >> 16) as u8; - fis.lba_3 = (block_id >> 24) as u8; - fis.lba_4 = (block_id >> 32) as u8; - fis.lba_5 = (block_id >> 40) as u8; + self.port.issue_command(0); + self.port.spin_on_slot(0); - driver.port.ci.write(1 << 0); + len + } +} - loop { - let ci = driver.port.ci.read(); - if (ci & (1 << 0)) == 0 { - break; - } +impl Driver for AHCIDriver { + fn try_handle_interrupt(&self, _irq: Option) -> bool { + false + } + + fn device_type(&self) -> DeviceType { + DeviceType::Block + } + + fn get_id(&self) -> String { + format!("ahci") + } + + fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool { + let mut driver = self.0.lock(); + driver.read_block(block_id, buf); + true + } + + fn write_block(&self, block_id: usize, buf: &[u8]) -> bool { + if buf.len() < BLOCK_SIZE { + return false; } - return true; + let mut driver = self.0.lock(); + driver.write_block(block_id, buf); + true } } @@ -320,93 +384,82 @@ fn alloc_dma(page_num: usize) -> (VirtAddr, PhysAddr) { pub fn ahci_init(irq: Option, header: usize, size: usize) -> Arc { let ghc = unsafe { &mut *(header as *mut AHCIGHC) }; - // AHCI Enable - ghc.ghc.write(ghc.ghc.read() | (1 << 13)); + ghc.enable(); - let num_ports = (ghc.cap.read().bits() & 0x1f) as usize + 1; - - for port_num in 0..num_ports { - if (ghc.pi.read() | (1 << port_num)) != 0 { + for port_num in 0..ghc.num_ports() { + if ghc.has_port(port_num) { let addr = header + 0x100 + 0x80 * port_num; let port = unsafe { &mut *(addr as *mut AHCIPort) }; // SSTS IPM Active - if (port.ssts.read() >> 8) & 0xF != 1 { + if port.sata_status.read().get_bits(8..12) != 1 { continue; } // SSTS DET Present - if port.ssts.read() & 0xF != 3 { + if port.sata_status.read().get_bits(0..4) != 3 { continue; } debug!("probing port {}", port_num); // Disable Port First - port.cmd.write(port.cmd.read() & !(1 << 4 | 1 << 0)); + port.command.update(|c| { + c.set_bit(4, false); + c.set_bit(0, false); + }); let (rfis_va, rfis_pa) = alloc_dma(1); let (cmd_list_va, cmd_list_pa) = alloc_dma(1); let (cmd_table_va, cmd_table_pa) = alloc_dma(1); let (data_va, data_pa) = alloc_dma(1); + let received_fis = unsafe { &mut *(rfis_va as *mut AHCIReceivedFIS) }; let cmd_list = unsafe { slice::from_raw_parts_mut( cmd_list_va as *mut AHCICommandHeader, PAGE_SIZE / size_of::(), ) }; - let cmd_table = unsafe { &mut *(cmd_table_va as *mut AHCICommandTable) }; + let identify_data = unsafe { &*(data_va as *mut ATAIdentifyPacket) }; - cmd_table.prdt[0].dba = data_pa as u32; - cmd_table.prdt[0].dbau = (data_pa >> 32) as u32; + cmd_table.prdt[0].data_base_address = data_pa as u64; cmd_table.prdt[0].dbc_i = (BLOCK_SIZE - 1) as u32; - cmd_list[0].ctba0 = cmd_table_pa as u32; - cmd_list[0].ctba_u0 = (cmd_table_pa >> 32) as u32; - cmd_list[0].prdtl = 1; - cmd_list[0].prdbc = 0; + cmd_list[0].command_table_base_address = cmd_table_pa as u64; + cmd_list[0].prdt_length = 1; + cmd_list[0].prd_byte_count = 0; - port.clb.write(cmd_list_pa as u32); - port.clbu.write((cmd_list_pa >> 32) as u32); - - port.fb.write(rfis_pa as u32); - port.fbu.write((rfis_pa >> 32) as u32); + port.command_list_base_address.write(cmd_list_pa as u64); + port.fis_base_address.write(rfis_pa as u64); // clear status and errors - port.ci.write(0); - port.sact.write(0); - port.serr.write(0); + port.command_issue.write(0); + port.sata_active.write(0); + port.sata_error.write(0); // enable port - port.cmd - .write(port.cmd.read() | 1 << 0 | 1 << 1 | 1 << 2 | 1 << 4 | 1 << 28); + port.command.update(|c| { + *c |= 1 << 0 | 1 << 1 | 1 << 2 | 1 << 4 | 1 << 28; + }); - let stat = port.ssts.read(); + let stat = port.sata_status.read(); if stat == 0 { warn!("port is not connected to external drive?"); } - let fis = unsafe { &mut *(cmd_table.cfis.as_ptr() as *mut SATAFISRegH2D) }; + let fis = &mut cmd_table.cfis; // Register FIS from HBA to device fis.fis_type = FIS_REG_H2D; fis.cflags = 1 << 7; // 7.15 IDENTIFY DEVICE - ECh, PIO Data-In fis.command = CMD_IDENTIFY_DEVICE; - fis.sector_count_lo = 1; + fis.sector_count = 1; debug!("issued identify command"); - port.ci.write(1 << 0); - - loop { - let ci = port.ci.read(); - if (ci & (1 << 0)) == 0 { - break; - } - } - - let identify_data = unsafe { &*(data_va as *mut ATAIdentifyPacket) }; + port.issue_command(0); + port.spin_on_slot(0); unsafe { debug!( @@ -424,7 +477,7 @@ pub fn ahci_init(irq: Option, header: usize, size: usize) -> Arc