fix: use wait queue for blocking process
[lunaix-os.git] / lunaix-os / hal / ahci / ahci.c
1 /**
2  * @file ahci.c
3  * @author Lunaixsky (zelong56@gmail.com)
4  * @brief A software implementation of Serial ATA AHCI 1.3.1 Specification
5  * @version 0.1
6  * @date 2022-06-28
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11 #include <hal/ahci/ahci.h>
12 #include <hal/ahci/hba.h>
13 #include <hal/ahci/sata.h>
14 #include <hal/ahci/scsi.h>
15
16 #include <hal/pci.h>
17 #include <klibc/string.h>
18 #include <lunaix/block.h>
19 #include <lunaix/isrm.h>
20 #include <lunaix/mm/mmio.h>
21 #include <lunaix/mm/pmm.h>
22 #include <lunaix/mm/valloc.h>
23 #include <lunaix/mm/vmm.h>
24 #include <lunaix/spike.h>
25 #include <lunaix/syslog.h>
26
27 #define HBA_FIS_SIZE 256
28 #define HBA_CLB_SIZE 1024
29
30 #define HBA_MY_IE (HBA_PxINTR_DHR | HBA_PxINTR_TFEE)
31
32 // #define DO_HBA_FULL_RESET
33
34 LOG_MODULE("AHCI")
35
36 struct ahci_hba hba;
37
38 static char sata_ifs[][20] = { "Not detected",
39                                "SATA I (1.5Gbps)",
40                                "SATA II (3.0Gbps)",
41                                "SATA III (6.0Gbps)" };
42
43 extern void
44 ahci_fsexport(struct block_dev* bdev, void* fs_node);
45
46 extern void
47 __ahci_hba_isr(const isr_param* param);
48
49 extern void
50 __ahci_blkio_handler(struct blkio_req* req);
51
52 int
53 ahci_init_device(struct hba_port* port);
54
55 void
56 achi_register_ops(struct hba_port* port);
57
58 void
59 ahci_register_device(struct hba_device* hbadev);
60
61 unsigned int
62 ahci_get_port_usage()
63 {
64     return hba.ports_bmp;
65 }
66
67 struct hba_port*
68 ahci_get_port(unsigned int index)
69 {
70     if (index >= 32) {
71         return 0;
72     }
73     return hba.ports[index];
74 }
75
76 void
77 __hba_reset_port(hba_reg_t* port_reg)
78 {
79     // 根据:SATA-AHCI spec section 10.4.2 描述的端口重置流程
80     port_reg[HBA_RPxCMD] &= ~HBA_PxCMD_ST;
81     port_reg[HBA_RPxCMD] &= ~HBA_PxCMD_FRE;
82     int cnt = wait_until_expire(!(port_reg[HBA_RPxCMD] & HBA_PxCMD_CR), 500000);
83     if (cnt) {
84         return;
85     }
86     // 如果port未响应,则继续执行重置
87     port_reg[HBA_RPxSCTL] = (port_reg[HBA_RPxSCTL] & ~0xf) | 1;
88     io_delay(100000); //等待至少一毫秒,差不多就行了
89     port_reg[HBA_RPxSCTL] &= ~0xf;
90 }
91
92 void
93 ahci_init()
94 {
95     struct pci_device* ahci_dev = pci_get_device_by_class(AHCI_HBA_CLASS);
96     assert_msg(ahci_dev, "AHCI: Not found.");
97
98     struct pci_base_addr* bar6 = &ahci_dev->bar[5];
99     assert_msg(bar6->type & BAR_TYPE_MMIO, "AHCI: BAR#6 is not MMIO.");
100
101     pci_reg_t cmd = pci_read_cspace(ahci_dev->cspace_base, PCI_REG_STATUS_CMD);
102
103     // 禁用传统中断(因为我们使用MSI),启用MMIO访问,允许PCI设备间访问
104     cmd |= (PCI_RCMD_MM_ACCESS | PCI_RCMD_DISABLE_INTR | PCI_RCMD_BUS_MASTER);
105
106     pci_write_cspace(ahci_dev->cspace_base, PCI_REG_STATUS_CMD, cmd);
107
108     pci_setup_msi(ahci_dev, isrm_ivexalloc(__ahci_hba_isr));
109
110     memset(&hba, 0, sizeof(hba));
111
112     hba.base = (hba_reg_t*)ioremap(bar6->start, bar6->size);
113
114 #ifdef DO_HBA_FULL_RESET
115     // 重置HBA
116     hba.base[HBA_RGHC] |= HBA_RGHC_RESET;
117     wait_until(!(hba.base[HBA_RGHC] & HBA_RGHC_RESET));
118 #endif
119
120     // 启用AHCI工作模式,启用中断
121     hba.base[HBA_RGHC] |= HBA_RGHC_ACHI_ENABLE;
122     hba.base[HBA_RGHC] |= HBA_RGHC_INTR_ENABLE;
123
124     // As per section 3.1.1, this is 0 based value.
125     hba_reg_t cap = hba.base[HBA_RCAP];
126     hba_reg_t pmap = hba.base[HBA_RPI];
127
128     hba.ports_num = (cap & 0x1f) + 1;  // CAP.PI
129     hba.cmd_slots = (cap >> 8) & 0x1f; // CAP.NCS
130     hba.version = hba.base[HBA_RVER];
131     hba.ports_bmp = pmap;
132
133     /* ------ HBA端口配置 ------ */
134     uintptr_t clb_pg_addr, fis_pg_addr, clb_pa, fis_pa;
135     for (size_t i = 0, fisp = 0, clbp = 0; i < 32;
136          i++, pmap >>= 1, fisp = (fisp + 1) % 16, clbp = (clbp + 1) % 4) {
137         if (!(pmap & 0x1)) {
138             continue;
139         }
140
141         struct hba_port* port =
142           (struct hba_port*)valloc(sizeof(struct hba_port));
143         hba_reg_t* port_regs =
144           (hba_reg_t*)(&hba.base[HBA_RPBASE + i * HBA_RPSIZE]);
145
146 #ifndef DO_HBA_FULL_RESET
147         __hba_reset_port(port_regs);
148 #endif
149
150         if (!clbp) {
151             // 每页最多4个命令队列
152             clb_pa = pmm_alloc_page(KERNEL_PID, PP_FGLOCKED);
153             clb_pg_addr = ioremap(clb_pa, 0x1000);
154             memset(clb_pg_addr, 0, 0x1000);
155         }
156         if (!fisp) {
157             // 每页最多16个FIS
158             fis_pa = pmm_alloc_page(KERNEL_PID, PP_FGLOCKED);
159             fis_pg_addr = ioremap(fis_pa, 0x1000);
160             memset(fis_pg_addr, 0, 0x1000);
161         }
162
163         /* 重定向CLB与FIS */
164         port_regs[HBA_RPxCLB] = clb_pa + clbp * HBA_CLB_SIZE;
165         port_regs[HBA_RPxFB] = fis_pa + fisp * HBA_FIS_SIZE;
166
167         *port = (struct hba_port){ .regs = port_regs,
168                                    .ssts = port_regs[HBA_RPxSSTS],
169                                    .cmdlst = clb_pg_addr + clbp * HBA_CLB_SIZE,
170                                    .fis = fis_pg_addr + fisp * HBA_FIS_SIZE };
171
172         /* 初始化端口,并置于就绪状态 */
173         port_regs[HBA_RPxCI] = 0;
174
175         hba_clear_reg(port_regs[HBA_RPxSERR]);
176
177         hba.ports[i] = port;
178
179         if (!HBA_RPxSSTS_IF(port->ssts)) {
180             continue;
181         }
182
183         wait_until(!(port_regs[HBA_RPxCMD] & HBA_PxCMD_CR));
184         port_regs[HBA_RPxCMD] |= HBA_PxCMD_FRE;
185         port_regs[HBA_RPxCMD] |= HBA_PxCMD_ST;
186
187         if (!ahci_init_device(port)) {
188             kprintf(KERROR "init fail: 0x%x@p%d\n", port->regs[HBA_RPxSIG], i);
189             continue;
190         }
191
192         struct hba_device* hbadev = port->device;
193         kprintf(KINFO "sata%d: %s, sector_size=%dB, sector=%d\n",
194                 i,
195                 hbadev->model,
196                 hbadev->block_size,
197                 (uint32_t)hbadev->max_lba);
198
199         ahci_register_device(hbadev);
200     }
201 }
202
203 void
204 ahci_register_device(struct hba_device* hbadev)
205 {
206     struct block_dev* bdev =
207       block_alloc_dev(hbadev->model, hbadev, __ahci_blkio_handler);
208
209     bdev->end_lba = hbadev->max_lba;
210     bdev->blk_size = hbadev->block_size;
211
212     block_mount(bdev, ahci_fsexport);
213 }
214
215 void
216 ahci_list_device()
217 {
218     kprintf(KINFO "Version: %x; Ports: %d; Slot: %d\n",
219             hba.version,
220             hba.ports_num,
221             hba.cmd_slots);
222     struct hba_port* port;
223     for (size_t i = 0; i < 32; i++) {
224         port = hba.ports[i];
225
226         // 愚蠢的gcc似乎认为 struct hba_port* 不可能为空
227         //  所以将这个非常关键的if给优化掉了。
228         //  这里将指针强制转换为整数,欺骗gcc :)
229         if ((uintptr_t)port == 0) {
230             continue;
231         }
232
233         int device_state = HBA_RPxSSTS_IF(port->ssts);
234
235         kprintf("\t Port %d: %s (%x)\n",
236                 i,
237                 &sata_ifs[device_state],
238                 port->device->flags);
239
240         struct hba_device* dev_info = port->device;
241         if (!device_state || !dev_info) {
242             continue;
243         }
244         kprintf("\t\t capacity: %d KiB\n",
245                 (dev_info->max_lba * dev_info->block_size) >> 10);
246         kprintf("\t\t block size: %dB\n", dev_info->block_size);
247         kprintf("\t\t block/sector: %d\n", dev_info->block_per_sec);
248         kprintf("\t\t alignment: %dB\n", dev_info->alignment_offset);
249         kprintf("\t\t capabilities: %x\n", dev_info->capabilities);
250         kprintf("\t\t model: %s\n", &dev_info->model);
251         kprintf("\t\t serial: %s\n", &dev_info->serial_num);
252     }
253 }
254
255 int
256 __get_free_slot(struct hba_port* port)
257 {
258     hba_reg_t pxsact = port->regs[HBA_RPxSACT];
259     hba_reg_t pxci = port->regs[HBA_RPxCI];
260     hba_reg_t free_bmp = pxsact | pxci;
261     uint32_t i = 0;
262     for (; i <= hba.cmd_slots && (free_bmp & 0x1); i++, free_bmp >>= 1)
263         ;
264     return i | -(i > hba.cmd_slots);
265 }
266
267 void
268 sata_create_fis(struct sata_reg_fis* cmd_fis,
269                 uint8_t command,
270                 uint64_t lba,
271                 uint16_t sector_count)
272 {
273     cmd_fis->head.type = SATA_REG_FIS_H2D;
274     cmd_fis->head.options = SATA_REG_FIS_COMMAND;
275     cmd_fis->head.status_cmd = command;
276     cmd_fis->dev = 0;
277
278     cmd_fis->lba0 = SATA_LBA_COMPONENT(lba, 0);
279     cmd_fis->lba8 = SATA_LBA_COMPONENT(lba, 8);
280     cmd_fis->lba16 = SATA_LBA_COMPONENT(lba, 16);
281     cmd_fis->lba24 = SATA_LBA_COMPONENT(lba, 24);
282
283     cmd_fis->lba32 = SATA_LBA_COMPONENT(lba, 32);
284     cmd_fis->lba40 = SATA_LBA_COMPONENT(lba, 40);
285
286     cmd_fis->count = sector_count;
287 }
288
289 int
290 hba_bind_sbuf(struct hba_cmdh* cmdh, struct hba_cmdt* cmdt, struct membuf mbuf)
291 {
292     assert_msg(mbuf.size <= 0x400000, "HBA: Buffer too big");
293     cmdh->prdt_len = 1;
294     cmdt->entries[0] = (struct hba_prdte){ .data_base = vmm_v2p(mbuf.buffer),
295                                            .byte_count = mbuf.size - 1 };
296 }
297
298 int
299 hba_bind_vbuf(struct hba_cmdh* cmdh, struct hba_cmdt* cmdt, struct vecbuf* vbuf)
300 {
301     size_t i = 0;
302     struct vecbuf* pos = vbuf;
303
304     do {
305         assert_msg(i < HBA_MAX_PRDTE, "HBA: Too many PRDTEs");
306         assert_msg(pos->buf.size <= 0x400000, "HBA: Buffer too big");
307
308         cmdt->entries[i++] =
309           (struct hba_prdte){ .data_base = vmm_v2p(pos->buf.buffer),
310                               .byte_count = pos->buf.size - 1 };
311         pos = list_entry(pos->components.next, struct vecbuf, components);
312     } while (pos != vbuf);
313
314     cmdh->prdt_len = i + 1;
315 }
316
317 int
318 hba_prepare_cmd(struct hba_port* port,
319                 struct hba_cmdt** cmdt,
320                 struct hba_cmdh** cmdh)
321 {
322     int slot = __get_free_slot(port);
323     assert_msg(slot >= 0, "HBA: No free slot");
324
325     // 构建命令头(Command Header)和命令表(Command Table)
326     struct hba_cmdh* cmd_header = &port->cmdlst[slot];
327     struct hba_cmdt* cmd_table = vzalloc_dma(sizeof(struct hba_cmdt));
328
329     memset(cmd_header, 0, sizeof(*cmd_header));
330
331     // 将命令表挂到命令头上
332     cmd_header->cmd_table_base = vmm_v2p(cmd_table);
333     cmd_header->options =
334       HBA_CMDH_FIS_LEN(sizeof(struct sata_reg_fis)) | HBA_CMDH_CLR_BUSY;
335
336     *cmdh = cmd_header;
337     *cmdt = cmd_table;
338
339     return slot;
340 }
341
342 int
343 ahci_init_device(struct hba_port* port)
344 {
345     /* 发送ATA命令,参考:SATA AHCI Spec Rev.1.3.1, section 5.5 */
346     struct hba_cmdt* cmd_table;
347     struct hba_cmdh* cmd_header;
348
349     // mask DHR interrupt
350     port->regs[HBA_RPxIE] &= ~HBA_MY_IE;
351
352     // 预备DMA接收缓存,用于存放HBA传回的数据
353     uint16_t* data_in = (uint16_t*)valloc_dma(512);
354
355     int slot = hba_prepare_cmd(port, &cmd_table, &cmd_header);
356     hba_bind_sbuf(
357       cmd_header, cmd_table, (struct membuf){ .buffer = data_in, .size = 512 });
358
359     port->device = vzalloc(sizeof(struct hba_device));
360     port->device->port = port;
361
362     // 在命令表中构建命令FIS
363     struct sata_reg_fis* cmd_fis = (struct sata_reg_fis*)cmd_table->command_fis;
364
365     // 根据设备类型使用合适的命令
366     if (port->regs[HBA_RPxSIG] == HBA_DEV_SIG_ATA) {
367         // ATA 一般为硬盘
368         sata_create_fis(cmd_fis, ATA_IDENTIFY_DEVICE, 0, 0);
369     } else {
370         // ATAPI 一般为光驱,软驱,或者磁带机
371         port->device->flags |= HBA_DEV_FATAPI;
372         sata_create_fis(cmd_fis, ATA_IDENTIFY_PAKCET_DEVICE, 0, 0);
373     }
374
375     if (!ahci_try_send(port, slot)) {
376         goto fail;
377     }
378
379     /*
380         等待数据到达内存
381         解析IDENTIFY DEVICE传回来的数据。
382           参考:
383             * ATA/ATAPI Command Set - 3 (ACS-3), Section 7.12.7
384     */
385     ahci_parse_dev_info(port->device, data_in);
386
387     if (!(port->device->flags & HBA_DEV_FATAPI)) {
388         goto done;
389     }
390
391     /*
392         注意:ATAPI设备是无法通过IDENTIFY PACKET DEVICE 获取容量信息的。
393         我们需要使用SCSI命令的READ_CAPACITY(16)进行获取。
394         步骤如下:
395             1. 因为ATAPI走的是SCSI,而AHCI对此专门进行了SATA的封装,
396                也就是通过SATA的PACKET命令对SCSI命令进行封装。所以我们
397                首先需要构建一个PACKET命令的FIS
398             2. 接着,在ACMD中构建命令READ_CAPACITY的CDB - 一种SCSI命令的封装
399             3. 然后把cmd_header->options的A位置位,表示这是一个送往ATAPI的命令。
400                 一点细节:
401                     1. HBA往底层SATA控制器发送PACKET FIS
402                     2. SATA控制器回复PIO Setup FIS
403                     3. HBA读入ACMD中的CDB,打包成Data FIS进行答复
404                     4. SATA控制器解包,拿到CDB,通过SCSI协议转发往ATAPI设备。
405                     5. ATAPI设备回复Return Parameter,SATA通过DMA Setup FIS
406                        发起DMA请求,HBA介入,将Return Parameter写入我们在PRDT
407                        里设置的data_in位置。
408             4. 最后照常等待HBA把结果写入data_in,然后直接解析就好了。
409           参考:
410             * ATA/ATAPI Command Set - 3 (ACS-3), Section 7.18
411             * SATA AHCI HBA Spec, Section 5.3.7
412             * SCSI Command Reference Manual, Section 3.26
413     */
414
415     sata_create_fis(cmd_fis, ATA_PACKET, 512 << 8, 0);
416
417     // for dev use 12 bytes cdb, READ_CAPACITY must use the 10 bytes variation.
418     if (port->device->cbd_size == SCSI_CDB12) {
419         struct scsi_cdb12* cdb12 = (struct scsi_cdb12*)cmd_table->atapi_cmd;
420         // ugly tricks to construct 10 byte cdb from 12 byte cdb
421         scsi_create_packet12(cdb12, SCSI_READ_CAPACITY_10, 0, 512 << 8);
422     } else {
423         struct scsi_cdb16* cdb16 = (struct scsi_cdb16*)cmd_table->atapi_cmd;
424         scsi_create_packet16(cdb16, SCSI_READ_CAPACITY_16, 0, 512);
425         cdb16->misc1 = 0x10; // service action
426     }
427
428     cmd_header->transferred_size = 0;
429     cmd_header->options |= HBA_CMDH_ATAPI;
430
431     if (!ahci_try_send(port, slot)) {
432         goto fail;
433     }
434
435     scsi_parse_capacity(port->device, (uint32_t*)data_in);
436
437 done:
438     // reset interrupt status and unmask D2HR interrupt
439     port->regs[HBA_RPxIE] |= HBA_MY_IE;
440     achi_register_ops(port);
441
442     vfree_dma(data_in);
443     vfree_dma(cmd_table);
444
445     return 1;
446
447 fail:
448     port->regs[HBA_RPxIE] |= HBA_MY_IE;
449     vfree_dma(data_in);
450     vfree_dma(cmd_table);
451
452     return 0;
453 }
454
455 int
456 ahci_identify_device(struct hba_device* device)
457 {
458     // 用于重新识别设备(比如在热插拔的情况下)
459     vfree(device);
460     return ahci_init_device(device->port);
461 }
462
463 void
464 achi_register_ops(struct hba_port* port)
465 {
466     port->device->ops.identify = ahci_identify_device;
467     if (!(port->device->flags & HBA_DEV_FATAPI)) {
468         port->device->ops.submit = sata_submit;
469     } else {
470         port->device->ops.submit = scsi_submit;
471     }
472 }