feat: spec-compliant AHCI HBA initialization
authorMinep <zelong56@gmail.com>
Wed, 29 Jun 2022 14:07:03 +0000 (15:07 +0100)
committerMinep <zelong56@gmail.com>
Wed, 29 Jun 2022 14:11:55 +0000 (15:11 +0100)
feat: detect device presence on hba port
fix: ioremap will disable caching on all mapped page.
refactor: releasing the reclaimable page when unlocking memory

lunaix-os/hal/ahci.c [deleted file]
lunaix-os/hal/ahci/ahci.c [new file with mode: 0644]
lunaix-os/includes/hal/ahci.h
lunaix-os/includes/hal/pci.h
lunaix-os/includes/lunaix/mm/vmm.h
lunaix-os/kernel/mm/mmio.c
lunaix-os/kernel/mm/vmap.c
lunaix-os/kernel/peripheral/ps2kbd.c
lunaix-os/kernel/proc0.c

diff --git a/lunaix-os/hal/ahci.c b/lunaix-os/hal/ahci.c
deleted file mode 100644 (file)
index fa87fd3..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * @file ahci.c
- * @author Lunaixsky (zelong56@gmail.com)
- * @brief A software implementation of Serial ATA AHCI 1.3.1 Specification
- * @version 0.1
- * @date 2022-06-28
- *
- * @copyright Copyright (c) 2022
- *
- */
-#include <hal/ahci.h>
-#include <hal/pci.h>
-#include <lunaix/mm/mmio.h>
-#include <lunaix/spike.h>
-#include <lunaix/syslog.h>
-
-LOG_MODULE("AHCI")
-
-static struct ahci_hba hba;
-
-void
-ahci_init()
-{
-    struct pci_device* ahci_dev = pci_get_device_by_class(0x10601);
-    assert_msg(ahci_dev, "AHCI: Not found.");
-
-    uintptr_t bar6, size;
-    size = pci_bar_sizing(ahci_dev, &bar6, 6);
-    assert_msg(bar6 && PCI_BAR_MMIO(bar6), "AHCI: BAR#6 is not MMIO.");
-
-    hba.base = (hba_reg_t*)ioremap(PCI_BAR_ADDR_MM(bar6), size);
-
-    // Enable AHCI, Enable interrupt generation.
-    hba.base[HBA_RGHC] |= 0x80000002;
-
-    // As per section 3.1.1, this is 0 based value.
-    hba.ports_num = (hba.base[HBA_RCAP] & 0x1f) + 1;
-    hba.version = hba.base[HBA_RVER];
-
-    kprintf(KINFO "Version: %x; Ports: %d\n", hba.version, hba.ports_num);
-
-    hba_reg_t pmap = hba.base[HBA_RPI];
-    for (size_t i = 0; i < 32; i++, pmap = pmap >> 1) {
-        if (!(pmap & 0x1)) {
-            continue;
-        }
-        hba.ports[i] = (hba_reg_t*)(&hba.base[HBA_RPBASE] + i);
-        kprintf("\t Port#%d, clb: %p, fis: %p\n",
-                i + 1,
-                hba.ports[i][HBA_RPxCLB],
-                hba.ports[i][HBA_RPxFB]);
-
-        // TODO: (?) Rebasing each port's command list and FIS
-    }
-}
\ No newline at end of file
diff --git a/lunaix-os/hal/ahci/ahci.c b/lunaix-os/hal/ahci/ahci.c
new file mode 100644 (file)
index 0000000..c5989fb
--- /dev/null
@@ -0,0 +1,120 @@
+/**
+ * @file ahci.c
+ * @author Lunaixsky (zelong56@gmail.com)
+ * @brief A software implementation of Serial ATA AHCI 1.3.1 Specification
+ * @version 0.1
+ * @date 2022-06-28
+ *
+ * @copyright Copyright (c) 2022
+ *
+ */
+#include <hal/ahci.h>
+#include <hal/pci.h>
+#include <klibc/string.h>
+#include <lunaix/mm/kalloc.h>
+#include <lunaix/mm/mmio.h>
+#include <lunaix/mm/pmm.h>
+#include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+
+#define HBA_FIS_SIZE 256
+#define HBA_CLB_SIZE 1024
+
+LOG_MODULE("AHCI")
+
+static struct ahci_hba hba;
+
+void
+ahci_init()
+{
+    struct pci_device* ahci_dev = pci_get_device_by_class(AHCI_HBA_CLASS);
+    assert_msg(ahci_dev, "AHCI: Not found.");
+
+    uintptr_t bar6, size;
+    size = pci_bar_sizing(ahci_dev, &bar6, 6);
+    assert_msg(bar6 && PCI_BAR_MMIO(bar6), "AHCI: BAR#6 is not MMIO.");
+
+    pci_reg_t cmd = pci_read_cspace(ahci_dev->cspace_base, PCI_REG_STATUS_CMD);
+
+    // 禁用传统中断(因为我们使用MSI),启用MMIO访问,允许PCI设备间访问
+    cmd |= (PCI_RCMD_MM_ACCESS | PCI_RCMD_DISABLE_INTR | PCI_RCMD_BUS_MASTER);
+
+    pci_write_cspace(ahci_dev->cspace_base, PCI_REG_STATUS_CMD, cmd);
+
+    hba.base = (hba_reg_t*)ioremap(PCI_BAR_ADDR_MM(bar6), size);
+
+    // 重置HBA
+    hba.base[HBA_RGHC] |= HBA_RGHC_RESET;
+    wait_until(!(hba.base[HBA_RGHC] & HBA_RGHC_RESET));
+
+    // 启用AHCI工作模式,启用中断
+    hba.base[HBA_RGHC] |= (HBA_RGHC_ACHI_ENABLE | HBA_RGHC_INTR_ENABLE);
+
+    // As per section 3.1.1, this is 0 based value.
+    hba_reg_t cap = hba.base[HBA_RCAP];
+    hba.ports_num = (cap & 0x1f) + 1;  // CAP.PI
+    hba.cmd_slots = (cap >> 8) & 0x1f; // CAP.NCS
+    hba.version = hba.base[HBA_RVER];
+
+    /* ------ HBA端口配置 ------ */
+    hba_reg_t pmap = hba.base[HBA_RPI];
+    uintptr_t clb_pg_addr, fis_pg_addr, clb_pa, fis_pa;
+    for (size_t i = 0, fisp = 0, clbp = 0; i < 32;
+         i++, pmap >>= 1, fisp = (fisp + 1) % 16, clbp = (clbp + 1) % 4) {
+        if (!(pmap & 0x1)) {
+            continue;
+        }
+
+        struct ahci_port* port =
+          (struct ahci_port*)lxmalloc(sizeof(struct ahci_port));
+        hba_reg_t* port_regs =
+          (hba_reg_t*)(&hba.base[HBA_RPBASE + i * HBA_RPSIZE]);
+
+        if (!clbp) {
+            // 每页最多4个命令队列
+            clb_pa = pmm_alloc_page(KERNEL_PID, PP_FGLOCKED);
+            clb_pg_addr = ioremap(clb_pa, 0x1000);
+            memset(clb_pg_addr, 0, 0x1000);
+        }
+        if (!fisp) {
+            // 每页最多16个FIS
+            fis_pa = pmm_alloc_page(KERNEL_PID, PP_FGLOCKED);
+            fis_pg_addr = ioremap(fis_pa, 0x1000);
+            memset(fis_pg_addr, 0, 0x1000);
+        }
+
+        /* 重定向CLB与FIS */
+        port_regs[HBA_RPxCLB] = clb_pa + clbp * HBA_CLB_SIZE;
+        port_regs[HBA_RPxFB] = fis_pa + fisp * HBA_FIS_SIZE;
+
+        *port =
+          (struct ahci_port){ .regs = port_regs,
+                              .ssts = port_regs[HBA_RPxSSTS],
+                              .cmdlstv = clb_pg_addr + clbp * HBA_CLB_SIZE,
+                              .fisv = fis_pg_addr + fisp * HBA_FIS_SIZE };
+
+        /* 初始化端口,并置于就绪状态 */
+        port_regs[HBA_RPxCI] = 0;
+        port_regs[HBA_RPxIE] |= (HBA_PxINTR_DMA | HBA_PxINTR_D2HR);
+        port_regs[HBA_RPxCMD] |= (HBA_PxCMD_FRE | HBA_PxCMD_ST);
+
+        hba.ports[i] = port;
+    }
+}
+
+char sata_ifs[][20] = { "Not detected",
+                        "SATA I (1.5Gbps)",
+                        "SATA II (3.0Gbps)",
+                        "SATA III (6.0Gbps)" };
+
+void
+ahci_list_device()
+{
+    kprintf(KINFO "Version: %x; Ports: %d\n", hba.version, hba.ports_num);
+    for (size_t i = 0; i < 32; i++) {
+        struct ahci_port* port = hba.ports[i];
+        if (!port)
+            continue;
+        kprintf("\t Port %d: %s\n", i, &sata_ifs[HBA_RPxSSTS_IF(port->ssts)]);
+    }
+}
\ No newline at end of file
index 438fa5cf9ef7cf707c0fe2a29e712c96038f147b..dc30ca8b1b2e1fb290492979107c4f431a462129 100644 (file)
@@ -10,6 +10,8 @@
  * All registers offset are 0 based index of a DWORD array
  */
 
+#define AHCI_HBA_CLASS 0x10601
+
 #define HBA_RCAP 0
 #define HBA_RGHC 1
 #define HBA_RIS 2
@@ -17,6 +19,7 @@
 #define HBA_RVER 4
 
 #define HBA_RPBASE (0x40)
+#define HBA_RPSIZE (0x80 >> 2)
 #define HBA_RPxCLB 0
 #define HBA_RPxFB 2
 #define HBA_RPxIS 4
 #define HBA_RPxSNTF 15
 #define HBA_RPxFBS 16
 
+#define HBA_PxCMD_FRE (1 << 4)
+#define HBA_PxCMD_ST (1)
+#define HBA_PxINTR_DMA (1 << 2)
+#define HBA_PxINTR_D2HR (1)
+
+#define HBA_RGHC_ACHI_ENABLE (1 << 31)
+#define HBA_RGHC_INTR_ENABLE (1 << 1)
+#define HBA_RGHC_RESET 1
+
+#define HBA_RPxSSTS_PWR(x) (((x) >> 8) & 0xf)
+#define HBA_RPxSSTS_IF(x) (((x) >> 4) & 0xf)
+#define HBA_RPxSSTS_PHYSTATE(x) ((x)&0xf)
+
 typedef unsigned int hba_reg_t;
 
+struct ahci_port
+{
+    hba_reg_t* regs;
+    unsigned int ssts;
+    void* cmdlstv;
+    void* fisv;
+};
+
 struct ahci_hba
 {
     volatile hba_reg_t* base;
     unsigned int ports_num;
-    unsigned int port_map;
+    unsigned int cmd_slots;
     unsigned int version;
-    hba_reg_t* ports[32];
+    struct ahci_port* ports[32];
 };
 
+/**
+ * @brief 初始化AHCI与HBA
+ *
+ */
 void
 ahci_init();
 
+void
+ahci_list_device();
+
 #endif /* __LUNAIX_AHCI_H */
index 639a12c4de271febd0897b3aa3c7b18896268f58..b31352b3428bec4653cd6e89f0d79a5c07f9d242 100644 (file)
 #define PCI_MSI_ADDR(msi_base) ((msi_base) + 4)
 #define PCI_MSI_DATA(msi_base) ((msi_base) + 8)
 
+#define PCI_RCMD_DISABLE_INTR (1 << 10)
+#define PCI_RCMD_FAST_B2B (1 << 9)
+#define PCI_RCMD_BUS_MASTER (1 << 2)
+#define PCI_RCMD_MM_ACCESS (1 << 1)
+#define PCI_RCMD_IO_ACCESS 1
+
 #define PCI_ADDRESS(bus, dev, funct)                                           \
     (((bus)&0xff) << 16) | (((dev)&0xff) << 11) | (((funct)&0xff) << 8) |      \
       0x80000000
index db6e058fcb9941568feb55e06186d859dab65744..98f57634f1a42963abfc6bc2290d696f195509bd 100644 (file)
@@ -117,6 +117,6 @@ vmm_next_free(uintptr_t start, int options);
  * @return void*
  */
 void*
-vmm_vmap(uintptr_t paddr, size_t size, pt_attr attr, uint32_t pattr);
+vmm_vmap(uintptr_t paddr, size_t size, pt_attr attr);
 
 #endif /* __LUNAIX_VMM_H */
index f6d53686eb5836baae85475fee15c04873c65b0a..5e0d01e28601b321c94d62914e0d26b006715655 100644 (file)
@@ -5,7 +5,7 @@
 void*
 ioremap(uintptr_t paddr, uint32_t size)
 {
-    return vmm_vmap(paddr, size, PG_PREM_RW, PP_FGPERSIST);
+    return vmm_vmap(paddr, size, PG_PREM_RW | PG_DISABLE_CACHE);
 }
 
 void*
index 0c1061009c2e66cb410991972165eaef46433967..5df524d532986fd2111521d0a6c6c378ed1f010c 100644 (file)
@@ -8,7 +8,7 @@
 static uintptr_t start = VMAP_START;
 
 void*
-vmm_vmap(uintptr_t paddr, size_t size, pt_attr attr, pp_attr_t pattr)
+vmm_vmap(uintptr_t paddr, size_t size, pt_attr attr)
 {
     // next fit
     assert_msg((paddr & 0xfff) == 0, "vmap: bad alignment");
index 72f5d1ff414c2b1eec5ef29022c5828a4a5f2d22..6d27b10847c8f28802548aa77a806f3a395a5205 100644 (file)
@@ -124,11 +124,9 @@ ps2_kbd_init()
          *  需要注意:Bochs 和 QEMU 使用的是ACPI v1,而非 v2
          * (virtualbox好像是v4)
          *
-         *  (2022/6/28)
-         *  QEMU似乎在 Q35 + ICH9 支持了 ACPI
-         * v2。但是对于IAPC_BOOT_ARCH的设置还是 停留在ACPI
-         * v1的时代。IAPC_ARCH_8042没有被正确的设置。这是一个今年的bug,好像还未修复
-         *  参考:https://lore.kernel.org/all/20220304115257.1816983-1-ani@anisinha.ca/T/
+         *  (2022/6/29)
+         *  QEMU在7.0.0版本中,修复了FADT::IAPC_BOOT无法正确提供关于i8042的信息的bug
+         *      https://wiki.qemu.org/ChangeLog/7.0#ACPI_.2F_SMBIOS
          *
          *  请看Bochs的bios源码(QEMU的BIOS其实是照抄bochs的,所以也是一个德行。。):
          *      https://bochs.sourceforge.io/cgi-bin/lxr/source/bios/rombios32.c#L1314
index d1956795bc6713d37c7a97bdf49f7a95e8f3db6a..3a6ea62116aea40654d96c93ee97a39ac8c1167f 100644 (file)
@@ -124,6 +124,7 @@ init_platform()
     pci_init();
     ahci_init();
     pci_print_device();
+    ahci_list_device();
 
     syscall_install();
 
@@ -174,6 +175,8 @@ __do_reserved_memory(int unlock)
                     break;
                 }
                 vmm_set_mapping(PD_REFERENCED, _pa, _pa, PG_PREM_R, VMAP_NULL);
+                pmm_mark_page_occupied(
+                  KERNEL_PID, _pa >> PG_SIZE_BITS, PP_FGLOCKED);
             }
             // Save the progress for later unmapping.
             mmaps[i].len_low = j * PG_SIZE;
@@ -181,6 +184,9 @@ __do_reserved_memory(int unlock)
             for (; j < pg_num; j++) {
                 uintptr_t _pa = pa + (j << PG_SIZE_BITS);
                 vmm_del_mapping(PD_REFERENCED, _pa);
+                if (mmap.type == MULTIBOOT_MEMORY_ACPI_RECLAIMABLE) {
+                    pmm_mark_page_free(_pa >> PG_SIZE_BITS);
+                }
             }
         }
     }