feat: probe more device info
authorMinep <zelong56@gmail.com>
Thu, 7 Jul 2022 13:12:53 +0000 (14:12 +0100)
committerMinep <zelong56@gmail.com>
Thu, 7 Jul 2022 13:14:47 +0000 (14:14 +0100)
feat: add port reset support
fix: command is executed as CHS mode instead of LBA
fix: delivery of MSI

lunaix-os/config/make-debug-tool
lunaix-os/hal/ahci/ahci.c
lunaix-os/hal/ahci/ata.c
lunaix-os/hal/ahci/utils.c
lunaix-os/hal/pci.c
lunaix-os/includes/hal/ahci/hba.h
lunaix-os/includes/lunaix/spike.h
lunaix-os/kernel/proc0.c

index 8d4e13dc8f20251c6338ffbac4c683448e6b9bca..2376ede1090d1800590496e495bec09d81e035df 100644 (file)
@@ -7,6 +7,7 @@ QEMU_OPTIONS := -s -S -m 1G \
                                -machine q35 \
                                -no-shutdown \
                                -d cpu_reset \
+                               -d trace:ide_dma_cb \
                                -drive id=disk,file="machine/disk0.vdi",if=none \
                                -device ahci,id=ahci \
                                -device ide-hd,drive=disk,bus=ahci.0 \
index 31e9725a812d7fcc9c2ab6699aa76d7a5a03b1ca..09076646d520d621b526cc6403cd870369510d7c 100644 (file)
@@ -26,6 +26,8 @@
 #define HBA_FIS_SIZE 256
 #define HBA_CLB_SIZE 1024
 
+// #define DO_HBA_FULL_RESET
+
 LOG_MODULE("AHCI")
 
 static struct ahci_hba hba;
@@ -54,6 +56,22 @@ ahci_get_port(unsigned int index)
     return hba.ports[index];
 }
 
+void
+__hba_reset_port(hba_reg_t* port_reg)
+{
+    // 根据:SATA-AHCI spec section 10.4.2 描述的端口重置流程
+    port_reg[HBA_RPxCMD] &= ~HBA_PxCMD_ST;
+    port_reg[HBA_RPxCMD] &= ~HBA_PxCMD_FRE;
+    int cnt = wait_until_expire(!(port_reg[HBA_RPxCMD] & HBA_PxCMD_CR), 500000);
+    if (cnt) {
+        return;
+    }
+    // 如果port未响应,则继续执行重置
+    port_reg[HBA_RPxSCTL] = (port_reg[HBA_RPxSCTL] & ~0xf) | 1;
+    io_delay(100000); //等待至少一毫秒,差不多就行了
+    port_reg[HBA_RPxSCTL] &= ~0xf;
+}
+
 void
 ahci_init()
 {
@@ -78,9 +96,11 @@ ahci_init()
 
     hba.base = (hba_reg_t*)ioremap(PCI_BAR_ADDR_MM(bar6), size);
 
+#ifdef DO_HBA_FULL_RESET
     // 重置HBA
     hba.base[HBA_RGHC] |= HBA_RGHC_RESET;
     wait_until(!(hba.base[HBA_RGHC] & HBA_RGHC_RESET));
+#endif
 
     // 启用AHCI工作模式,启用中断
     hba.base[HBA_RGHC] |= HBA_RGHC_ACHI_ENABLE;
@@ -108,6 +128,10 @@ ahci_init()
         hba_reg_t* port_regs =
           (hba_reg_t*)(&hba.base[HBA_RPBASE + i * HBA_RPSIZE]);
 
+#ifndef DO_HBA_FULL_RESET
+        __hba_reset_port(port_regs);
+#endif
+
         if (!clbp) {
             // 每页最多4个命令队列
             clb_pa = pmm_alloc_page(KERNEL_PID, PP_FGLOCKED);
@@ -136,19 +160,20 @@ ahci_init()
         // 需要通过全部置位去清空这些寄存器(相当的奇怪……)
         port_regs[HBA_RPxSERR] = -1;
 
-        port_regs[HBA_RPxIE] |= (HBA_PxINTR_DMA);
         port_regs[HBA_RPxIE] |= (HBA_PxINTR_D2HR);
 
         hba.ports[i] = port;
 
-        if (HBA_RPxSSTS_IF(port->ssts)) {
-            wait_until(!(port_regs[HBA_RPxCMD] & HBA_PxCMD_CR));
-            port_regs[HBA_RPxCMD] |= HBA_PxCMD_FRE;
-            port_regs[HBA_RPxCMD] |= HBA_PxCMD_ST;
+        if (!HBA_RPxSSTS_IF(port->ssts)) {
+            continue;
+        }
+
+        wait_until(!(port_regs[HBA_RPxCMD] & HBA_PxCMD_CR));
+        port_regs[HBA_RPxCMD] |= HBA_PxCMD_FRE;
+        port_regs[HBA_RPxCMD] |= HBA_PxCMD_ST;
 
-            if (!ahci_init_device(port)) {
-                kprintf(KERROR "fail to init device");
-            }
+        if (!ahci_init_device(port)) {
+            kprintf(KERROR "fail to init device");
         }
     }
 }
@@ -197,6 +222,9 @@ ahci_list_device()
         kprintf("\t\t capacity: %d KiB\n",
                 (dev_info->max_lba * dev_info->block_size) >> 10);
         kprintf("\t\t block size: %dB\n", dev_info->block_size);
+        kprintf("\t\t block/sector: %d\n", dev_info->block_per_sec);
+        kprintf("\t\t alignment: %dB\n", dev_info->alignment_offset);
+        kprintf("\t\t capabilities: %x\n", dev_info->capabilities);
         kprintf("\t\t model: %s\n", &dev_info->model);
         kprintf("\t\t serial: %s\n", &dev_info->serial_num);
     }
index 91863fdfc4ad5ca3d3dd76192f12a7300b16a88a..486d69d7c76ac7a551c125f96c0da4e68b10b845 100644 (file)
@@ -36,10 +36,19 @@ __sata_buffer_io(struct hba_port* port,
         // 如果该设备支持48位LBA寻址
         sata_create_fis(
           fis, write ? ATA_WRITE_DMA_EXT : ATA_READ_DMA_EXT, lba, count);
-        fis->dev = (1 << 6) * (!write);
     } else {
         sata_create_fis(fis, write ? ATA_WRITE_DMA : ATA_READ_DMA, lba, count);
     }
+    /*
+          确保我们使用的是LBA寻址模式
+          注意:在ACS-3中(甚至在ACS-4),只有在(READ/WRITE)_DMA_EXT指令中明确注明了需要将这一位置位
+        而并没有在(READ/WRITE)_DMA注明。
+          但是这在ACS-2中是有的!于是这也就导致了先前的测试中,LBA=0根本无法访问,因为此时
+        的访问模式是在CHS下,也就是说LBA=0 => Sector=0,是非法的。
+          所以,我猜测,这要么是QEMU/VirtualBox根据ACS-2来编写的AHCI模拟,
+        要么是标准出错了(毕竟是working draft)
+    */
+    fis->dev = (1 << 6);
 
     int retries = 0;
 
index 3b1fbcb5f27fb83fa7d5106cedb7d5f9db36c80e..e101efdd42d300c11f504bb5f7beff4f6de70abc 100644 (file)
@@ -9,6 +9,9 @@
 #define IDDEV_OFFSERIALNUM 10
 #define IDDEV_OFFMODELNUM 27
 #define IDDEV_OFFADDSUPPORT 69
+#define IDDEV_OFFALIGN 209
+#define IDDEV_OFFLPP 106
+#define IDDEV_OFFCAPABILITIES 49
 
 void
 ahci_parse_dev_info(struct hba_device* dev_info, uint16_t* data)
@@ -17,6 +20,9 @@ ahci_parse_dev_info(struct hba_device* dev_info, uint16_t* data)
     dev_info->block_size = *((uint32_t*)(data + IDDEV_OFFLSECSIZE));
     dev_info->cbd_size = (*data & 0x3) ? 16 : 12;
     dev_info->wwn = *(uint64_t*)(data + IDDEV_OFFWWN);
+    dev_info->block_per_sec = 1 << (*(data + IDDEV_OFFLPP) & 0xf);
+    dev_info->alignment_offset = *(data + IDDEV_OFFALIGN) & 0x3fff;
+    dev_info->capabilities = *((uint32_t*)(data + IDDEV_OFFCAPABILITIES));
 
     if (!dev_info->block_size) {
         dev_info->block_size = 512;
index c3771d571e2a10885521d5ecb9922acda7b3efa4..4bf4c56863dac16b6fa4b16e483ea97557199376 100644 (file)
@@ -174,7 +174,7 @@ void
 pci_setup_msi(struct pci_device* device, int vector)
 {
     // Dest: APIC#0, Physical Destination, No redirection
-    uint32_t msi_addr = (__APIC_BASE_PADDR | 0x8);
+    uint32_t msi_addr = (__APIC_BASE_PADDR);
 
     // Edge trigger, Fixed delivery
     uint32_t msi_data = vector;
index 19f76cd08603f8e57272d57685a7e01a586c0c43..9dc7071aa2d71e5320f960d615cf05fc0ddf4553 100644 (file)
@@ -101,6 +101,9 @@ struct hba_device
     uint8_t cbd_size;
     uint8_t last_error;
     uint8_t last_status;
+    uint32_t alignment_offset;
+    uint32_t block_per_sec;
+    uint32_t capabilities;
 
     struct
     {
index 6f91924dc19a174d280cb3bb7c98905f8cd91837..777a457ae1a0c26a4b7952d7f1c12914841da88c 100644 (file)
@@ -57,7 +57,7 @@ panick(const char* msg);
 #define wait_until_expire(cond, max)                                           \
     ({                                                                         \
         unsigned int __wcounter__ = (max);                                     \
-        while (!(cond) && __wcounter__-- > 0)                                  \
+        while (!(cond) && __wcounter__-- > 1)                                  \
             ;                                                                  \
         __wcounter__;                                                          \
     })
index b9a2470a765b1b86cfc42fa939d190a3d5dd6fd9..be7d46885530f7b2afd4f5bdaf55b06d4b2b3210 100644 (file)
@@ -119,14 +119,14 @@ void
 __test_disk_io()
 {
     struct hba_port* port = ahci_get_port(0);
-    char* buffer = valloc_dma(port->device->block_size);
+    char* buffer = vcalloc_dma(port->device->block_size);
     strcpy(buffer, test_sequence);
     kprintf("WRITE: %s\n", buffer);
     int result;
 
-    // 写入第一扇区
+    // 写入第一扇区 (LBA=0)
     result =
-      port->device->ops.write_buffer(port, 1, buffer, port->device->block_size);
+      port->device->ops.write_buffer(port, 0, buffer, port->device->block_size);
     if (!result) {
         kprintf(KWARN "fail to write: %x\n", port->device->last_error);
     }
@@ -135,7 +135,7 @@ __test_disk_io()
 
     // 读出我们刚刚写的内容!
     result =
-      port->device->ops.read_buffer(port, 1, buffer, port->device->block_size);
+      port->device->ops.read_buffer(port, 0, buffer, port->device->block_size);
     kprintf(KDEBUG "%x, %x\n", port->regs[HBA_RPxIS], port->regs[HBA_RPxTFD]);
     if (!result) {
         kprintf(KWARN "fail to read: %x\n", port->device->last_error);