Kernel: Implement NVMe driver
I'm actually able to boot this os fine on own laptop now!
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
#define PAGE_SIZE ((uintptr_t)4096)
|
||||
#define PAGE_SIZE_SHIFT 12
|
||||
#define PAGE_ADDR_MASK (~(uintptr_t)0xFFF)
|
||||
|
||||
namespace Kernel
|
||||
|
||||
52
kernel/include/kernel/Storage/NVMe/Controller.h
Normal file
52
kernel/include/kernel/Storage/NVMe/Controller.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/PCI.h>
|
||||
#include <kernel/Storage/NVMe/Definitions.h>
|
||||
#include <kernel/Storage/NVMe/Namespace.h>
|
||||
#include <kernel/Storage/NVMe/Queue.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class NVMeController final : public StorageController, public CharacterDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(NVMeController);
|
||||
BAN_NON_MOVABLE(NVMeController);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<StorageController>> create(PCI::Device&);
|
||||
~NVMeController() { ASSERT_NOT_REACHED(); }
|
||||
|
||||
NVMeQueue& io_queue() { return *m_io_queue; }
|
||||
|
||||
virtual dev_t rdev() const override { return m_rdev; }
|
||||
virtual BAN::StringView name() const override { return m_name; }
|
||||
|
||||
private:
|
||||
NVMeController(PCI::Device& pci_device);
|
||||
virtual BAN::ErrorOr<void> initialize() override;
|
||||
|
||||
BAN::ErrorOr<void> identify_controller();
|
||||
BAN::ErrorOr<void> identify_namespaces();
|
||||
|
||||
BAN::ErrorOr<void> wait_until_ready(bool expected_value);
|
||||
BAN::ErrorOr<void> create_admin_queue();
|
||||
BAN::ErrorOr<void> create_io_queue();
|
||||
|
||||
private:
|
||||
PCI::Device& m_pci_device;
|
||||
BAN::UniqPtr<PCI::BarRegion> m_bar0;
|
||||
volatile NVMe::ControllerRegisters* m_controller_registers;
|
||||
|
||||
BAN::UniqPtr<NVMeQueue> m_admin_queue;
|
||||
BAN::UniqPtr<NVMeQueue> m_io_queue;
|
||||
|
||||
BAN::Vector<BAN::RefPtr<NVMeNamespace>> m_namespaces;
|
||||
|
||||
char m_name[20];
|
||||
const dev_t m_rdev;
|
||||
};
|
||||
|
||||
}
|
||||
295
kernel/include/kernel/Storage/NVMe/Definitions.h
Normal file
295
kernel/include/kernel/Storage/NVMe/Definitions.h
Normal file
@@ -0,0 +1,295 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel::NVMe
|
||||
{
|
||||
|
||||
struct CAP
|
||||
{
|
||||
uint64_t mqes : 16;
|
||||
uint64_t cqr : 1;
|
||||
uint64_t ams : 2;
|
||||
uint64_t __reserved0 : 5;
|
||||
uint64_t to : 8;
|
||||
uint64_t dstrd : 4;
|
||||
uint64_t nssrs : 1;
|
||||
uint64_t css : 8;
|
||||
uint64_t bps : 1;
|
||||
uint64_t cps : 2;
|
||||
uint64_t mpsmin : 4;
|
||||
uint64_t mpsmax : 4;
|
||||
uint64_t pmrs : 1;
|
||||
uint64_t cmpbs : 1;
|
||||
uint64_t nsss : 1;
|
||||
uint64_t crms : 2;
|
||||
uint64_t __reserved1 : 3;
|
||||
};
|
||||
static_assert(sizeof(CAP) == sizeof(uint64_t));
|
||||
|
||||
enum CAP_CSS
|
||||
{
|
||||
CAP_CSS_NVME = 1 << 0,
|
||||
CAP_CSS_IO = 1 << 6,
|
||||
CAP_CSS_ADMIN = 1 << 7,
|
||||
};
|
||||
|
||||
struct VS
|
||||
{
|
||||
uint32_t tertiary : 8;
|
||||
uint32_t minor : 8;
|
||||
uint32_t major : 16;
|
||||
};
|
||||
static_assert(sizeof(VS) == sizeof(uint32_t));
|
||||
|
||||
struct CC
|
||||
{
|
||||
uint32_t en : 1;
|
||||
uint32_t __reserved0 : 3;
|
||||
uint32_t css : 3;
|
||||
uint32_t mps : 4;
|
||||
uint32_t ams : 3;
|
||||
uint32_t shn : 2;
|
||||
uint32_t iosqes : 4;
|
||||
uint32_t iocqes : 4;
|
||||
uint32_t crime : 1;
|
||||
uint32_t __reserved1 : 7;
|
||||
};
|
||||
static_assert(sizeof(CC) == sizeof(uint32_t));
|
||||
|
||||
struct CSTS
|
||||
{
|
||||
uint32_t rdy : 1;
|
||||
uint32_t cfs : 1;
|
||||
uint32_t shts : 2;
|
||||
uint32_t nssro : 1;
|
||||
uint32_t pp : 1;
|
||||
uint32_t st : 1;
|
||||
uint32_t __reserved : 25;
|
||||
};
|
||||
static_assert(sizeof(CSTS) == sizeof(uint32_t));
|
||||
|
||||
struct AQA
|
||||
{
|
||||
uint32_t asqs : 12;
|
||||
uint32_t __reserved0 : 4;
|
||||
uint32_t acqs : 12;
|
||||
uint32_t __reserved1 : 4;
|
||||
};
|
||||
static_assert(sizeof(AQA) == sizeof(uint32_t));
|
||||
|
||||
// BAR0
|
||||
struct ControllerRegisters
|
||||
{
|
||||
CAP cap;
|
||||
VS vs;
|
||||
uint32_t intms;
|
||||
uint32_t intmc;
|
||||
CC cc;
|
||||
uint8_t __reserved0[4];
|
||||
CSTS csts;
|
||||
uint32_t nssr;
|
||||
AQA aqa;
|
||||
uint64_t asq;
|
||||
uint64_t acq;
|
||||
|
||||
static constexpr uint32_t SQ0TDBL = 0x1000;
|
||||
};
|
||||
static_assert(sizeof(ControllerRegisters) == 0x38);
|
||||
|
||||
struct DoorbellRegisters
|
||||
{
|
||||
uint32_t sq_tail;
|
||||
uint32_t cq_head;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CompletionQueueEntry
|
||||
{
|
||||
uint32_t dontcare[3];
|
||||
uint16_t cid;
|
||||
uint16_t sts;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CompletionQueueEntry) == 16);
|
||||
|
||||
struct DataPtr
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint64_t prp1;
|
||||
uint64_t prp2;
|
||||
};
|
||||
uint8_t sgl1[16];
|
||||
};
|
||||
};
|
||||
|
||||
struct CommandGeneric
|
||||
{
|
||||
uint32_t nsid;
|
||||
uint32_t cdw2;
|
||||
uint32_t cdw3;
|
||||
uint64_t mptr;
|
||||
DataPtr dptr;
|
||||
uint32_t cdw10;
|
||||
uint32_t cdw11;
|
||||
uint32_t cdw12;
|
||||
uint32_t cdw13;
|
||||
uint32_t cdw14;
|
||||
uint32_t cdw15;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CommandGeneric) == 15 * sizeof(uint32_t));
|
||||
|
||||
struct CommandIdentify
|
||||
{
|
||||
uint32_t nsid;
|
||||
uint64_t __reserved0[2];
|
||||
DataPtr dptr;
|
||||
// dword 10
|
||||
uint8_t cns;
|
||||
uint8_t __reserved1;
|
||||
uint16_t cntid;
|
||||
// dword 11
|
||||
uint16_t cnsid;
|
||||
uint8_t __reserved2;
|
||||
uint8_t csi;
|
||||
// dword 12-15
|
||||
uint32_t __reserved3[4];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CommandIdentify) == 15 * sizeof(uint32_t));
|
||||
|
||||
struct CommandCreateCQ
|
||||
{
|
||||
uint32_t __reserved0;
|
||||
uint64_t __reserved1[2];
|
||||
DataPtr dptr;
|
||||
// dword 10
|
||||
uint16_t qid;
|
||||
uint16_t qsize;
|
||||
// dword 11
|
||||
uint16_t pc : 1;
|
||||
uint16_t ien : 1;
|
||||
uint16_t __reserved2 : 14;
|
||||
uint16_t iv;
|
||||
// dword 12-15
|
||||
uint32_t __reserved4[4];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CommandCreateCQ) == 15 * sizeof(uint32_t));
|
||||
|
||||
struct CommandCreateSQ
|
||||
{
|
||||
uint32_t __reserved0;
|
||||
uint64_t __reserved1[2];
|
||||
DataPtr dptr;
|
||||
// dword 10
|
||||
uint16_t qid;
|
||||
uint16_t qsize;
|
||||
// dword 11
|
||||
uint16_t pc : 1;
|
||||
uint16_t qprio : 2;
|
||||
uint16_t __reserved2 : 13;
|
||||
uint16_t cqid;
|
||||
// dword 12
|
||||
uint16_t nvmsetid;
|
||||
uint16_t __reserved4;
|
||||
// dword 13-15
|
||||
uint32_t __reserved5[3];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CommandCreateSQ) == 15 * sizeof(uint32_t));
|
||||
|
||||
|
||||
struct CommandRead
|
||||
{
|
||||
uint32_t nsid;
|
||||
uint64_t __reserved0;
|
||||
uint64_t mptr;
|
||||
DataPtr dptr;
|
||||
// dword 10-11
|
||||
uint64_t slba;
|
||||
// dword 12
|
||||
uint16_t nlb;
|
||||
uint16_t __reserved1;
|
||||
// dword 13-15
|
||||
uint32_t __reserved2[3];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(CommandRead) == 15 * sizeof(uint32_t));
|
||||
|
||||
struct SubmissionQueueEntry
|
||||
{
|
||||
uint8_t opc;
|
||||
uint8_t fuse : 2;
|
||||
uint8_t __reserved : 4;
|
||||
uint8_t psdt : 2;
|
||||
uint16_t cid;
|
||||
union
|
||||
{
|
||||
CommandGeneric generic;
|
||||
CommandIdentify identify;
|
||||
CommandCreateCQ create_cq;
|
||||
CommandCreateSQ create_sq;
|
||||
CommandRead read;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(SubmissionQueueEntry) == 64);
|
||||
|
||||
enum OPC : uint8_t
|
||||
{
|
||||
OPC_ADMIN_CREATE_SQ = 0x01,
|
||||
OPC_ADMIN_CREATE_CQ = 0x05,
|
||||
OPC_ADMIN_IDENTIFY = 0x06,
|
||||
OPC_IO_WRITE = 0x01,
|
||||
OPC_IO_READ = 0x02,
|
||||
};
|
||||
|
||||
enum CNS : uint8_t
|
||||
{
|
||||
CNS_INDENTIFY_NAMESPACE = 0x00,
|
||||
CNS_INDENTIFY_CONTROLLER = 0x01,
|
||||
CNS_INDENTIFY_ACTIVE_NAMESPACES = 0x02,
|
||||
};
|
||||
|
||||
struct NamespaceIdentify
|
||||
{
|
||||
uint64_t nsze;
|
||||
uint64_t ncap;
|
||||
uint64_t nuse;
|
||||
uint8_t nsfeat;
|
||||
uint8_t nlbaf;
|
||||
uint8_t flbas;
|
||||
uint8_t mc;
|
||||
uint8_t dpc;
|
||||
uint8_t dps;
|
||||
uint8_t nmic;
|
||||
uint8_t rescap;
|
||||
uint8_t fpi;
|
||||
uint8_t dlfeat;
|
||||
uint16_t nawun;
|
||||
uint16_t nawupf;
|
||||
uint16_t nacwu;
|
||||
uint16_t nabsn;
|
||||
uint16_t nabo;
|
||||
uint16_t nabspf;
|
||||
uint16_t noiob;
|
||||
uint64_t nvmcap[2];
|
||||
uint16_t npwg;
|
||||
uint16_t npwa;
|
||||
uint16_t npdg;
|
||||
uint16_t npda;
|
||||
uint16_t nows;
|
||||
uint16_t mssrl;
|
||||
uint32_t mcl;
|
||||
uint8_t msrc;
|
||||
uint8_t __reserved0[11];
|
||||
uint32_t adagrpid;
|
||||
uint8_t __reserved1[3];
|
||||
uint8_t nsattr;
|
||||
uint16_t nvmsetid;
|
||||
uint16_t endgid;
|
||||
uint64_t nguid[2];
|
||||
uint64_t eui64;
|
||||
uint32_t lbafN[64];
|
||||
uint8_t vendor_specific[3712];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(NamespaceIdentify) == 0x1000);
|
||||
|
||||
}
|
||||
41
kernel/include/kernel/Storage/NVMe/Namespace.h
Normal file
41
kernel/include/kernel/Storage/NVMe/Namespace.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/Storage/StorageDevice.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class NVMeController;
|
||||
|
||||
class NVMeNamespace : public StorageDevice
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<NVMeNamespace>> create(NVMeController&, uint32_t nsid, uint64_t block_count, uint32_t block_size);
|
||||
|
||||
virtual uint32_t sector_size() const override { return m_block_size; }
|
||||
virtual uint64_t total_size() const override { return m_block_size * m_block_count; }
|
||||
|
||||
virtual dev_t rdev() const override { return m_rdev; }
|
||||
virtual BAN::StringView name() const { return m_name; }
|
||||
|
||||
private:
|
||||
NVMeNamespace(NVMeController&, uint32_t nsid, uint64_t block_count, uint32_t block_size);
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
virtual BAN::ErrorOr<void> read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan) override;
|
||||
virtual BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan) override;
|
||||
|
||||
private:
|
||||
NVMeController& m_controller;
|
||||
BAN::UniqPtr<DMARegion> m_dma_region;
|
||||
|
||||
const uint32_t m_nsid;
|
||||
const uint32_t m_block_size;
|
||||
const uint64_t m_block_count;
|
||||
|
||||
char m_name[10] {};
|
||||
const dev_t m_rdev;
|
||||
};
|
||||
|
||||
}
|
||||
37
kernel/include/kernel/Storage/NVMe/Queue.h
Normal file
37
kernel/include/kernel/Storage/NVMe/Queue.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/Semaphore.h>
|
||||
#include <kernel/Storage/NVMe/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class NVMeQueue : public Interruptable
|
||||
{
|
||||
public:
|
||||
NVMeQueue(BAN::UniqPtr<Kernel::DMARegion>&& cq, BAN::UniqPtr<Kernel::DMARegion>&& sq, volatile NVMe::DoorbellRegisters& db, uint32_t qdepth, uint8_t irq);
|
||||
|
||||
uint16_t submit_command(NVMe::SubmissionQueueEntry& sqe);
|
||||
|
||||
virtual void handle_irq() final override;
|
||||
|
||||
private:
|
||||
SpinLock m_lock;
|
||||
BAN::UniqPtr<Kernel::DMARegion> m_completion_queue;
|
||||
BAN::UniqPtr<Kernel::DMARegion> m_submission_queue;
|
||||
volatile NVMe::DoorbellRegisters& m_doorbell;
|
||||
const uint32_t m_qdepth;
|
||||
uint32_t m_sq_tail { 0 };
|
||||
uint32_t m_cq_head { 0 };
|
||||
uint16_t m_cq_valid_phase { 1 };
|
||||
|
||||
Semaphore m_semaphore;
|
||||
volatile uint16_t m_status;
|
||||
volatile bool m_done { false };
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user