+++ /dev/null
-/**
- * @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
--- /dev/null
+/**
+ * @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
* 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
#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 */
#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
* @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 */
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*
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");
* 需要注意: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
pci_init();
ahci_init();
pci_print_device();
+ ahci_list_device();
syscall_install();
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;
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);
+ }
}
}
}