Files
Deshab-OS/CODE/DKM/nvme/nvme.c
T
Linehu 583262f5f0 feat: SKEL skeleton instrumentation - designed-but-unimplemented paths panic
- hypercall.c: 5 Phase 1.3 stubs -> HCALL-E01..E05 panic (no NOSYS return)
- drr_stub.c: snapshot alloc fail -> DRR-E01; new skeletons DRR-E02 root key
  seal, DRR-E03 task kill crypto erase, DRR-E04/E05 segment/system rollback,
  DRR-E06/E07 driver recovery ops dispatch
- sched.c: enable fail -> SCH-E02 panic (no silent demo skip)
- dkm/kernel_api.c: D1 hot-unload DKM-E01, D2 bare code loader DKM-E02,
  SIPI AP wake DKM-E03 skeletons
- virtio_net tx/rx stubs -> VNET-E01/E02; ath9k tx stub -> ATH-E01;
  xhci port reset -> XHC-E01; nvme MSI/MSI-X fail -> NVME-E01..E03 (no
  silent polling downgrade); e1000 MAC fallback -> E1K-E01
- pe_shim unimpl API -> PE-E01; PE ordinal import -> PE-E02
- kernel/main.c: lib sync / vscode install fail -> LNX-E01/E02
- NEW brand/brand.c (E1 boot anim BRAND-E01, E2 time egg BRAND-E02) and
  pkg/pkg.c (pacman skeleton PKG-E01..03), wired into UTSM MAKEFILE
- Deliberate design-internal fallbacks kept and documented (VMX skip,
  bzImage skip, KDE fallback, boot-module fallback, apic->PIC route)
2026-10-03 00:30:54 +08:00

1642 lines
64 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。B7 阶段3 起 IO 队列
* 默认尝试 MSI/MSI-X 中断驱动(FUCK [drivers] nvme_msi, 默认 1 自动):
* 先 MSI cap 编程, 缺则 MSI-X (QEMU nvme 只暴露 MSI-X) — 表项
* addr=0xFEE00000|dest<<12, data=动态向量, entry unmask + cap Enable;
* Create IOCQ IEN=1/IV=0 + INTMC 解除 IV0; handler 摘 CQE 进完成槽、
* 推进 head、写 CQ doorbell, 自写 LAPIC EOI 并返回 1 (MSI 直投 LAPIC,
* 与 apic_route 开关无关, 不依赖阶段2 的 EOI 钩子注册状态)。
* 提交侧统一等待: 先查完成槽(中断路径), 再在 irqsave 临界区内手动
* 摘取(自适应轮询路径) — IF=0 (DSK 阶段) 或 MSI 未投递时零感知降级,
* 行为与旧版纯轮询一致。计数: g_msi_irq_count / g_msi_slot_hits /
* g_msi_poll_harvests / g_msi_timeouts, init 末尾与压测后打点。
* 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.4.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 签名的测试盘 */
/* ---- B7 阶段3: MSI 状态 ---- */
static int g_cfg_nvme_msi = 1; /* FUCK [drivers] nvme_msi, 默认 1 自动尝试 */
static int g_msi_enabled; /* MSI/MSI-X 编程成功且 handler 已注册 */
static int g_msix_mode; /* 1=MSI-X 表项模式, 0=MSI cap 模式 */
static int g_msi_vector = -1; /* 动态分配的 IDT 向量 (0x40-0xDF 池) */
/* 完成槽: 单 outstanding 前提下, handler 是唯一写者、提交侧是唯一读者,
* 且下一命令提交前不可能再有新 CQE, 故读-清无需互斥; valid 最后写/最先读,
* x86 TSO store-store / load-load 天然有序, nvme_mb 只挡编译器重排。 */
static volatile u32 g_slot_valid;
static volatile u32 g_slot_status; /* sc | (sct << 8), 0 = 成功 */
static volatile u32 g_slot_dw0; /* CQE dw0 (命令特定返回) */
static volatile u32 g_slot_cid; /* CQE dw3 低 16 位 CID, 提交侧比对防错位 */
static u64 g_msi_irq_count; /* MSI handler 进入次数 (投递证明) */
static u64 g_msi_slot_hits; /* 提交侧经完成槽完成的命令数 (中断路径) */
static u64 g_msi_poll_harvests; /* 提交侧 irqsave 手动摘取数 (轮询路径) */
static u64 g_msi_timeouts; /* 完成超时计数 (真故障, 非降级) */
static u64 g_msi_cid_mismatch; /* 摘到 CID 与提交 cid 不符的 CQE 计数 (队列错位警报) */
/* ---- 基础工具 ---- */
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");
}
/* 十进制打印 (MSI 计数器打点用, 比 hex 直观) */
static void log_dec(const char *prefix, u64 val) {
char buf[24];
u32 pos = 0;
if (!val) {
buf[pos++] = '0';
} else {
char tmp[20];
u32 n = 0;
while (val) { tmp[n++] = (char)('0' + (val % 10)); val /= 10; }
while (n) buf[pos++] = tmp[--n];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static int memeq(const char *a, const char *b, u32 n) {
for (u32 i = 0; i < n; i++) {
if (a[i] != b[i]) return 0;
}
return 1;
}
static u32 str_len(const char *s) {
u32 n = 0;
while (s[n]) n++;
return n;
}
/* 在 NUL 结尾字符串中查找子串 (boot module cmdline 匹配用) */
static int str_contains(const char *hay, const char *needle) {
u32 nlen = str_len(needle);
if (!nlen) return 1;
for (u32 i = 0; hay[i]; i++) {
u32 j = 0;
while (j < nlen && hay[i + j] && hay[i + j] == needle[j]) j++;
if (j == nlen) return 1;
}
return 0;
}
static void nvme_cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) {
__asm__ volatile ("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "a"(leaf), "c"(0));
}
/* irqsave 临界区: 提交侧手动摘取 CQE 时屏蔽中断, 与 MSI handler 互斥。
* pushf/popf 配对恢复 IF — DSK 阶段 IF=0, 绝不能无条件 sti。 */
static __inline__ u64 nvme_irqsave(void) {
u64 flags;
__asm__ volatile("pushfq; popq %0; cli" : "=r"(flags) :: "memory");
return flags;
}
static __inline__ void nvme_irqrestore(u64 flags) {
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory", "cc");
}
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;
}
/* ---- FUCK 配置自解析 (与 apic.c 同一范式, 不依赖 UTSM ini_parser) ----
* UTSM 经 Limine boot module (cmdline="fuck:config") 预加载 FUCK;
* kernel_api.boot_modules_response 对所有 DKM 驱动可见。
* 此处只找 [drivers] 分区下的 nvme_msi 键; 缺失文件/键一律默认 1
* (自动尝试 MSI, 失败回轮询), 与无配置时行为一致。
*/
/* Limine boot module 最小类型 (与 apic.c / bootfs.c / UTSM limine.h 布局一致) */
struct limine_file_min {
u64 revision;
void *address;
u64 size;
char *path;
char *cmdline;
u32 media_type;
u32 unused;
u32 tftp_ip;
u32 tftp_port;
u32 partition_index;
u32 mbr_disk_id;
u8 gpt_disk_uuid[16];
u8 gpt_part_uuid[16];
u8 part_uuid[16];
};
struct limine_module_response_min {
u64 revision;
u64 module_count;
struct limine_file_min **modules;
};
/* 解析值 token: 支持 0/1/true/false/on/off/yes/no 及十进制整数 */
static int parse_scalar(const char *line, u64 start, u64 line_len, int defval) {
u64 i = start;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
u64 tok_start = i;
while (i < line_len && line[i] != ' ' && line[i] != '\t' &&
line[i] != '#' && line[i] != ';' && line[i] != '\r') i++;
u64 tok_len = i - tok_start;
const char *tok = line + tok_start;
if (!tok_len) return defval;
if (tok_len == 1 && tok[0] == '1') return 1;
if (tok_len == 1 && tok[0] == '0') return 0;
if (tok_len == 4 && memeq(tok, "true", 4)) return 1;
if (tok_len == 5 && memeq(tok, "false", 5)) return 0;
if (tok_len == 2 && memeq(tok, "on", 2)) return 1;
if (tok_len == 3 && memeq(tok, "off", 3)) return 0;
if (tok_len == 3 && memeq(tok, "yes", 3)) return 1;
if (tok_len == 2 && memeq(tok, "no", 2)) return 0;
int neg = 0;
u64 j = 0;
if (tok[0] == '-') { neg = 1; j = 1; }
int val = 0;
int any = 0;
for (; j < tok_len; j++) {
if (tok[j] < '0' || tok[j] > '9') return defval;
val = val * 10 + (tok[j] - '0');
any = 1;
}
if (!any) return defval;
return neg ? -val : val;
}
static int fuck_get_int(const char *text, u64 size,
const char *section, const char *key, int defval) {
u64 pos = 0;
int in_section = 0;
u32 slen = str_len(section);
u32 klen = str_len(key);
while (pos < size) {
u64 end = pos;
while (end < size && text[end] != '\n') end++;
u64 line_len = end - pos;
if (line_len > 0 && text[pos + line_len - 1] == '\r') line_len--;
const char *line = text + pos;
u64 i = 0;
while (i < line_len && (line[i] == ' ' || line[i] == '\t')) i++;
if (i < line_len) {
char c = line[i];
if (c == '#' || c == ';') {
/* 注释行, 跳过 */
} else if (c == '[') {
in_section = 0;
if (line_len >= (u64)(i + 1 + slen + 1) &&
memeq(line + i + 1, section, slen) &&
line[i + 1 + slen] == ']') {
in_section = 1;
}
} else if (in_section) {
if (line_len >= (u64)(i + klen) &&
memeq(line + i, key, klen)) {
u64 j = i + klen;
while (j < line_len && (line[j] == ' ' || line[j] == '\t')) j++;
if (j < line_len && line[j] == '=') {
return parse_scalar(line, j + 1, line_len, defval);
}
}
}
}
pos = end + 1;
}
return defval;
}
static void nvme_load_config(const struct dkm_kernel_api *api) {
/* 编译期默认: 自动尝试 MSI (任何前提缺失都会回退纯轮询, 零回归) */
g_cfg_nvme_msi = 1;
const struct limine_module_response_min *resp =
(const struct limine_module_response_min *)api->boot_modules_response;
if (!resp || !resp->modules) {
g_log->warn("[nvme] no boot modules; FUCK defaults in effect");
return;
}
for (u64 i = 0; i < resp->module_count; i++) {
struct limine_file_min *f = resp->modules[i];
if (!f || !f->cmdline || !f->address || !f->size) continue;
if (!str_contains(f->cmdline, "fuck:config")) continue;
g_cfg_nvme_msi = fuck_get_int((const char *)f->address, f->size,
"drivers", "nvme_msi", 1);
g_log->info("[nvme] FUCK config module found");
log_dec("[nvme] cfg nvme_msi=", (u64)g_cfg_nvme_msi);
return;
}
g_log->warn("[nvme] FUCK module (fuck:config) not found; defaults in effect");
}
/* ---- 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);
/* ---- B7 阶段3: IO CQ 完成摘取 (MSI handler 与提交侧轮询共用) ----
* 返回 1 = 摘到一枚 CQE; 0 = 无新完成。
* out_cid 返回 CQE dw3 低 16 位的命令 ID, 供调用方与提交 cid 比对 —
* 单 outstanding 模型下 CQ 任何时刻只应有当前命令的一枚 CQE, CID 不匹配
* 即队列状态错位的硬证据, 必须显式报错而非静默当作当前命令完成
* (route=0 曾观测 stress readback MISMATCH @0x9000, 疑 CQE 提前消费)。
* 并发模型: 单生产者(控制器 DMA 写 CQ) + 单逻辑消费者, 消费者有两个入口 ——
* 1. MSI handler (IRQ 上下文): 同一 vector 在 EOI 前不会被 LAPIC 再次
* 投递, handler 对同向量天然不可重入, 无需自锁;
* 2. 提交侧统一等待 (主上下文): 必须在 nvme_irqsave 临界区内调用,
* 屏蔽中断期间 handler 不可能插入, 消除 "phase 判定后、head 推进前
* 被 handler 抢摘同一 CQE" 的双消费竞态。
* head 推进 + CQ doorbell 只在此函数内发生, 是两个入口的唯一汇合点。
*/
static int nvme_iocq_harvest(u32 *out_status, u32 *out_dw0, u32 *out_cid) {
volatile u32 *cq = (volatile u32 *)g_iocq.virt;
u32 dw3 = cq[g_iocq_head * 4 + 3];
if (((dw3 >> 16) & 1u) != g_iocq_phase) return 0;
if (out_dw0) *out_dw0 = cq[g_iocq_head * 4 + 0];
if (out_cid) *out_cid = dw3 & 0xffffu;
u32 sc = (dw3 >> 17) & 0xffu;
u32 sct = (dw3 >> 25) & 0x7u;
/* SC 与 SCT 联合编码 (与 nvme_submit 经典路径同一约定):
* SCT!=0 且 SC==0 时只返回 sc 会被误判成功。 */
*out_status = sc | (sct << 8);
g_iocq_head = (g_iocq_head + 1) % IO_QSIZE;
if (g_iocq_head == 0) g_iocq_phase ^= 1u;
nvme_mb(); /* head 先于 doorbell 可见 */
mmio_write32(cq_head_db(1), g_iocq_head);
return 1;
}
/* MSI 中断 handler: 摘 CQE 进完成槽。
* 返回 1 = 自负 LAPIC EOI (抑制 idt_handler 的钩子补 EOI)。
* 为何自写 EOI 而不依赖钩子: MSI 是设备向 LAPIC 直投的消息中断,
* 投递路径不经过 IOAPIC/PIC, 与 apic_route 开关无关; 但 EOI 钩子只在
* route=1 时注册, route=0 下钩子为 NULL 会漏写 EOI — LAPIC ISR 位
* 残留将永久阻塞同优先级后续中断, MSI 静默退化为 one-shot。
* handler 内直写 LAPIC EOI 寄存器后返回 1, 两种路由模式下行为一致。
* LAPIC 未软件使能时该写为无害 no-op (此时 MSI 本就不会投递)。
* 地址取 x86 默认 LAPIC base 0xFEE00000 + EOI 偏移 0xB0 (QEMU/MADT
* 一致; SAS-R0 恒等映射覆盖 4G 以下, apic.drv 已实证可直访)。
* 时序说明: 即使提交侧轮询抢先摘走 CQE, 控制器中断信号早已发出,
* 本次 handler 仍会执行并计入 g_msi_irq_count — 投递证据不受竞态影响。 */
static int nvme_msi_handler(u8 vector) {
(void)vector;
g_msi_irq_count++;
u32 st = 0, dw0 = 0, cid = 0;
if (nvme_iocq_harvest(&st, &dw0, &cid)) {
g_slot_status = st;
g_slot_dw0 = dw0;
g_slot_cid = cid;
nvme_mb(); /* payload 先于 valid 对提交侧可见 */
g_slot_valid = 1;
}
*(volatile u32 *)(uintptr_t)0xFEE000B0u = 0; /* LAPIC EOI */
return 1;
}
/* IO 命令统一完成等待 (MSI 模式): 完成槽(中断路径)优先, irqsave 手动摘取
* (自适应轮询路径)兜底。MSI 健康时 handler 在 CQE 落盘瞬间填槽; MSI 未投递
* (LAPIC 未启用/IF=0/信号丢失) 时手动摘取沿原 phase 语义直接完成, 调用方
* 零感知。超时语义与经典路径一致 (CAP.TO*500ms*10, 下限 5s)。 */
static int nvme_io_wait_completion(u16 cid, u32 opcode, u32 *out_cqe_dw0) {
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 (;;) {
/* 中断路径: handler 已摘取 CQE 进完成槽 */
if (g_slot_valid) {
u32 st = g_slot_status;
u32 dw0 = g_slot_dw0;
u32 scid = g_slot_cid;
nvme_mb();
g_slot_valid = 0;
g_msi_slot_hits++;
if (scid != (u32)cid) {
/* CID 错位: 槽内 CQE 不属于本命令 — 队列状态已损,
* 显式报错并继续等真 CQE (超时兜底), 绝不静默当成功 */
g_msi_cid_mismatch++;
g_log->error("[nvme] CID mismatch (slot)");
log_hex("[nvme] expect cid=", cid);
log_hex("[nvme] got cid=", scid);
continue;
}
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
if (st) {
g_log->error("[nvme] command failed (msi slot)");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] status=", st);
return (int)st;
}
return 0;
}
/* 自适应轮询路径: irqsave 临界区内手动摘取 (与 handler 互斥) */
u32 st = 0, dw0 = 0, hcid = 0;
u64 flags = nvme_irqsave();
int got = nvme_iocq_harvest(&st, &dw0, &hcid);
nvme_irqrestore(flags);
if (got) {
g_msi_poll_harvests++;
if (hcid != (u32)cid) {
g_msi_cid_mismatch++;
g_log->error("[nvme] CID mismatch (poll)");
log_hex("[nvme] expect cid=", cid);
log_hex("[nvme] got cid=", hcid);
continue;
}
if (out_cqe_dw0) *out_cqe_dw0 = dw0;
if (st) {
g_log->error("[nvme] command failed");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
log_hex("[nvme] status=", st);
return (int)st;
}
return 0;
}
if (dkm_rdtsc() > deadline) {
g_msi_timeouts++;
g_log->error("[nvme] completion timeout (msi mode)");
log_hex("[nvme] opcode=", opcode);
log_hex("[nvme] cid=", cid);
nvme_dump_regs("completion timeout");
return -100;
}
__asm__ volatile("pause");
}
}
/* ---- 命令提交(同步轮询, 单 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++;
/* CID=0 保留不用: QEMU 在 MSI-X unmask 边界会向 IO CQ 补投一枚
* CID=0/phase 正确/status 成功的幽灵 CQE (实测稳定复现), g_cid 回绕
* 跳过 0 可保证该幽灵 CQE 永远被 CID 校验拒绝, 绝无误判窗口。 */
if (g_cid == 0) g_cid = 1;
/* 逐 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);
/* B7 阶段3: IO 队列 MSI 模式 → 完成槽 + irqsave 摘取统一等待。
* admin 队列保持纯轮询 (仅 init 期使用, 且 INTMC 解除前 IV0 处于
* 屏蔽态, admin 完成不会触发 MSI)。 */
if (cq_qid == 1 && g_msi_enabled) {
return nvme_io_wait_completion(cid, dw[0] & 0xffu, out_cqe_dw0);
}
/* 真机超时: 使用 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;
}
/* ---- B7 阶段3: PCI MSI 能力编程 ----
* MSI cap (ID 0x05) 布局:
* +0x00 u8 Cap ID (=0x05) +0x01 u8 Next Ptr
* +0x02 u16 Message Control: bit0 Enable, bit[3:1] MMC(可申请向量数-1),
* bit[6:4] MME(已分配向量数-1), bit7 64-bit Capable, bit8 PVM
* +0x04 u32 Message Address
* 64-bit 时: +0x08 u32 Message Upper Address, +0x0C u16 Message Data
* 32-bit 时: +0x08 u16 Message Data
* PVM(bit8)=1 时另有 Mask/Pending 寄存器, 复位值 0 (全部 unmask)。
* x86 LAPIC 投递公式 (physical destination, fixed delivery):
* Address = 0xFEE00000 | (dest_apic_id << 12) (RH=0, DM=0)
* Data = vector | (0<<8 delivery=fixed) (level/trigger 对 MSI 无效)
*/
#define PCI_CAP_ID_MSI 0x05
#define PCI_CAP_ID_MSIX 0x11
#define PCI_STATUS_CAPLIST 0x10u
static int nvme_find_pci_cap(u8 bus, u8 dev, u8 func, u8 cap_id) {
u32 reg04 = dkm_pci_read(bus, dev, func, PCI_COMMAND);
if (!((reg04 >> 16) & PCI_STATUS_CAPLIST)) return -1; /* Status.CapList=0 */
u8 ptr = (u8)(dkm_pci_read(bus, dev, func, PCI_CAP_PTR) & 0xFC);
int guard = 0;
while (ptr && guard++ < 64) { /* guard 防配置空间坏链死循环 */
u32 dw = dkm_pci_read(bus, dev, func, ptr);
u8 id = (u8)(dw & 0xff);
u8 next = (u8)((dw >> 8) & 0xff);
log_hex("[nvme] pci cap id=", id);
log_hex("[nvme] pci cap at=", ptr);
if (id == cap_id) return (int)ptr;
ptr = next & 0xFC;
}
return -1;
}
/* MSI cap (0x05) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
* 失败路径完整回滚 (恢复 msg_ctl + 释放向量), 不留半编程状态。 */
static int nvme_msi_program(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSI);
if (cap < 0) {
g_log->info("[nvme] MSI capability not present");
return 0;
}
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
u32 msg_ctl = (cdw >> 16) & 0xffffu;
int cap64 = (int)((msg_ctl >> 7) & 1u);
int pvm = (int)((msg_ctl >> 8) & 1u);
log_hex("[nvme] MSI msg_ctl=", msg_ctl);
log_hex("[nvme] MSI 64bit=", (u64)cap64);
log_hex("[nvme] MSI pvm=", (u64)pvm);
/* 动态向量: UTSM 位图池 0x40-0xDF, 与静态向量 (tick 0xE0/spurious 0xFF)
* 及 legacy PIC 段 (0x20-0x2F) 无交叠。 */
int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) {
g_log->warn("[nvme] MSI: vector alloc failed; polling");
return 0;
}
log_dec("[nvme] MSI vector=", (u64)vector);
/* 目的 APIC ID: CPUID.1:EBX[31:24] = BSP initial APIC ID (QEMU=0) */
u32 ca, cb, cc_, cd;
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
(void)ca; (void)cc_; (void)cd;
u32 dest = (cb >> 24) & 0xffu;
log_hex("[nvme] MSI dest apic id=", dest);
u32 addr = 0xFEE00000u | (dest << 12);
u32 data = (u32)vector & 0xffu; /* fixed delivery, edge */
/* 先写 Address/Data, 最后置 Enable — 避免半成品消息被发出 */
dkm_pci_write(bus, dev, func, (u8)(cap + 4), addr);
if (cap64) {
dkm_pci_write(bus, dev, func, (u8)(cap + 8), 0); /* upper addr */
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 12));
dw = (dw & 0xffff0000u) | data; /* data 为低 u16 */
dkm_pci_write(bus, dev, func, (u8)(cap + 12), dw);
} else {
u32 dw = dkm_pci_read(bus, dev, func, (u8)(cap + 8));
dw = (dw & 0xffff0000u) | data;
dkm_pci_write(bus, dev, func, (u8)(cap + 8), dw);
}
/* Message Control: MME=0 (单向量), Enable=1; 低 u16 (id/next) 回写原值 */
u32 mc = msg_ctl;
mc &= ~(0x7u << 4); /* MME=0 → 1 vector */
mc |= 1u; /* MSI Enable */
cdw = (cdw & 0x0000ffffu) | (mc << 16);
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
/* PVM: 显式清 mask bit0 (复位值即为 0, 此处防御性 RMW) */
if (pvm) {
int mask_off = cap64 ? cap + 16 : cap + 12;
u32 mask = dkm_pci_read(bus, dev, func, (u8)mask_off);
mask &= ~1u;
dkm_pci_write(bus, dev, func, (u8)mask_off, mask);
log_hex("[nvme] MSI mask cleared=", mask);
}
/* 回读验证: Enable 置位 + Address/Data 与编程值一致 */
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
u32 rb_addr = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
u32 rb_data = dkm_pci_read(bus, dev, func,
(u8)(cap64 ? cap + 12 : cap + 8)) & 0xffffu;
log_hex("[nvme] MSI readback ctl=", rb_ctl);
log_hex("[nvme] MSI readback addr=", rb_addr);
log_hex("[nvme] MSI readback data=", rb_data);
if (!(rb_ctl & 1u) || rb_addr != addr || (rb_data & 0xffu) != data) {
g_log->warn("[nvme] MSI: readback mismatch; rolling back");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb);
api->irq_vector_free(vector);
return 0;
}
/* 注册 handler (irq_register 已接受向量命名空间, >0x2F 直挂分发表) */
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
g_msi_enabled = 1;
g_msi_vector = vector;
g_log->info("[nvme] MSI enabled (IO CQ IEN=1, IV=0)");
return 1;
}
/* 按 BIR 读取 BAR 物理基址 (处理 64-bit BAR; IO BAR 返回 0) */
static u64 nvme_bar_phys(u8 bus, u8 dev, u8 func, u32 bir) {
u8 reg = (u8)(PCI_BAR0 + bir * 4u);
u32 lo = dkm_pci_read(bus, dev, func, reg);
if (lo & 1u) return 0; /* IO BAR */
if ((lo & 0x6u) == 0x4u) { /* 64-bit MMIO BAR */
u32 hi = dkm_pci_read(bus, dev, func, (u8)(reg + 4));
return ((u64)hi << 32) | (u64)(lo & 0xFFFFFFF0u);
}
return (u64)(lo & 0xFFFFFFF0u);
}
/* MSI-X cap (0x11) 编程路径: 成功返回 1, cap 缺失或失败返回 0。
* QEMU nvme 只暴露 MSI-X (无 0x05), 本路径是 QEMU 下中断驱动化的唯一通道。
* 与 MSI 的差异仅在消息存储位置: MSI-X 的 addr/data 存放在 BAR 内的
* 表项 (16B/项), 需先把表区 MMIO 映射进 CPU 地址空间再编程 entry 0。
* 失败路径完整回滚 (清 Enable + 表项重新 mask + 释放向量)。 */
static int nvme_msix_program(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
int cap = nvme_find_pci_cap(bus, dev, func, PCI_CAP_ID_MSIX);
if (cap < 0) {
g_log->warn("[nvme] MSI-X capability not present; polling");
return 0;
}
if (!api->mm_map_mmio) {
g_log->warn("[nvme] MSI-X: mm_map_mmio unavailable; polling");
return 0;
}
u32 cdw = dkm_pci_read(bus, dev, func, (u8)cap);
u32 msg_ctl = (cdw >> 16) & 0xffffu;
u32 table_size = (msg_ctl & 0x7ffu) + 1u; /* N-1 encoded */
u32 tbl_dw = dkm_pci_read(bus, dev, func, (u8)(cap + 4));
u32 bir = tbl_dw & 7u;
u32 tbl_off = tbl_dw & ~7u;
log_hex("[nvme] MSI-X msg_ctl=", msg_ctl);
log_dec("[nvme] MSI-X table size=", table_size);
log_hex("[nvme] MSI-X table BIR=", bir);
log_hex("[nvme] MSI-X table offset=", tbl_off);
if (bir > 5u) {
g_log->warn("[nvme] MSI-X: invalid BIR; polling");
return 0;
}
u64 bar_phys = nvme_bar_phys(bus, dev, func, bir);
if (!bar_phys) {
g_log->warn("[nvme] MSI-X: table BAR invalid; polling");
return 0;
}
u64 tbl_phys = bar_phys + tbl_off;
log_hex("[nvme] MSI-X table BAR phys=", bar_phys);
log_hex("[nvme] MSI-X table phys=", tbl_phys);
/* 映射表区首页 (含 entry0 的 16B)。mm_map_mmio 返回含页内偏移的指针,
* 页属性 PCD|PWT (UC), MSI-X 表访问不要求 cache 一致性操作。 */
volatile u32 *tbl = (volatile u32 *)api->mm_map_mmio(tbl_phys, 4096);
if (!tbl) {
/* 严格错误策略插桩:MSI-X 表映射失败不允许静默转轮询(NVME-E01) */
g_log->error("[nvme] MSI-X: table MMIO map failed");
g_log->panic("NVME-E01 MSIX TABLE MAP FAILED");
return 0;
}
int vector = api->irq_vector_alloc();
if (vector < 0 || vector > 255) {
/* 严格错误策略插桩:向量分配失败不允许静默转轮询(NVME-E02) */
g_log->error("[nvme] MSI-X: vector alloc failed");
g_log->panic("NVME-E02 VECTOR ALLOC FAILED");
return 0;
}
log_dec("[nvme] MSI-X vector=", (u64)vector);
u32 ca, cb, cc_, cd;
nvme_cpuid(1, &ca, &cb, &cc_, &cd);
(void)ca; (void)cc_; (void)cd;
u32 dest = (cb >> 24) & 0xffu;
u32 addr = 0xFEE00000u | (dest << 12);
/* 先写表项 (addr/data/mask=0), 再置 cap Enable — 避免半成品消息 */
volatile u32 *e = tbl; /* entry 0 */
e[0] = addr; /* Msg Addr Lo */
e[1] = 0; /* Msg Addr Hi */
e[2] = (u32)vector & 0xffu; /* Msg Data = 向量 */
e[3] = 0; /* Vector Control: unmask */
nvme_mb();
u32 nc = (msg_ctl | 0x8000u) & ~(1u << 14); /* Enable=1, FunctionMask=0 */
cdw = (cdw & 0x0000ffffu) | (nc << 16);
dkm_pci_write(bus, dev, func, (u8)cap, cdw);
/* 回读验证: cap Enable + 表项 addr/data/mask 与编程值一致 */
u32 rb_ctl = (dkm_pci_read(bus, dev, func, (u8)cap) >> 16) & 0xffffu;
u32 rb_a0 = e[0], rb_a1 = e[1], rb_d = e[2], rb_vc = e[3];
log_hex("[nvme] MSI-X readback ctl=", rb_ctl);
log_hex("[nvme] MSI-X readback entry addr=", ((u64)rb_a1 << 32) | rb_a0);
log_hex("[nvme] MSI-X readback entry data=", rb_d);
log_hex("[nvme] MSI-X readback entry vctl=", rb_vc);
if (!(rb_ctl & 0x8000u) || rb_a0 != addr || rb_a1 != 0 ||
(rb_d & 0xffu) != ((u32)vector & 0xffu) || (rb_vc & 1u)) {
/* 严格错误策略插桩:MSI-X 编程回读不符 = 硬件/虚拟机拒绝消息中断,
* 不允许静默回滚转轮询(NVME-E03)。 */
g_log->error("[nvme] MSI-X: readback mismatch");
g_log->panic("NVME-E03 MSIX READBACK MISMATCH");
u32 rb = dkm_pci_read(bus, dev, func, (u8)cap);
rb = (rb & 0x0000ffffu) | (msg_ctl << 16); /* 恢复原 msg_ctl */
dkm_pci_write(bus, dev, func, (u8)cap, rb);
e[3] = 1; /* 表项重新 mask */
api->irq_vector_free(vector);
return 0;
}
api->irq_register((u8)vector, (void *)(uintptr_t)nvme_msi_handler);
g_msi_enabled = 1;
g_msix_mode = 1;
g_msi_vector = vector;
g_log->info("[nvme] MSI-X enabled (table entry0, IO CQ IEN=1, IV=0)");
return 1;
}
/* 中断驱动化入口: 先 MSI (0x05), 缺则 MSI-X (0x11), 皆不可则纯轮询。
* 前提检查集中在此; 两条编程路径各自保证失败完整回滚。 */
static int nvme_msi_setup(const struct dkm_kernel_api *api,
u8 bus, u8 dev, u8 func) {
if (!g_cfg_nvme_msi) {
g_log->info("[nvme] MSI disabled by FUCK (nvme_msi=0); polling mode");
return 0;
}
if (!api->irq_vector_alloc || !api->irq_vector_free || !api->irq_register) {
g_log->warn("[nvme] MSI: kernel vector allocator unavailable; polling");
return 0;
}
if (nvme_msi_program(api, bus, dev, func)) return 1;
return nvme_msix_program(api, bus, dev, func);
}
/* ---- IO queue 创建 ---- */
static int nvme_create_io_queues(void) {
u32 dw[16];
/* Create IO CQ (qid=1): PC=1; MSI 就绪时 IEN=1 + IV=0
* CDW10 = QSIZE[31:16](0's based) | QID[15:0] —— QID 必须在低半字
* CDW11 = IV[31:16] | IEN[1] | PC[0] —— IV=0 与 INTMS/INTMC bit0 对应 */
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] = 1u;
if (g_msi_enabled) dw[11] = 1u | (1u << 1); /* PC=1, IEN=1, IV=0 */
log_hex("[nvme] create IOCQ CDW11=", dw[11]);
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");
}
/* B7 阶段3 压测: 128 扇区 (64KiB, PRP list 路径) × 50 次 写+读。
* 统计总耗时与 MSI 计数器增量, 供中断/轮询两模式粗粒度对比。
* 仅签名测试盘运行 (写 LBA3000 起的 50 个块, 不碰 LBA0 签名区)。 */
static void nvme_selftest_stress(void) {
static u8 wbuf[65536];
static u8 rbuf[65536];
for (u32 i = 0; i < sizeof(wbuf); i++) wbuf[i] = (u8)(i * 17u + 3u);
u64 irq0 = g_msi_irq_count, slot0 = g_msi_slot_hits, poll0 = g_msi_poll_harvests;
u64 t0 = dkm_rdtsc();
u32 fails = 0;
for (u32 i = 0; i < 50; i++) {
u64 lba = 3000 + (u64)i * 128;
if (nvme_block_write(0, lba, 128, wbuf) != 0) { fails++; break; }
if (nvme_block_read(0, lba, 128, rbuf) != 0) { fails++; break; }
}
u64 elapsed_ms = dkm_tsc_per_ms ? (dkm_rdtsc() - t0) / dkm_tsc_per_ms : 0;
/* 末次读回全量校验 (数据通路在 MSI 模式下完整性证明) */
if (!fails) {
for (u32 i = 0; i < sizeof(wbuf); i++) {
if (rbuf[i] != wbuf[i]) {
g_log->error("[nvme] stress readback MISMATCH");
log_hex("[nvme] first bad idx=", i);
log_hex("[nvme] expect=", wbuf[i]);
log_hex("[nvme] actual=", rbuf[i]);
log_hex("[nvme] cq head=", g_iocq_head);
log_hex("[nvme] cq phase=", g_iocq_phase);
log_hex("[nvme] slot valid=", g_slot_valid);
fails++;
break;
}
}
}
log_dec("[nvme] stress 50x(128sec W+R) ms=", elapsed_ms);
log_dec("[nvme] stress fails=", fails);
log_dec("[nvme] stress irq delta=", g_msi_irq_count - irq0);
log_dec("[nvme] stress slot delta=", g_msi_slot_hits - slot0);
log_dec("[nvme] stress poll delta=", g_msi_poll_harvests - poll0);
if (!fails) g_log->info("[nvme] stress verify OK");
}
static void nvme_msi_stats_dump(const char *tag) {
g_log->info(tag);
log_dec("[nvme] msi enabled=", (u64)g_msi_enabled);
log_dec("[nvme] msix mode=", (u64)g_msix_mode);
log_dec("[nvme] msi vector=", (u64)(g_msi_vector < 0 ? 0 : g_msi_vector));
log_dec("[nvme] msi irq count=", g_msi_irq_count);
log_dec("[nvme] msi slot hits=", g_msi_slot_hits);
log_dec("[nvme] msi poll harvests=", g_msi_poll_harvests);
log_dec("[nvme] msi timeouts=", g_msi_timeouts);
log_dec("[nvme] msi cid mismatch=", g_msi_cid_mismatch);
}
/* ---- 真机诊断: 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");
nvme_load_config(api);
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);
}
/* ---- B7 阶段3: IO queue 创建前尝试 MSI (任何前提缺失回退纯轮询) ---- */
nvme_msi_setup(api, bus, dev, func);
/* ---- 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");
/* MSI 就绪: INTMC 解除 IV0 屏蔽 (此前 IV0 保持屏蔽, admin 完成不产生
* MSI; 此后 IO CQ 每次完成经 MSI 投递到 g_msi_vector) */
if (g_msi_enabled) {
mmio_write32(NVME_INTMC, 1u);
log_hex("[nvme] INTMC unmask IV0, INTMS=", mmio_read32(NVME_INTMS));
}
/* ---- 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 页) 写+读回 */
nvme_selftest_stress(); /* B7: 128 扇区×50 压测 + MSI 计数 */
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");
}
nvme_msi_stats_dump("[nvme] --- MSI stats ---");
g_log->info("[nvme] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
return 0;
}