xref: /DragonOS/kernel/src/driver/disk/ahci/hba.rs (revision 7c28051e8c601312d3d0fd7bcb71bc71450d10c0)
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