/* DKM NVMe Driver — 高位 BAR MMIO 映射 + admin/IO queue + block provider * * Stage 1 optional storage driver, depends on "pci" and "irq", provides "block". * * 实现要点: * 1. 64-bit BAR 常位于 4G 以上(QEMU/OVMF 把 NVMe BAR0 放到 ~48GiB), * HHDM 直映射不保证覆盖。访问前先用 kernel_api.mmio->is_mapped 做 * 四级页表 walk 核实;未映射则 map_mmio 原地补建 4KiB UC 页。 * 2. 同步轮询模型: 单 outstanding 命令 + CQE phase bit 轮询 + TSC 超时, * 不依赖 IRQ/MSI(当前 PIC/APIC 后端未完工)。 * 3. 数据面: 32 页 (128KiB) 物理连续 DMA bounce buffer + 单页 PRP list。 * PRP 布局按传输页数分档: 1 页单 PRP1; 2 页 PRP2 直连第二页; * >=3 页 PRP2 指向 PRP list 页 (512 项 u64, 上限 513 页 = 2052KiB)。 * chunk 上限 = min(buffer 页数, 2^MDTS 页, NLB 16-bit)。 * 读写一律 bounce: 调用方缓冲是任意虚拟地址 (DSK BSS / vmm dbuf), * 物理底账未知, 统一经 DMA 缓冲中转, 一次命令读满 chunk 再 memcpy。 * WRITE 路径 memcpy 后 wbinvd (沿用 ahci BUG-P0-2 防陈旧 cache 先例), * 每次写调用末尾随一个 FLUSH。admin/IO queue 各 64 项, 均每队列一页。 */ #include "../dkm_shared.h" #include "../dkm_instr.h" DKM_STAT_DECL(nvm0_pci_found); static const char *const g_depends[] = { "pci", "irq" }; static const char *const g_provides[] = { "block" }; __attribute__((visibility("default"), used)) const struct dkm_driver_desc driver_desc = { .magic = DKM_DRIVER_MAGIC, .abi_version = DKM_ABI_VERSION, .desc_size = sizeof(struct dkm_driver_desc), .name = "nvme", .version = "0.3.0", .vendor = "Deshab", .driver_class = 6, /* DKM_CLASS_STORAGE */ .stage = 1, .flags = 0, /* optional */ .priority = 0, .depends = g_depends, .depends_count = 2, .provides = g_provides, .provides_count = 1, .min_kernel_abi = 1, .feature_bits = 0, .reserved0 = 0, .reserved1 = 0 }; /* ---- NVMe 控制器寄存器 ---- */ #define NVME_CAP 0x00 /* u64 capabilities */ #define NVME_VS 0x08 /* u32 version */ #define NVME_INTMS 0x0C /* interrupt mask set */ #define NVME_INTMC 0x10 /* interrupt mask clear */ #define NVME_CC 0x14 /* controller configuration */ #define NVME_CSTS 0x1C /* controller status */ #define NVME_AQA 0x24 /* admin queue attributes */ #define NVME_ASQ 0x28 /* u64 admin SQ base */ #define NVME_ACQ 0x30 /* u64 admin CQ base */ #define NVME_DB_BASE 0x1000 /* doorbell 起始偏移 */ #define NVME_CLASS_STORAGE 0x01 #define NVME_SUBCLASS_NVM 0x08 #define NVME_PROGIF_NVME 0x02 /* admin 命令 opcode */ #define NVME_ADM_DELETE_IOSQ 0x00 #define NVME_ADM_CREATE_IOSQ 0x01 #define NVME_ADM_DELETE_IOCQ 0x04 #define NVME_ADM_CREATE_IOCQ 0x05 #define NVME_ADM_IDENTIFY 0x06 /* IO 命令 opcode(NVM 命令集: 00h=Flush, 01h=Write, 02h=Read) */ #define NVME_IO_FLUSH 0x00 #define NVME_IO_READ 0x02 #define NVME_IO_WRITE 0x01 #define ADMIN_QSIZE 64 /* entry 数(SQ 64*64B=4K 一页, CQ 64*16B=1K) */ #define IO_QSIZE 64 #define NVME_IDENT_BYTES 4096 /* identify 数据页 */ #define NVME_DATA_PAGES 32u /* IO bounce: 128KiB = 512B×256 扇区, * 正好覆盖 DSK FAT32 的 256 扇区批量读 */ #define NVME_PRP_LIST_ENTRIES 512u /* 单 list 页 4KiB / 8B = 512 项 */ static const struct dkm_log_api *g_log; static volatile u8 *g_regs; /* MMIO 基址(虚拟) */ static u32 g_db_stride; /* doorbell 步长 = 4 << CAP.DSTRD */ static u64 g_timeout_ms; /* CAP.TO * 500ms */ static struct dkm_dma_buffer g_asq; /* admin SQ */ static struct dkm_dma_buffer g_acq; /* admin CQ */ static struct dkm_dma_buffer g_ident; /* identify 数据页 */ static struct dkm_dma_buffer g_data; /* IO 读写 bounce (NVME_DATA_PAGES 页连续) */ static struct dkm_dma_buffer g_prp; /* PRP list 页 */ static struct dkm_dma_buffer g_iosq; /* IO SQ (qid=1) */ static struct dkm_dma_buffer g_iocq; /* IO CQ (qid=1) */ static u32 g_asq_tail; static u32 g_acq_head; static u32 g_acq_phase; static u32 g_iosq_tail; static u32 g_iocq_head; static u32 g_iocq_phase; static u16 g_cid; static u64 g_sector_size; static u64 g_sector_count; static u32 g_mdts; /* Identify Controller byte 77 */ static u32 g_chunk_max_sectors; /* 单命令扇区上限 (buffer/MDTS/NLB 取小) */ static int g_io_ready; static int g_test_disk; /* LBA0 带 DESHABNVME0 签名的测试盘 */ /* ---- 基础工具 ---- */ static void log_hex(const char *prefix, u64 value) { static const char hex[] = "0123456789abcdef"; char buf[19]; u32 pos = 0; buf[pos++] = '0'; buf[pos++] = 'x'; for (int i = 15; i >= 0; i--) { buf[pos++] = hex[(value >> (i * 4)) & 0xf]; } buf[pos] = 0; g_log->info(prefix); g_log->info(buf); } /* 打印有界 ASCII 串(identify 数据中的 SN/MN 等定长字段) */ static void log_str(const char *prefix, const u8 *src, u32 len) { char buf[48]; if (len > 40) len = 40; for (u32 i = 0; i < len; i++) { char c = (char)src[i]; buf[i] = (c >= 32 && c < 127) ? c : ' '; } buf[len] = 0; g_log->info(prefix); g_log->info(buf); } static void nvme_zero(void *ptr, u32 len) { u8 *p = (u8 *)ptr; for (u32 i = 0; i < len; i++) p[i] = 0; } static __inline__ void nvme_mb(void) { /* x86 TSO 保证 store-store 顺序, 这里只需阻断编译器重排 */ __asm__ volatile("" ::: "memory"); } static u32 mmio_read32(u32 off) { return *(volatile u32 *)(void *)(g_regs + off); } static void mmio_write32(u32 off, u32 value) { *(volatile u32 *)(void *)(g_regs + off) = value; } static u64 mmio_read64(u32 off) { u32 lo = mmio_read32(off); u32 hi = mmio_read32(off + 4); return ((u64)hi << 32) | lo; } static void mmio_write64(u32 off, u64 value) { mmio_write32(off, (u32)(value & 0xffffffffu)); mmio_write32(off + 4, (u32)(value >> 32)); } static u32 sq_tail_db(u16 qid) { return NVME_DB_BASE + (2u * qid) * g_db_stride; } static u32 cq_head_db(u16 qid) { return NVME_DB_BASE + (2u * qid + 1u) * g_db_stride; } /* ---- PCI ---- */ static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { for (u8 bus = 0; bus < 16; bus++) { for (u8 dev = 0; dev < 32; dev++) { u32 vd = dkm_pci_read(bus, dev, 0, PCI_VENDOR_ID); if ((vd & 0xffff) == 0xffff) continue; u8 header = (u8)(dkm_pci_read(bus, dev, 0, PCI_HEADER) >> 16); u8 func_count = (header & 0x80) ? 8 : 1; for (u8 func = 0; func < func_count; func++) { u32 vd2 = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID); if ((vd2 & 0xffff) == 0xffff) continue; u32 class_reg = dkm_pci_read(bus, dev, func, 0x08); u8 prog_if = (u8)((class_reg >> 8) & 0xff); u8 subclass = (u8)((class_reg >> 16) & 0xff); u8 class_code = (u8)((class_reg >> 24) & 0xff); if (class_code == NVME_CLASS_STORAGE && subclass == NVME_SUBCLASS_NVM && prog_if == NVME_PROGIF_NVME) { *out_bus = bus; *out_dev = dev; *out_func = func; return 0; } } } } return -1; } /* ---- CSTS 等待 ---- */ static int nvme_wait_csts(u32 mask, u32 expect, u64 timeout_ms) { u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * timeout_ms; for (;;) { u32 csts = mmio_read32(NVME_CSTS); if (csts & (1u << 1)) { /* CFS: controller fatal status */ g_log->error("[nvme] CSTS.CFS set"); log_hex("[nvme] CSTS=", csts); return -2; } if ((csts & mask) == expect) return 0; if (dkm_rdtsc() > deadline) { g_log->error("[nvme] CSTS wait timeout"); log_hex("[nvme] CSTS=", csts); log_hex("[nvme] wait mask=", mask); return -1; } __asm__ volatile("pause"); } } /* 前向声明: nvme_dump_regs 在 nvme_submit 中使用 */ static void nvme_dump_regs(const char *ctx); /* ---- 命令提交(同步轮询, 单 outstanding) ---- * dw[16] 为完整 SQE(dw0 低 16 位不含 CID, 由本函数填入)。 * 返回 0 成功;>0 = NVMe status code;<0 超时/内部错误。 */ static int nvme_submit(volatile u32 *sq, u32 *sq_tail, u16 sq_qid, volatile u32 *cq, u32 *cq_head, u32 *cq_phase, u16 cq_qid, u32 qsize, u32 *dw, u32 *out_cqe_dw0) { dw[0] = (dw[0] & 0xffffu) | ((u32)g_cid << 16); u32 cid = g_cid; g_cid++; /* 逐 dword 写入 SQE(64B),避免 freestanding 下编译器生成 memcpy 调用 */ u32 pos = *sq_tail; for (u32 i = 0; i < 16; i++) sq[pos * 16 + i] = dw[i]; *sq_tail = (pos + 1) % qsize; nvme_mb(); /* SQ 内容先于 doorbell 可见 */ mmio_write32(sq_tail_db(sq_qid), *sq_tail); /* 真机超时: 使用 CAP.TO * 500ms * 10 (留 10 倍余量), 下限 5s。 * 真机 NVMe 控制器在重负载下可能需要更长的完成时间。 */ u64 submit_timeout_ms = g_timeout_ms * 10; if (submit_timeout_ms < 5000) submit_timeout_ms = 5000; u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * submit_timeout_ms; for (;;) { u32 dw3 = cq[*cq_head * 4 + 3]; if (((dw3 >> 16) & 1u) == *cq_phase) { u32 sc = (dw3 >> 17) & 0xffu; u32 sct = (dw3 >> 25) & 0x7u; if (out_cqe_dw0) *out_cqe_dw0 = cq[*cq_head * 4 + 0]; *cq_head = (*cq_head + 1) % qsize; if (*cq_head == 0) *cq_phase ^= 1u; nvme_mb(); mmio_write32(cq_head_db(cq_qid), *cq_head); if (sc || sct) { g_log->error("[nvme] command failed"); log_hex("[nvme] opcode=", dw[0] & 0xffu); log_hex("[nvme] cid=", cid); log_hex("[nvme] SC=", sc); log_hex("[nvme] SCT=", sct); /* SC 与 SCT 必须联合编码: SCT!=0 且 SC==0 时只返回 sc * 会返回 0 被调用方误判为成功(CQID_INVALID 正是此类)。 */ return (int)(sc | (sct << 8)); } return 0; } if (dkm_rdtsc() > deadline) { g_log->error("[nvme] completion timeout"); log_hex("[nvme] opcode=", dw[0] & 0xffu); nvme_dump_regs("completion timeout"); return -100; } __asm__ volatile("pause"); } } static int nvme_admin_submit(u32 *dw, u32 *out_cqe_dw0) { return nvme_submit((volatile u32 *)g_asq.virt, &g_asq_tail, 0, (volatile u32 *)g_acq.virt, &g_acq_head, &g_acq_phase, 0, ADMIN_QSIZE, dw, out_cqe_dw0); } static int nvme_io_submit(u32 *dw, u32 *out_cqe_dw0) { return nvme_submit((volatile u32 *)g_iosq.virt, &g_iosq_tail, 1, (volatile u32 *)g_iocq.virt, &g_iocq_head, &g_iocq_phase, 1, IO_QSIZE, dw, out_cqe_dw0); } /* ---- Identify ---- */ static int nvme_identify_controller(u32 *out_nn) { u32 dw[16]; nvme_zero(dw, sizeof(dw)); nvme_zero(g_ident.virt, NVME_IDENT_BYTES); dw[0] = NVME_ADM_IDENTIFY; dw[1] = 0; /* NSID=0 for controller */ dw[6] = (u32)(g_ident.phys & 0xffffffffu); dw[7] = (u32)(g_ident.phys >> 32); dw[10] = 1; /* CNS=1: identify controller */ int rc = nvme_admin_submit(dw, 0); if (rc != 0) return rc; u8 *id = (u8 *)g_ident.virt; log_str("[nvme] ctrl SN=", id + 4, 20); log_str("[nvme] ctrl MN=", id + 24, 40); log_str("[nvme] ctrl FR=", id + 64, 8); /* Identify Controller 布局锚点核对(NVMe 1.4, CNS=1 数据结构): * byte 77 MDTS u8 最大传输 = 2^MDTS × CAP.MPSMIN 页, 0=无限制 * byte 512 SQES/CQES/MAXCMD —— QEMU 实测 0x00004466, 证明偏移基正确 * byte 516 NN u32 Number of Namespaces * NN 核实结论 (QEMU 11.0 串口实证): NN=0x100 不是解析错位。NN 的 spec * 语义是"控制器支持的最大合法 NSID 数"而非已挂载数量; QEMU hw/nvme * 填的是 NVME_MAX_NAMESPACES=256 (0x100)。实际挂载的 NSID=1 由后续 * Identify Namespace(CNS=0) 成功返回确认。解析偏移 516 与 spec 一致, * 无需修正。 */ u32 anchor512 = (u32)id[512] | ((u32)id[513] << 8) | ((u32)id[514] << 16) | ((u32)id[515] << 24); log_hex("[nvme] ID dword@512(SQES/CQES/MAXCMD)=", anchor512); u32 nn = (u32)id[516] | ((u32)id[517] << 8) | ((u32)id[518] << 16) | ((u32)id[519] << 24); log_hex("[nvme] NN(max valid NSID)=", nn); log_hex("[nvme] MDTS=", id[77]); g_mdts = id[77]; *out_nn = nn; return 0; } static int nvme_identify_namespace(u32 nsid, u64 *out_nsze, u32 *out_lbads) { u32 dw[16]; nvme_zero(dw, sizeof(dw)); nvme_zero(g_ident.virt, NVME_IDENT_BYTES); dw[0] = NVME_ADM_IDENTIFY; dw[1] = nsid; dw[6] = (u32)(g_ident.phys & 0xffffffffu); dw[7] = (u32)(g_ident.phys >> 32); dw[10] = 0; /* CNS=0: identify namespace */ int rc = nvme_admin_submit(dw, 0); if (rc != 0) return rc; u8 *id = (u8 *)g_ident.virt; u64 nsze = 0; for (u32 i = 0; i < 8; i++) nsze |= ((u64)id[i]) << (i * 8); u8 flbas = id[26] & 0x0f; u32 lbaf_off = 128 + (u32)flbas * 4; u32 lbaf = (u32)id[lbaf_off] | ((u32)id[lbaf_off + 1] << 8) | ((u32)id[lbaf_off + 2] << 16) | ((u32)id[lbaf_off + 3] << 24); u32 lbads = (lbaf >> 16) & 0xff; log_hex("[nvme] NSZE=", nsze); log_hex("[nvme] FLBAS=", flbas); log_hex("[nvme] LBADS=", lbads); *out_nsze = nsze; *out_lbads = lbads; return 0; } /* ---- IO queue 创建 ---- */ static int nvme_create_io_queues(void) { u32 dw[16]; /* Create IO CQ (qid=1): PC=1, IEN=0 (轮询模式) * CDW10 = QSIZE[31:16](0's based) | QID[15:0] —— QID 必须在低半字 */ nvme_zero(dw, sizeof(dw)); dw[0] = NVME_ADM_CREATE_IOCQ; dw[6] = (u32)(g_iocq.phys & 0xffffffffu); dw[7] = (u32)(g_iocq.phys >> 32); dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u; dw[11] = 1; int rc = nvme_admin_submit(dw, 0); if (rc != 0) { g_log->error("[nvme] create IO CQ failed"); return rc; } /* Create IO SQ (qid=1, cqid=1): PC=1 * CDW10 同 Create IOCQ: QSIZE[31:16] | QID[15:0]; CDW11 = CQID[31:16] | PC */ nvme_zero(dw, sizeof(dw)); dw[0] = NVME_ADM_CREATE_IOSQ; dw[6] = (u32)(g_iosq.phys & 0xffffffffu); dw[7] = (u32)(g_iosq.phys >> 32); dw[10] = ((u32)(IO_QSIZE - 1u) << 16) | 1u; dw[11] = (1u << 16) | 1; rc = nvme_admin_submit(dw, 0); if (rc != 0) { g_log->error("[nvme] create IO SQ failed"); return rc; } g_iosq_tail = 0; g_iocq_head = 0; g_iocq_phase = 1; return 0; } /* ---- IO Read/Write(PRP1 + PRP2 直连 / PRP list 分档) ---- * buf_phys 必须 4KiB 对齐且物理连续(本驱动只传 g_data.phys)。 * 传输页数由 nlb × sector_size 推出,决定 PRP 布局: * 1 页 : PRP2 = 0(单 PRP1,等价旧 bounce 路径) * 2 页 : PRP2 = 第二页物理地址(spec 规定的两页直连形式) * >=3 页: PRP2 = g_prp list 页物理地址, list[i] = 第 i+2 页物理地址 * 单 list 页 512 项, 上限 513 页 (2052KiB), 远大于 chunk 上限 32 页, * 无需 list 链页。PRP list 由 CPU 写、控制器 DMA 读: x86 TSO + 编译屏障 * 保证 list 先于 doorbell 可见(与 SQE 提交路径同一约定)。 */ static int nvme_io_rw(u8 opcode, u64 slba, u32 nlb, u64 buf_phys) { u32 bytes = nlb * (u32)g_sector_size; u32 pages = (bytes + 4095u) >> 12; if (pages == 0 || (pages - 1u) > NVME_PRP_LIST_ENTRIES) return -3; u32 dw[16]; nvme_zero(dw, sizeof(dw)); dw[0] = opcode; dw[1] = 1; /* nsid=1 */ dw[6] = (u32)(buf_phys & 0xffffffffu); dw[7] = (u32)(buf_phys >> 32); if (pages == 2) { u64 p2 = buf_phys + 4096u; dw[8] = (u32)(p2 & 0xffffffffu); dw[9] = (u32)(p2 >> 32); } else if (pages >= 3) { volatile u64 *list = (volatile u64 *)g_prp.virt; for (u32 i = 0; i < pages - 1u; i++) list[i] = buf_phys + 4096ULL * (u64)(i + 1u); nvme_mb(); /* list 内容先于 doorbell 可见 */ dw[8] = (u32)(g_prp.phys & 0xffffffffu); dw[9] = (u32)(g_prp.phys >> 32); } dw[10] = (u32)(slba & 0xffffffffu); dw[11] = (u32)(slba >> 32); dw[12] = nlb - 1u; /* NLB 0-based */ return nvme_io_submit(dw, 0); } /* NVM Flush (opcode 0x00): 无数据字段, 把 volatile write cache 刷到介质 */ static int nvme_io_flush(void) { u32 dw[16]; nvme_zero(dw, sizeof(dw)); dw[0] = NVME_IO_FLUSH; dw[1] = 1; /* nsid=1 */ return nvme_io_submit(dw, 0); } /* ---- block provider ---- * 调用方缓冲为任意虚拟地址(DSK 静态 BSS、vmm virtio_blk dbuf 等), * 物理底账未知且不一定连续/对齐,不能直接进 PRP。一律 bounce 经 * g_data(物理连续 DMA 区): 一次命令读/写满 chunk,再 memcpy 中转。 */ static int nvme_block_read(void *ctx, u64 lba, u32 count, void *buffer) { (void)ctx; if (!g_io_ready || !buffer || count == 0) return -1; u8 *out = (u8 *)buffer; while (count > 0) { u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count; int rc = nvme_io_rw(NVME_IO_READ, lba, n, g_data.phys); if (rc != 0) return rc; u32 bytes = n * (u32)g_sector_size; u8 *src = (u8 *)g_data.virt; for (u32 i = 0; i < bytes; i++) out[i] = src[i]; out += bytes; lba += n; count -= n; } return 0; } static int nvme_block_write(void *ctx, u64 lba, u32 count, const void *buffer) { (void)ctx; if (!g_io_ready || !buffer || count == 0) return -1; const u8 *in = (const u8 *)buffer; while (count > 0) { u32 n = count > g_chunk_max_sectors ? g_chunk_max_sectors : count; u32 bytes = n * (u32)g_sector_size; u8 *dst = (u8 *)g_data.virt; for (u32 i = 0; i < bytes; i++) dst[i] = in[i]; /* 沿用 ahci BUG-P0-2 先例: 写前 flush CPU cache, 防 DMA 读到陈旧数据 */ __asm__ volatile("wbinvd" ::: "memory"); int rc = nvme_io_rw(NVME_IO_WRITE, lba, n, g_data.phys); if (rc != 0) return rc; in += bytes; lba += n; count -= n; } /* 每次写调用末尾 FLUSH 一次,保证 volatile write cache 落盘 */ return nvme_io_flush(); } /* ---- 数据面自测(仅签名测试盘运行写测试,防实机盘数据损坏) ---- */ /* 读回 LBA0 并核对 DESHABNVME0 签名(写测试后复查无串扰用) */ static int nvme_lba0_sig_ok(void) { static const char sig[] = "DESHABNVME0"; int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys); if (rc != 0) return 0; u8 *d = (u8 *)g_data.virt; for (u32 i = 0; i < 11; i++) { if (d[i] != (u8)sig[i]) return 0; } return 1; } /* 多页单命令读: LBA0 起 32 扇区 = 16KiB = 4 页 → PRP list 路径。 * 测试盘内容已知: LBA0 为 "DESHABNVME0" 重复填满 512B, LBA1..31 全零。 */ static void nvme_selftest_read_multipage(void) { static u8 rbuf[16384]; static const char sig[] = "DESHABNVME0"; int rc = nvme_block_read(0, 0, 32, rbuf); log_hex("[nvme] PRPLIST read 32sec rc=", (u64)(i64)rc); if (rc != 0) return; u32 bad = 0; for (u32 i = 0; i < 512; i++) { if (rbuf[i] != (u8)sig[i % 11]) { bad = 1; log_hex("[nvme] sig mismatch idx=", i); break; } } if (!bad) { for (u32 i = 512; i < 16384; i++) { if (rbuf[i] != 0) { bad = 1; log_hex("[nvme] nonzero idx=", i); break; } } } if (bad) { g_log->error("[nvme] PRPLIST read verify FAILED"); } else { g_log->info("[nvme] PRPLIST read verify OK (LBA0 sig + LBA1-31 zero)"); } } /* 写 → FLUSH → 读回逐字节校验。sectors=16 走 PRP2 直连(2页), * sectors=32 走 PRP list(4页)。pattern 为首 8 字节 "NVMEWRIT" + 全缓冲 * 确定式伪随机,重跑幂等(与上次写入内容一致)。 */ static void nvme_selftest_rw(u64 lba, u32 sectors) { static u8 wpat[16384]; static u8 rbuf[16384]; u32 bytes = sectors * (u32)g_sector_size; if (bytes > sizeof(wpat)) return; for (u32 i = 0; i < bytes; i++) wpat[i] = (u8)(i * 31u + 17u); wpat[0] = 'N'; wpat[1] = 'V'; wpat[2] = 'M'; wpat[3] = 'E'; wpat[4] = 'W'; wpat[5] = 'R'; wpat[6] = 'I'; wpat[7] = 'T'; log_hex("[nvme] selftest rw lba=", lba); log_hex("[nvme] selftest rw sectors=", sectors); int rc = nvme_block_write(0, lba, sectors, wpat); log_hex("[nvme] write+flush rc=", (u64)(i64)rc); if (rc != 0) return; for (u32 i = 0; i < bytes; i++) rbuf[i] = 0; rc = nvme_block_read(0, lba, sectors, rbuf); log_hex("[nvme] readback rc=", (u64)(i64)rc); if (rc != 0) return; for (u32 i = 0; i < bytes; i++) { if (rbuf[i] != wpat[i]) { g_log->error("[nvme] readback MISMATCH"); log_hex("[nvme] first bad idx=", i); log_hex("[nvme] expect=", wpat[i]); log_hex("[nvme] actual=", rbuf[i]); return; } } g_log->info("[nvme] write+flush+readback verify OK"); } /* ---- 真机诊断: dump 关键寄存器 ---- */ static void nvme_dump_regs(const char *ctx) { g_log->info("[nvme] --- register dump: "); g_log->info(ctx); log_hex("[nvme] CAP =", mmio_read64(NVME_CAP)); log_hex("[nvme] VS =", mmio_read32(NVME_VS)); log_hex("[nvme] CC =", mmio_read32(NVME_CC)); log_hex("[nvme] CSTS =", mmio_read32(NVME_CSTS)); log_hex("[nvme] AQA =", mmio_read32(NVME_AQA)); log_hex("[nvme] ASQ =", mmio_read64(NVME_ASQ)); log_hex("[nvme] ACQ =", mmio_read64(NVME_ACQ)); /* doorbell 区域首个 entry 诊断 */ log_hex("[nvme] SQ0DB=", mmio_read32(NVME_DB_BASE)); log_hex("[nvme] CQ0DB=", mmio_read32(NVME_DB_BASE + g_db_stride)); } /* ---- 高位 BAR MMIO 接入 ---- */ /* 真机 NVMe 控制器寄存器 + doorbell 空间可能远大于 8K。 * 典型实机: CAP.MQES 决定队列深度,doorbell 偏移 = DB_BASE + 2*QID*DSTRD*4, * 需要映射足够大的范围覆盖至少 admin + 1 个 IO queue 对。 * 安全映射大小: 至少 64K(覆盖 QID 0-7 的 SQ/CQ doorbell)。 */ #define NVME_MAP_SIZE 0x10000 static volatile u8 *nvme_map_bar(const struct dkm_kernel_api *api, u64 bar_phys) { u64 bar_virt = api->hhdm_offset + bar_phys; /* 优先使用 mm_map_mmio(独立窗口映射, PCD|PWT, 不与 HHDM 空洞竞争) */ if (api->mm_map_mmio) { void *mapped = api->mm_map_mmio(bar_phys, NVME_MAP_SIZE); if (mapped) { g_log->info("[nvme] BAR mapped via mm_map_mmio window"); return (volatile u8 *)mapped; } g_log->warn("[nvme] mm_map_mmio failed; trying HHDM fallback"); } if (!api->mmio || !api->mmio->is_mapped) { /* 内核无页表服务: 仅当 BAR 位于低 4G(HHDM 经验上覆盖)时冒险直用 */ if (bar_phys < 0x100000000ULL) { g_log->warn("[nvme] no mmio api; assume HHDM covers low BAR"); return (volatile u8 *)(uintptr_t)bar_virt; } g_log->warn("[nvme] no mmio api and BAR above 4G; cannot access"); return (volatile u8 *)0; } log_hex("[nvme] CR3=", api->mmio->cr3 ? api->mmio->cr3() : 0); /* 核实: HHDM 是否直接覆盖该 BAR(四级页表只读 walk, 无 #PF 风险) */ int level = api->mmio->is_mapped(bar_virt); log_hex("[nvme] HHDM is_mapped(BAR)=", (u64)(i64)level); if (level == 0) { g_log->info("[nvme] HHDM does NOT cover BAR; mapping via page tables"); if (!api->mmio->map_mmio) return (volatile u8 *)0; int rc = api->mmio->map_mmio(bar_phys, NVME_MAP_SIZE); log_hex("[nvme] map_mmio rc=", (u64)(i64)rc); if (rc != 0) return (volatile u8 *)0; level = api->mmio->is_mapped(bar_virt); log_hex("[nvme] post-map is_mapped=", (u64)(i64)level); if (level == 0) { g_log->error("[nvme] BAR still unmapped after map_mmio"); return (volatile u8 *)0; } } else { g_log->info("[nvme] HHDM already covers BAR; direct access"); } return (volatile u8 *)(uintptr_t)bar_virt; } /* ---- driver entry ---- */ __attribute__((visibility("default"))) int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { (void)handle; dkm_tsc_calibrate(); if (!api || !api->log) return -1; g_log = api->log; dkm_instr_init(api); g_log->info("[nvme] init begin"); u8 bus = 0, dev = 0, func = 0; if (nvme_find(&bus, &dev, &func) != 0) { g_log->warn("[nvme] NVMe controller not found"); g_log->info("[nvme] driver ready"); return 0; } g_log->info("[nvme] NVMe controller found"); DKM_STAT_INC(nvm0_pci_found); log_hex("[nvme] bus=", bus); log_hex("[nvme] dev=", dev); log_hex("[nvme] func=", func); u32 vd = dkm_pci_read(bus, dev, func, PCI_VENDOR_ID); log_hex("[nvme] vendor=", vd & 0xffff); log_hex("[nvme] device=", vd >> 16); /* 启用 MEM + BUSM, 并禁用 INTx(轮询模式不需要中断) */ u32 command = dkm_pci_read(bus, dev, func, PCI_COMMAND) & 0xffff; command |= PCI_CMD_MEM | PCI_CMD_BUSM | (1u << 10); dkm_pci_write(bus, dev, func, PCI_COMMAND, command); log_hex("[nvme] PCI command=", command); u32 bar0_lo = dkm_pci_read(bus, dev, func, PCI_BAR0); u32 bar1_hi = dkm_pci_read(bus, dev, func, PCI_BAR1); u64 bar_phys; if (bar0_lo & 1u) { g_log->warn("[nvme] BAR0 is IO space; unsupported"); g_log->info("[nvme] driver ready"); return 0; } if ((bar0_lo & 0x6u) == 0x4u) { bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u); } else { bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u); } u32 irq_line = dkm_pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff; log_hex("[nvme] BAR0 phys=", bar_phys); log_hex("[nvme] PCI IRQ line=", irq_line); log_hex("[nvme] HHDM offset=", api->hhdm_offset); if (!api->hhdm_offset || !bar_phys) { g_log->warn("[nvme] missing HHDM or BAR0; skip"); g_log->info("[nvme] driver ready"); return 0; } g_regs = nvme_map_bar(api, bar_phys); if (!g_regs) { g_log->error("[nvme] BAR MMIO unavailable; driver inactive"); g_log->info("[nvme] driver ready"); return 0; } /* ---- 寄存器枚举(验证 MMIO 可读) ---- */ u64 cap = mmio_read64(NVME_CAP); u32 vs = mmio_read32(NVME_VS); u32 cc = mmio_read32(NVME_CC); u32 csts = mmio_read32(NVME_CSTS); log_hex("[nvme] CAP=", cap); log_hex("[nvme] VS=", vs); log_hex("[nvme] CC=", cc); log_hex("[nvme] CSTS=", csts); log_hex("[nvme] CAP.MQES=", (cap & 0xffff) + 1); log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf); log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff); log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff); log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf); log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf); if (cap == 0 || cap == 0xFFFFFFFFFFFFFFFFULL) { g_log->error("[nvme] CAP invalid; MMIO read broken"); g_log->info("[nvme] driver ready"); return 0; } if (!((cap >> 37) & 1u)) { g_log->error("[nvme] NVM command set not supported"); g_log->info("[nvme] driver ready"); return 0; } g_db_stride = 4u << ((cap >> 32) & 0xf); g_timeout_ms = ((cap >> 24) & 0xff) * 500; if (!g_timeout_ms) g_timeout_ms = 500; log_hex("[nvme] doorbell stride=", g_db_stride); log_hex("[nvme] timeout ms=", g_timeout_ms); if (!api->dma) { g_log->warn("[nvme] DMA API unavailable; queues not configured"); g_log->info("[nvme] driver ready"); return 0; } /* ---- 分配队列与数据缓冲(低 4G, 4K 对齐, 清零, 物理连续) ---- */ if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_asq) != 0) goto dma_fail; if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_acq) != 0) goto dma_fail; if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_ident) != 0) goto dma_fail; if (api->dma->alloc_pages(NVME_DATA_PAGES, 4096, 0x100000000ULL, &g_data) != 0) goto dma_fail; if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_prp) != 0) goto dma_fail; if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iosq) != 0) goto dma_fail; if (api->dma->alloc_pages(1, 4096, 0x100000000ULL, &g_iocq) != 0) goto dma_fail; goto dma_ok; dma_fail: g_log->error("[nvme] DMA alloc failed"); g_log->info("[nvme] driver ready"); return 0; dma_ok: log_hex("[nvme] ASQ phys=", g_asq.phys); log_hex("[nvme] ACQ phys=", g_acq.phys); log_hex("[nvme] DATA phys=", g_data.phys); log_hex("[nvme] PRP list phys=", g_prp.phys); log_hex("[nvme] IOSQ phys=", g_iosq.phys); log_hex("[nvme] IOCQ phys=", g_iocq.phys); /* ---- 控制器复位: EN=0 等 RDY=0 ---- */ mmio_write32(NVME_INTMS, 0xFFFFFFFFu); /* 屏蔽全部中断向量 */ cc = mmio_read32(NVME_CC); if (cc & 1u) { mmio_write32(NVME_CC, cc & ~1u); } if (nvme_wait_csts(1u, 0u, g_timeout_ms) != 0) { g_log->error("[nvme] disable failed"); nvme_dump_regs("controller disable timeout"); g_log->info("[nvme] driver ready"); return 0; } g_log->info("[nvme] controller disabled"); /* ---- 配置 admin queue ---- */ mmio_write32(NVME_AQA, ((u32)(ADMIN_QSIZE - 1) << 16) | (u32)(ADMIN_QSIZE - 1)); mmio_write64(NVME_ASQ, g_asq.phys); mmio_write64(NVME_ACQ, g_acq.phys); g_asq_tail = 0; g_acq_head = 0; g_acq_phase = 1; g_cid = 1; /* ---- 使能: EN=1, CSS=NVM, MPS=4K, AMS=RR, IOSQES=6, IOCQES=4 ---- */ mmio_write32(NVME_CC, 0x00460001u); if (nvme_wait_csts(1u, 1u, g_timeout_ms) != 0) { g_log->error("[nvme] enable failed"); nvme_dump_regs("controller enable timeout"); g_log->info("[nvme] driver ready"); return 0; } g_log->info("[nvme] controller enabled, admin queue live"); /* ---- Identify ---- */ u32 nn = 0; if (nvme_identify_controller(&nn) != 0) { g_log->error("[nvme] identify controller failed"); g_log->info("[nvme] driver ready"); return 0; } if (nn == 0) { g_log->warn("[nvme] no namespace"); g_log->info("[nvme] driver ready"); return 0; } u64 nsze = 0; u32 lbads = 0; if (nvme_identify_namespace(1, &nsze, &lbads) != 0) { g_log->error("[nvme] identify namespace failed"); g_log->info("[nvme] driver ready"); return 0; } if (lbads < 9 || lbads > 12 || nsze == 0) { g_log->warn("[nvme] unsupported LBA format or empty NS"); log_hex("[nvme] LBADS=", lbads); g_log->info("[nvme] driver ready"); return 0; } g_sector_size = 1ULL << lbads; g_sector_count = nsze; log_hex("[nvme] sector size=", g_sector_size); log_hex("[nvme] sector count=", g_sector_count); /* ---- 单命令扇区上限: min(bounce 缓冲, 2^MDTS 页, NLB 16-bit) ---- * QEMU MDTS=7 → 512KiB, bounce 32 页=128KiB 更紧, 故 chunk=256 扇区, * 恰好覆盖 DSK FAT32 的 256 扇区批量读(一次命令读满)。 */ { u64 buf_bytes = (u64)NVME_DATA_PAGES * 4096u; u64 mdts_bytes = g_mdts ? ((1ULL << g_mdts) * 4096u) : buf_bytes; u64 cap = buf_bytes < mdts_bytes ? buf_bytes : mdts_bytes; g_chunk_max_sectors = (u32)(cap / g_sector_size); if (g_chunk_max_sectors > 65536u) g_chunk_max_sectors = 65536u; if (g_chunk_max_sectors == 0) g_chunk_max_sectors = 1; log_hex("[nvme] chunk max sectors=", g_chunk_max_sectors); } /* ---- IO queue ---- */ if (nvme_create_io_queues() != 0) { g_log->error("[nvme] IO queue creation failed"); g_log->info("[nvme] driver ready"); return 0; } g_io_ready = 1; g_log->info("[nvme] IO queue live"); /* ---- LBA0 读取验证 ---- */ { int rc = nvme_io_rw(NVME_IO_READ, 0, 1, g_data.phys); log_hex("[nvme] READ LBA0 rc=", (u64)(i64)rc); if (rc == 0) { u8 *d = (u8 *)g_data.virt; u32 *w = (u32 *)g_data.virt; log_hex("[nvme] LBA0 dword0=", w[0]); log_hex("[nvme] LBA0 dword1=", w[1]); log_hex("[nvme] LBA0 dword2=", w[2]); log_hex("[nvme] LBA0 dword3=", w[3]); static const char sig[] = "DESHABNVME0"; u32 match = 1; for (u32 i = 0; i < 11; i++) { if (d[i] != (u8)sig[i]) { match = 0; break; } } if (match) { g_log->info("[nvme] LBA0 signature OK (DESHABNVME0)"); g_test_disk = 1; } else { g_log->info("[nvme] LBA0 data read (no test signature)"); } } } /* ---- 注册 block provider(读 + 写) ---- */ if (api->block && api->block->register_device) { struct dkm_block_device_desc desc; desc.name = "nvme0"; desc.sector_size = g_sector_size; desc.sector_count = g_sector_count; desc.ctx = 0; desc.read = nvme_block_read; desc.write = nvme_block_write; int index = api->block->register_device(&desc); log_hex("[nvme] block provider index=", (u64)(i64)index); } else { g_log->warn("[nvme] block API unavailable; provider not registered"); } /* ---- 数据面自测: 写测试仅在签名测试盘上运行(防实机盘数据损坏) ---- */ if (g_test_disk) { nvme_selftest_read_multipage(); /* 32 扇区 16KiB: PRP list 读 */ nvme_selftest_rw(1000, 16); /* 8KiB: PRP2 直连 (2 页) 写+读回 */ nvme_selftest_rw(2000, 32); /* 16KiB: PRP list (4 页) 写+读回 */ if (nvme_lba0_sig_ok()) { g_log->info("[nvme] LBA0 sig intact after write tests"); } else { g_log->error("[nvme] LBA0 sig CORRUPTED after write tests"); } } else { g_log->info("[nvme] not a test disk; write self-test skipped"); } g_log->info("[nvme] driver ready"); return 0; } __attribute__((visibility("default"))) int driver_exit(struct dkm_driver_handle *handle) { (void)handle; return 0; }