1 //! 文件说明: 实现了 AHCI 中的控制器 HBA 的相关行为 2 use core::{intrinsics::size_of, ptr}; 3 4 use core::sync::atomic::compiler_fence; 5 6 use crate::arch::MMArch; 7 use crate::mm::{MemoryManagementArch, PhysAddr}; 8 9 /// 根据 AHCI 写出 HBA 的 Command 10 pub const ATA_CMD_READ_DMA_EXT: u8 = 0x25; // 读操作,并且退出 11 pub const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35; // 写操作,并且退出 12 #[allow(dead_code)] 13 pub const ATA_CMD_IDENTIFY: u8 = 0xEC; 14 #[allow(dead_code)] 15 pub const ATA_CMD_IDENTIFY_PACKET: u8 = 0xA1; 16 #[allow(dead_code)] 17 pub const ATA_CMD_PACKET: u8 = 0xA0; 18 pub const ATA_DEV_BUSY: u8 = 0x80; 19 pub const ATA_DEV_DRQ: u8 = 0x08; 20 21 pub const HBA_PORT_CMD_CR: u32 = 1 << 15; 22 pub const HBA_PORT_CMD_FR: u32 = 1 << 14; 23 pub const HBA_PORT_CMD_FRE: u32 = 1 << 4; 24 pub const HBA_PORT_CMD_ST: u32 = 1; 25 #[allow(dead_code)] 26 pub const HBA_PORT_IS_ERR: u32 = 1 << 30 | 1 << 29 | 1 << 28 | 1 << 27; 27 pub const HBA_SSTS_PRESENT: u32 = 0x3; 28 pub const HBA_SIG_ATA: u32 = 0x00000101; 29 pub const HBA_SIG_ATAPI: u32 = 0xEB140101; 30 pub const HBA_SIG_PM: u32 = 0x96690101; 31 pub const HBA_SIG_SEMB: u32 = 0xC33C0101; 32 33 /// 接入 Port 的 不同设备类型 34 #[derive(Debug)] 35 pub enum HbaPortType { 36 None, 37 Unknown(u32), 38 SATA, 39 SATAPI, 40 PM, 41 SEMB, 42 } 43 44 /// 声明了 HBA 的所有属性 45 #[repr(packed)] 46 #[allow(dead_code)] 47 pub struct HbaPort { 48 pub clb: u64, // 0x00, command list base address, 1K-byte aligned 49 pub fb: u64, // 0x08, FIS base address, 256-byte aligned 50 pub is: u32, // 0x10, interrupt status 51 pub ie: u32, // 0x14, interrupt enable 52 pub cmd: u32, // 0x18, command and status 53 pub _rsv0: u32, // 0x1C, Reserved 54 pub tfd: u32, // 0x20, task file data 55 pub sig: u32, // 0x24, signature 56 pub ssts: u32, // 0x28, SATA status (SCR0:SStatus) 57 pub sctl: u32, // 0x2C, SATA control (SCR2:SControl) 58 pub serr: u32, // 0x30, SATA error (SCR1:SError) 59 pub sact: u32, // 0x34, SATA active (SCR3:SActive) 60 pub ci: u32, // 0x38, command issue 61 pub sntf: u32, // 0x3C, SATA notification (SCR4:SNotification) 62 pub fbs: u32, // 0x40, FIS-based switch control 63 pub _rsv1: [u32; 11], // 0x44 ~ 0x6F, Reserved 64 pub vendor: [u32; 4], // 0x70 ~ 0x7F, vendor specific 65 } 66 67 /// 全称 HBA Memory Register,是HBA的寄存器在内存中的映射 68 #[repr(packed)] 69 #[allow(dead_code)] 70 pub struct HbaMem { 71 pub cap: u32, // 0x00, Host capability 72 pub ghc: u32, // 0x04, Global host control 73 pub is: u32, // 0x08, Interrupt status 74 pub pi: u32, // 0x0C, Port implemented 75 pub vs: u32, // 0x10, Version 76 pub ccc_ctl: u32, // 0x14, Command completion coalescing control 77 pub ccc_pts: u32, // 0x18, Command completion coalescing ports 78 pub em_loc: u32, // 0x1C, Enclosure management location 79 pub em_ctl: u32, // 0x20, Enclosure management control 80 pub cap2: u32, // 0x24, Host capabilities extended 81 pub bohc: u32, // 0x28, BIOS/OS handoff control and status 82 pub _rsv: [u8; 116], // 0x2C - 0x9F, Reserved 83 pub vendor: [u8; 96], // 0xA0 - 0xFF, Vendor specific registers 84 pub ports: [HbaPort; 32], // 0x100 - 0x10FF, Port control registers 85 } 86 87 /// HBA Command Table 里面的 PRDT 项 88 /// 作用: 记录了内存中读/写数据的位置,以及长度。你可以把他类比成一个指针? 89 #[repr(packed)] 90 pub struct HbaPrdtEntry { 91 pub dba: u64, // Data base address 92 _rsv0: u32, // Reserved 93 pub dbc: u32, // Byte count, 4M max, interrupt = 1 94 } 95 96 /// HAB Command Table 97 /// 每个 Port 一个 Table,主机和设备的交互都靠这个数据结构 98 #[repr(packed)] 99 #[allow(dead_code)] 100 pub struct HbaCmdTable { 101 // 0x00 102 pub cfis: [u8; 64], // Command FIS 103 // 0x40 104 pub acmd: [u8; 16], // ATAPI command, 12 or 16 bytes 105 // 0x50 106 _rsv: [u8; 48], // Reserved 107 // 0x80 108 pub prdt_entry: [HbaPrdtEntry; 8], // Physical region descriptor table entries, 0 ~ 65535, 需要注意不要越界 这里设置8的原因是,目前CmdTable只预留了8个PRDT项的空间 109 } 110 111 /// HBA Command Header 112 /// 作用: 你可以把他类比成 Command Table 的指针。 113 /// 猜测: 这里多了一层 Header,而不是直接在 HbaMem 结构体指向 CmdTable,可能是为了兼容和可移植性? 114 #[repr(packed)] 115 pub struct HbaCmdHeader { 116 // DW0 117 pub cfl: u8, 118 // Command FIS length in DWORDS: 5(len in [2, 16]), atapi: 1, write - host to device: 1, prefetchable: 1 119 pub _pm: u8, // Reset - 0x80, bist: 0x40, clear busy on ok: 0x20, port multiplier 120 pub prdtl: u16, // Physical region descriptor table length in entries 121 // DW1 122 pub _prdbc: u32, // Physical region descriptor byte count transferred 123 // DW2, 3 124 pub ctba: u64, // Command table descriptor base address 125 // DW4 - 7 126 pub _rsv1: [u32; 4], // Reserved 127 } 128 129 /// Port 的函数实现 130 impl HbaPort { 131 /// 获取设备类型 check_type(&mut self) -> HbaPortType132 pub fn check_type(&mut self) -> HbaPortType { 133 if volatile_read!(self.ssts) & HBA_SSTS_PRESENT > 0 { 134 let sig = volatile_read!(self.sig); 135 match sig { 136 HBA_SIG_ATA => HbaPortType::SATA, 137 HBA_SIG_ATAPI => HbaPortType::SATAPI, 138 HBA_SIG_PM => HbaPortType::PM, 139 HBA_SIG_SEMB => HbaPortType::SEMB, 140 _ => HbaPortType::Unknown(sig), 141 } 142 } else { 143 HbaPortType::None 144 } 145 } 146 147 /// 启动该端口的命令引擎 start(&mut self)148 pub fn start(&mut self) { 149 while volatile_read!(self.cmd) & HBA_PORT_CMD_CR > 0 { 150 core::hint::spin_loop(); 151 } 152 let val: u32 = volatile_read!(self.cmd) | HBA_PORT_CMD_FRE | HBA_PORT_CMD_ST; 153 volatile_write!(self.cmd, val); 154 } 155 156 /// 关闭该端口的命令引擎 stop(&mut self)157 pub fn stop(&mut self) { 158 #[allow(unused_unsafe)] 159 { 160 volatile_write!( 161 self.cmd, 162 (u32::MAX ^ HBA_PORT_CMD_ST) & volatile_read!(self.cmd) 163 ); 164 } 165 166 while volatile_read!(self.cmd) & (HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) 167 == (HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) 168 { 169 core::hint::spin_loop(); 170 } 171 172 #[allow(unused_unsafe)] 173 { 174 volatile_write!( 175 self.cmd, 176 (u32::MAX ^ HBA_PORT_CMD_FRE) & volatile_read!(self.cmd) 177 ); 178 } 179 } 180 181 /// @return: 返回一个空闲 cmd table 的 id; 如果没有,则返回 Option::None find_cmdslot(&self) -> Option<u32>182 pub fn find_cmdslot(&self) -> Option<u32> { 183 let slots = volatile_read!(self.sact) | volatile_read!(self.ci); 184 (0..32).find(|&i| slots & 1 << i == 0) 185 } 186 187 /// 初始化, 把 CmdList 等变量的地址赋值到 HbaPort 上 - 这些空间由操作系统分配且固定 188 /// 等价于原C版本的 port_rebase 函数 init(&mut self, clb: u64, fb: u64, ctbas: &[u64])189 pub fn init(&mut self, clb: u64, fb: u64, ctbas: &[u64]) { 190 self.stop(); // 先暂停端口 191 192 // 赋值 command list base address 193 // Command list offset: 1K*portno 194 // Command list entry size = 32 195 // Command list entry maxim count = 32 196 // Command list maxim size = 32*32 = 1K per port 197 volatile_write!(self.clb, clb); 198 199 unsafe { 200 compiler_fence(core::sync::atomic::Ordering::SeqCst); 201 ptr::write_bytes( 202 MMArch::phys_2_virt(PhysAddr::new(clb as usize)) 203 .unwrap() 204 .data() as *mut u64, 205 0, 206 1024, 207 ); 208 } 209 210 // 赋值 fis base address 211 // FIS offset: 32K+256*portno 212 // FIS entry size = 256 bytes per port 213 volatile_write!(self.fb, fb); 214 unsafe { 215 compiler_fence(core::sync::atomic::Ordering::SeqCst); 216 ptr::write_bytes( 217 MMArch::phys_2_virt(PhysAddr::new(fb as usize)) 218 .unwrap() 219 .data() as *mut u64, 220 0, 221 256, 222 ); 223 } 224 225 // 赋值 command table base address 226 // Command table offset: 40K + 8K*portno 227 // Command table size = 256*32 = 8K per port 228 let mut cmdheaders = unsafe { 229 MMArch::phys_2_virt(PhysAddr::new(clb as usize)) 230 .unwrap() 231 .data() 232 } as *mut u64 as *mut HbaCmdHeader; 233 for ctbas_value in ctbas.iter().take(32) { 234 volatile_write!((*cmdheaders).prdtl, 0); // 一开始没有询问,prdtl = 0(预留了8个PRDT项的空间) 235 volatile_write!((*cmdheaders).ctba, *ctbas_value); 236 // 这里限制了 prdtl <= 8, 所以一共用了256bytes,如果需要修改,可以修改这里 237 compiler_fence(core::sync::atomic::Ordering::SeqCst); 238 unsafe { 239 ptr::write_bytes( 240 MMArch::phys_2_virt(PhysAddr::new(*ctbas_value as usize)) 241 .unwrap() 242 .data() as *mut u64, 243 0, 244 256, 245 ); 246 } 247 cmdheaders = (cmdheaders as usize + size_of::<HbaCmdHeader>()) as *mut HbaCmdHeader; 248 } 249 250 #[allow(unused_unsafe)] 251 { 252 // 启动中断 253 volatile_write!(self.ie, 0 /*TODO: Enable interrupts: 0b10111*/); 254 255 // 错误码 256 volatile_write!(self.serr, volatile_read!(self.serr)); 257 258 // Disable power management 259 volatile_write!(self.sctl, volatile_read!(self.sctl) | 7 << 8); 260 261 // Power on and spin up device 262 volatile_write!(self.cmd, volatile_read!(self.cmd) | 1 << 2 | 1 << 1); 263 } 264 self.start(); // 重新开启端口 265 } 266 } 267 268 #[repr(u8)] 269 #[allow(dead_code)] 270 pub enum FisType { 271 /// Register FIS - host to device 272 RegH2D = 0x27, 273 /// Register FIS - device to host 274 RegD2H = 0x34, 275 /// DMA activate FIS - device to host 276 DmaAct = 0x39, 277 /// DMA setup FIS - bidirectional 278 DmaSetup = 0x41, 279 /// Data FIS - bidirectional 280 Data = 0x46, 281 /// BIST activate FIS - bidirectional 282 Bist = 0x58, 283 /// PIO setup FIS - device to host 284 PioSetup = 0x5F, 285 /// Set device bits FIS - device to host 286 DevBits = 0xA1, 287 } 288 289 #[repr(packed)] 290 #[allow(dead_code)] 291 pub struct FisRegH2D { 292 // DWORD 0 293 pub fis_type: u8, // FIS_TYPE_REG_H2D 294 295 pub pm: u8, // Port multiplier, 1: Command, 0: Control 296 // uint8_t pmport : 4; // Port multiplier 低4位 297 // uint8_t rsv0 : 3; // Reserved 298 // uint8_t c : 1; // 1: Command, 0: Control 299 pub command: u8, // Command register 300 pub featurel: u8, // Feature register, 7:0 301 302 // DWORD 1 303 pub lba0: u8, // LBA low register, 7:0 304 pub lba1: u8, // LBA mid register, 15:8 305 pub lba2: u8, // LBA high register, 23:16 306 pub device: u8, // Device register 307 308 // DWORD 2 309 pub lba3: u8, // LBA register, 31:24 310 pub lba4: u8, // LBA register, 39:32 311 pub lba5: u8, // LBA register, 47:40 312 pub featureh: u8, // Feature register, 15:8 313 314 // DWORD 3 315 pub countl: u8, // Count register, 7:0 316 pub counth: u8, // Count register, 15:8 317 pub icc: u8, // Isochronous command completion 318 pub control: u8, // Control register 319 320 // DWORD 4 321 pub rsv1: [u8; 4], // Reserved 322 } 323 324 #[repr(packed)] 325 #[allow(dead_code)] 326 pub struct FisRegD2H { 327 // DWORD 0 328 pub fis_type: u8, // FIS_TYPE_REG_D2H 329 330 pub pm: u8, // Port multiplier, Interrupt bit: 2 331 332 pub status: u8, // Status register 333 pub error: u8, // Error register 334 335 // DWORD 1 336 pub lba0: u8, // LBA low register, 7:0 337 pub lba1: u8, // LBA mid register, 15:8 338 pub lba2: u8, // LBA high register, 23:16 339 pub device: u8, // Device register 340 341 // DWORD 2 342 pub lba3: u8, // LBA register, 31:24 343 pub lba4: u8, // LBA register, 39:32 344 pub lba5: u8, // LBA register, 47:40 345 pub rsv2: u8, // Reserved 346 347 // DWORD 3 348 pub countl: u8, // Count register, 7:0 349 pub counth: u8, // Count register, 15:8 350 pub rsv3: [u8; 2], // Reserved 351 352 // DWORD 4 353 pub rsv4: [u8; 4], // Reserved 354 } 355 356 #[repr(packed)] 357 #[allow(dead_code)] 358 pub struct FisData { 359 // DWORD 0 360 pub fis_type: u8, // FIS_TYPE_DATA 361 362 pub pm: u8, // Port multiplier 363 364 pub rsv1: [u8; 2], // Reserved 365 366 // DWORD 1 ~ N 367 pub data: [u8; 252], // Payload 368 } 369 370 #[repr(packed)] 371 #[allow(dead_code)] 372 pub struct FisPioSetup { 373 // DWORD 0 374 pub fis_type: u8, // FIS_TYPE_PIO_SETUP 375 376 pub pm: u8, // Port multiplier, direction: 4 - device to host, interrupt: 2 377 378 pub status: u8, // Status register 379 pub error: u8, // Error register 380 381 // DWORD 1 382 pub lba0: u8, // LBA low register, 7:0 383 pub lba1: u8, // LBA mid register, 15:8 384 pub lba2: u8, // LBA high register, 23:16 385 pub device: u8, // Device register 386 387 // DWORD 2 388 pub lba3: u8, // LBA register, 31:24 389 pub lba4: u8, // LBA register, 39:32 390 pub lba5: u8, // LBA register, 47:40 391 pub rsv2: u8, // Reserved 392 393 // DWORD 3 394 pub countl: u8, // Count register, 7:0 395 pub counth: u8, // Count register, 15:8 396 pub rsv3: u8, // Reserved 397 pub e_status: u8, // New value of status register 398 399 // DWORD 4 400 pub tc: u16, // Transfer count 401 pub rsv4: [u8; 2], // Reserved 402 } 403 404 #[repr(packed)] 405 #[allow(dead_code)] 406 pub struct FisDmaSetup { 407 // DWORD 0 408 pub fis_type: u8, // FIS_TYPE_DMA_SETUP 409 410 pub pm: u8, // Port multiplier, direction: 4 - device to host, interrupt: 2, auto-activate: 1 411 412 pub rsv1: [u8; 2], // Reserved 413 414 // DWORD 1&2 415 pub dma_buffer_id: u64, /* DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. */ 416 417 // DWORD 3 418 pub rsv3: u32, // More reserved 419 420 // DWORD 4 421 pub dma_buffer_offset: u32, // Byte offset into buffer. First 2 bits must be 0 422 423 // DWORD 5 424 pub transfer_count: u32, // Number of bytes to transfer. Bit 0 must be 0 425 426 // DWORD 6 427 pub rsv6: u32, // Reserved 428 } 429