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