Files
MontaukOS/kernel/src/Drivers/Storage/Nvme.hpp
T

199 lines
8.0 KiB
C++

/*
* Nvme.hpp
* NVM Express (NVMe) storage driver
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Pci/Pci.hpp>
namespace Drivers::Storage::Nvme {
// =========================================================================
// NVMe controller registers (memory-mapped via BAR0)
// Ref: NVM Express Base Specification 2.0
// =========================================================================
constexpr uint32_t REG_CAP = 0x00; // Controller Capabilities (64-bit)
constexpr uint32_t REG_VS = 0x08; // Version
constexpr uint32_t REG_INTMS = 0x0C; // Interrupt Mask Set
constexpr uint32_t REG_INTMC = 0x10; // Interrupt Mask Clear
constexpr uint32_t REG_CC = 0x14; // Controller Configuration
constexpr uint32_t REG_CSTS = 0x1C; // Controller Status
constexpr uint32_t REG_AQA = 0x24; // Admin Queue Attributes
constexpr uint32_t REG_ASQ = 0x28; // Admin SQ Base Address (64-bit)
constexpr uint32_t REG_ACQ = 0x30; // Admin CQ Base Address (64-bit)
// CAP register fields (64-bit)
constexpr uint64_t CAP_MQES_MASK = 0xFFFF; // Max Queue Entries Supported (0-based)
constexpr int CAP_DSTRD_SHIFT = 32; // Doorbell Stride (2 ^ (2 + DSTRD))
constexpr uint64_t CAP_DSTRD_MASK = 0xFULL << 32;
constexpr int CAP_MPSMIN_SHIFT = 48; // Memory Page Size Minimum
constexpr uint64_t CAP_MPSMIN_MASK = 0xFULL << 48;
constexpr int CAP_MPSMAX_SHIFT = 52; // Memory Page Size Maximum
constexpr uint64_t CAP_MPSMAX_MASK = 0xFULL << 52;
constexpr uint64_t CAP_CSS_NVM = (1ULL << 37); // NVM Command Set supported
// CC register fields
constexpr uint32_t CC_EN = (1u << 0); // Enable
constexpr uint32_t CC_CSS_NVM = (0u << 4); // NVM Command Set
constexpr uint32_t CC_MPS_SHIFT = 7; // Memory Page Size (2 ^ (12 + MPS))
constexpr uint32_t CC_AMS_RR = (0u << 11); // Arbitration: Round Robin
constexpr uint32_t CC_SHN_NONE = (0u << 14); // No shutdown notification
constexpr uint32_t CC_SHN_NORMAL = (1u << 14); // Normal shutdown
constexpr uint32_t CC_IOSQES_SHIFT = 16; // I/O SQ Entry Size (2^n)
constexpr uint32_t CC_IOCQES_SHIFT = 20; // I/O CQ Entry Size (2^n)
// CSTS register fields
constexpr uint32_t CSTS_RDY = (1u << 0); // Ready
constexpr uint32_t CSTS_CFS = (1u << 1); // Controller Fatal Status
constexpr uint32_t CSTS_SHST_MASK = (3u << 2); // Shutdown Status
constexpr uint32_t CSTS_SHST_NORMAL = (0u << 2);
constexpr uint32_t CSTS_SHST_COMPLETE = (2u << 2);
// =========================================================================
// Submission Queue Entry (64 bytes)
// =========================================================================
struct SqEntry {
// Dword 0: Command Dword 0
uint8_t Opcode;
uint8_t Flags; // Fused (1:0), PSDT (7:6)
uint16_t CommandId;
// Dword 1
uint32_t Nsid; // Namespace ID
// Dwords 2-3
uint64_t Reserved;
// Dwords 4-5: Metadata Pointer
uint64_t Mptr;
// Dwords 6-9: Data Pointer (PRP1, PRP2)
uint64_t Prp1;
uint64_t Prp2;
// Dwords 10-15: Command specific
uint32_t Cdw10;
uint32_t Cdw11;
uint32_t Cdw12;
uint32_t Cdw13;
uint32_t Cdw14;
uint32_t Cdw15;
} __attribute__((packed));
static_assert(sizeof(SqEntry) == 64, "SqEntry must be 64 bytes");
// =========================================================================
// Completion Queue Entry (16 bytes)
// =========================================================================
struct CqEntry {
uint32_t Result; // Command-specific result (DW0)
uint32_t Reserved;
uint16_t SqHead; // SQ Head Pointer
uint16_t SqId; // SQ Identifier
uint16_t CommandId; // Command Identifier
uint16_t Status; // Status Field (bit 0 = Phase Tag)
} __attribute__((packed));
static_assert(sizeof(CqEntry) == 16, "CqEntry must be 16 bytes");
// Status field: Phase bit is bit 0; status code is bits 15:1
constexpr uint16_t CQE_PHASE_BIT = (1u << 0);
constexpr uint16_t CQE_STATUS_MASK = 0xFFFE; // bits 15:1
// =========================================================================
// Admin opcodes
// =========================================================================
constexpr uint8_t ADMIN_DELETE_IO_SQ = 0x00;
constexpr uint8_t ADMIN_CREATE_IO_SQ = 0x01;
constexpr uint8_t ADMIN_DELETE_IO_CQ = 0x04;
constexpr uint8_t ADMIN_CREATE_IO_CQ = 0x05;
constexpr uint8_t ADMIN_IDENTIFY = 0x06;
constexpr uint8_t ADMIN_SET_FEATURES = 0x09;
// =========================================================================
// NVM I/O opcodes
// =========================================================================
constexpr uint8_t IO_CMD_FLUSH = 0x00;
constexpr uint8_t IO_CMD_READ = 0x02;
constexpr uint8_t IO_CMD_WRITE = 0x01;
// =========================================================================
// Identify CNS values
// =========================================================================
constexpr uint32_t IDENTIFY_CNS_NAMESPACE = 0x00;
constexpr uint32_t IDENTIFY_CNS_CONTROLLER = 0x01;
// =========================================================================
// Feature identifiers
// =========================================================================
constexpr uint32_t FEATURE_NUM_QUEUES = 0x07;
// =========================================================================
// Queue parameters
// =========================================================================
constexpr int ADMIN_QUEUE_DEPTH = 32; // Admin queue entries
constexpr int IO_QUEUE_DEPTH = 64; // I/O queue entries
constexpr int MAX_NAMESPACES = 8;
// =========================================================================
// Namespace info
// =========================================================================
struct NamespaceInfo {
bool Active;
uint32_t Nsid;
uint64_t SectorCount; // Total LBAs (NSZE)
uint32_t SectorSize; // Bytes per LBA
uint32_t MaxTransferBlocks; // MDTS in blocks
char Model[41];
};
// =========================================================================
// MSI configuration (use a different IRQ slot than AHCI)
// =========================================================================
constexpr uint8_t MSI_IRQ = 26; // IRQ slot 26 = vector 58
constexpr uint32_t MSI_VECTOR = 58;
constexpr uint32_t MSI_ADDR_BASE = 0xFEE00000;
// =========================================================================
// Public API
// =========================================================================
// Probe a PCI device (called by driver matching framework)
bool Probe(const Pci::PciDevice& dev);
// Check if the driver was initialized
bool IsInitialized();
// Get number of active namespaces
int GetNamespaceCount();
// Read sectors from an NVMe namespace
// ns: namespace index, lba: starting LBA, count: sector count (max 128)
// buffer: destination buffer
// Returns true on success
bool ReadSectors(int ns, uint64_t lba, uint32_t count, void* buffer);
// Write sectors to an NVMe namespace
bool WriteSectors(int ns, uint64_t lba, uint32_t count, const void* buffer);
// Flush the namespace's volatile write cache to media (NVM Flush command).
// Returns true on success. Used during graceful shutdown.
bool Flush(int ns);
// Get info about a specific namespace
const NamespaceInfo* GetNamespaceInfo(int ns);
};