xref: /DragonOS/kernel/src/driver/disk/ahci/hba.rs (revision a8753f8fffb992e4d3bbd21eda431081b395af6b)
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     /// 获取设备类型
133     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     /// 启动该端口的命令引擎
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     /// 关闭该端口的命令引擎
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
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 函数
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