Files
Deshab-OS/CODE/DKM/nvme/nvme.c
T
2026-07-31 13:53:49 +08:00

942 lines
35 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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;
}