dev/block: NVMe drive enumeration
This commit is contained in:
parent
506476e9c3
commit
352c68e31e
@ -16,6 +16,7 @@ device-api = { path = "lib/device-api", features = ["derive"] }
|
|||||||
kernel-util = { path = "lib/kernel-util" }
|
kernel-util = { path = "lib/kernel-util" }
|
||||||
memtables = { path = "lib/memtables" }
|
memtables = { path = "lib/memtables" }
|
||||||
vmalloc = { path = "lib/vmalloc" }
|
vmalloc = { path = "lib/vmalloc" }
|
||||||
|
device-api-macros = { path = "lib/device-api/macros" }
|
||||||
|
|
||||||
atomic_enum = "0.2.0"
|
atomic_enum = "0.2.0"
|
||||||
bitflags = "2.3.3"
|
bitflags = "2.3.3"
|
||||||
|
@ -7,8 +7,8 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||||
macros = { path = "macros", optional = true }
|
device-api-macros = { path = "macros", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
derive = ["macros"]
|
derive = ["device-api-macros"]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "macros"
|
name = "device-api-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
36
src/debug.rs
36
src/debug.rs
@ -200,21 +200,35 @@ pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new();
|
|||||||
|
|
||||||
/// Prints a hex-dump of a slice, appending a virtual address offset to the output
|
/// Prints a hex-dump of a slice, appending a virtual address offset to the output
|
||||||
pub fn hex_dump(level: LogLevel, addr_offset: usize, data: &[u8]) {
|
pub fn hex_dump(level: LogLevel, addr_offset: usize, data: &[u8]) {
|
||||||
for (i, b) in data.iter().enumerate() {
|
const WINDOW_SIZE: usize = 16;
|
||||||
if i % 16 == 0 {
|
let window_count = (data.len() + WINDOW_SIZE) / WINDOW_SIZE;
|
||||||
log_print_raw!(level, "{:X}: ", addr_offset + i)
|
|
||||||
|
for iw in 0..window_count {
|
||||||
|
let off = iw * WINDOW_SIZE;
|
||||||
|
let len = core::cmp::min(data.len() - off, WINDOW_SIZE);
|
||||||
|
let window = &data[off..off + len];
|
||||||
|
|
||||||
|
log_print_raw!(level, "{:04X}: ", addr_offset + off);
|
||||||
|
for i in 0..WINDOW_SIZE {
|
||||||
|
if i < window.len() {
|
||||||
|
log_print_raw!(level, "{:02X}", window[i]);
|
||||||
|
} else {
|
||||||
|
log_print_raw!(level, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if i % 2 == 1 {
|
||||||
|
log_print_raw!(level, " ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_print_raw!(level, "{:02X}", b);
|
for &ch in window {
|
||||||
|
if ch.is_ascii_graphic() || ch == b' ' {
|
||||||
if i % 16 == 15 {
|
log_print_raw!(level, "{}", ch as char);
|
||||||
log_print_raw!(level, "\n");
|
} else {
|
||||||
} else if i % 2 == 1 {
|
log_print_raw!(level, ".");
|
||||||
log_print_raw!(level, " ");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if data.len() % 16 != 0 {
|
|
||||||
log_print_raw!(level, "\n");
|
log_print_raw!(level, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ pub trait PciConfigurationSpace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Locates a capability within this configuration space
|
/// Locates a capability within this configuration space
|
||||||
fn capability<'s, C: PciCapability>(&'s self) -> Option<C::CapabilityData<'s, Self>> {
|
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
|
||||||
self.capability_iter().find_map(|(id, offset)| {
|
self.capability_iter().find_map(|(id, offset)| {
|
||||||
if id == C::ID {
|
if id == C::ID {
|
||||||
Some(C::data(self, offset))
|
Some(C::data(self, offset))
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
|
use tock_registers::{interfaces::Readable, register_structs, registers::ReadOnly, UIntLike};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
device::nvme::queue::PhysicalRegionPage,
|
device::nvme::queue::PhysicalRegionPage,
|
||||||
mem::{address::IntoRaw, PhysicalAddress},
|
mem::{address::IntoRaw, PhysicalAddress},
|
||||||
@ -33,6 +35,22 @@ pub enum ControllerType {
|
|||||||
Administrative,
|
Administrative,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I/O commands
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct IoRead {
|
||||||
|
pub nsid: u32,
|
||||||
|
pub lba: u64,
|
||||||
|
pub count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct IoWrite {
|
||||||
|
pub nsid: u32,
|
||||||
|
pub lba: u64,
|
||||||
|
pub count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
// Requests
|
// Requests
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -41,7 +59,15 @@ pub enum SetFeatureRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct IdentifyControllerRequest {
|
pub struct IdentifyControllerRequest;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct IdentifyActiveNamespaceIdListRequest {
|
||||||
|
pub start_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct IdentifyNamespaceRequest {
|
||||||
pub nsid: u32,
|
pub nsid: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,11 +107,34 @@ pub struct IdentifyControllerResponse {
|
|||||||
pub cntrltype: ControllerType,
|
pub cntrltype: ControllerType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct IdentifyActiveNamespaceIdListResponse {
|
||||||
|
pub entries: [u32; 1024],
|
||||||
|
}
|
||||||
|
|
||||||
|
register_structs! {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub IdentifyNamespaceResponse {
|
||||||
|
(0 => NSZE: ReadOnly<u64>),
|
||||||
|
(8 => _0),
|
||||||
|
(25 => NLBAF: ReadOnly<u8>),
|
||||||
|
(26 => FLBAS: ReadOnly<u8>),
|
||||||
|
(27 => _1),
|
||||||
|
(128 => LBAFS: [ReadOnly<u32>; 64]),
|
||||||
|
(384 => _2),
|
||||||
|
(4096 => @END),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct LbaFormat(u32);
|
||||||
|
|
||||||
impl Command for IdentifyControllerRequest {
|
impl Command for IdentifyControllerRequest {
|
||||||
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
sqe.command.set_opcode(0x06);
|
sqe.command.set_opcode(0x06);
|
||||||
sqe.command_specific[0] = 0x01;
|
sqe.command_specific[0] = 0x01;
|
||||||
sqe.nsid = self.nsid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +142,62 @@ impl Request for IdentifyControllerRequest {
|
|||||||
type Response = IdentifyControllerResponse;
|
type Response = IdentifyControllerResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Command for IdentifyActiveNamespaceIdListRequest {
|
||||||
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
|
sqe.command.set_opcode(0x06);
|
||||||
|
sqe.command_specific[0] = 0x02;
|
||||||
|
sqe.nsid = self.start_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request for IdentifyActiveNamespaceIdListRequest {
|
||||||
|
type Response = IdentifyActiveNamespaceIdListResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command for IdentifyNamespaceRequest {
|
||||||
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
|
sqe.command.set_opcode(0x06);
|
||||||
|
sqe.command_specific[0] = 0x00;
|
||||||
|
sqe.nsid = self.nsid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request for IdentifyNamespaceRequest {
|
||||||
|
type Response = IdentifyNamespaceResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentifyNamespaceResponse {
|
||||||
|
pub fn current_lba_fmt_idx(&self) -> usize {
|
||||||
|
let flbas = self.FLBAS.get();
|
||||||
|
let mut index = flbas & 0xF;
|
||||||
|
if self.NLBAF.get() > 16 {
|
||||||
|
index |= (flbas & 0xE0) >> 1;
|
||||||
|
}
|
||||||
|
index as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lba_fmt(&self, idx: usize) -> Option<LbaFormat> {
|
||||||
|
if idx >= self.NLBAF.get() as usize {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(LbaFormat(self.LBAFS[idx].get()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_lba_count(&self) -> u64 {
|
||||||
|
self.NSZE.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LbaFormat {
|
||||||
|
pub fn lba_data_size(&self) -> Option<u64> {
|
||||||
|
let lbads = (self.0 >> 16) & 0xFF;
|
||||||
|
if lbads < 9 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(1 << lbads)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Command for SetFeatureRequest {
|
impl Command for SetFeatureRequest {
|
||||||
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
sqe.command.set_opcode(0x09);
|
sqe.command.set_opcode(0x09);
|
||||||
@ -111,7 +216,7 @@ impl Command for SetFeatureRequest {
|
|||||||
impl Command for CreateIoCompletionQueue {
|
impl Command for CreateIoCompletionQueue {
|
||||||
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
sqe.command.set_opcode(0x05);
|
sqe.command.set_opcode(0x05);
|
||||||
sqe.data_pointer[1] = PhysicalRegionPage::with_addr(self.data.into_raw());
|
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(self.data);
|
||||||
sqe.command_specific[0] = ((self.size as u32 - 1) << 16) | self.id;
|
sqe.command_specific[0] = ((self.size as u32 - 1) << 16) | self.id;
|
||||||
sqe.command_specific[1] = (self.vector << 16) | 3;
|
sqe.command_specific[1] = (self.vector << 16) | 3;
|
||||||
}
|
}
|
||||||
@ -120,7 +225,7 @@ impl Command for CreateIoCompletionQueue {
|
|||||||
impl Command for CreateIoSubmissionQueue {
|
impl Command for CreateIoSubmissionQueue {
|
||||||
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
sqe.command.set_opcode(0x01);
|
sqe.command.set_opcode(0x01);
|
||||||
sqe.data_pointer[1] = PhysicalRegionPage::with_addr(self.data.into_raw());
|
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(self.data);
|
||||||
sqe.command_specific[0] = ((self.size as u32 - 1) << 16) | self.id;
|
sqe.command_specific[0] = ((self.size as u32 - 1) << 16) | self.id;
|
||||||
// Medium priority
|
// Medium priority
|
||||||
sqe.command_specific[1] = (self.cq_id << 16) | 1;
|
sqe.command_specific[1] = (self.cq_id << 16) | 1;
|
||||||
@ -140,3 +245,27 @@ impl<const N: usize> fmt::Debug for String<N> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Command for IoRead {
|
||||||
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
|
assert!(self.count < 65536);
|
||||||
|
|
||||||
|
sqe.command.set_opcode(0x02);
|
||||||
|
sqe.command_specific[0] = self.lba as u32;
|
||||||
|
sqe.command_specific[1] = (self.lba >> 32) as u32;
|
||||||
|
sqe.command_specific[2] = self.count;
|
||||||
|
sqe.nsid = self.nsid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command for IoWrite {
|
||||||
|
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
|
||||||
|
assert!(self.count < 65536);
|
||||||
|
|
||||||
|
sqe.command.set_opcode(0x01);
|
||||||
|
sqe.command_specific[0] = self.lba as u32;
|
||||||
|
sqe.command_specific[1] = (self.lba >> 32) as u32;
|
||||||
|
sqe.command_specific[2] = self.count;
|
||||||
|
sqe.nsid = self.nsid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
87
src/device/nvme/drive.rs
Normal file
87
src/device/nvme/drive.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use abi::{error::Error, io::DeviceRequest};
|
||||||
|
use alloc::{boxed::Box, format};
|
||||||
|
use vfs::BlockDevice;
|
||||||
|
|
||||||
|
use crate::{device::nvme::command::IdentifyNamespaceRequest, fs::devfs};
|
||||||
|
|
||||||
|
use super::{error::NvmeError, NvmeController};
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct NvmeDrive {
|
||||||
|
controller: &'static NvmeController,
|
||||||
|
nsid: u32,
|
||||||
|
total_lba_count: u64,
|
||||||
|
lba_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NvmeDrive {
|
||||||
|
pub async fn create(
|
||||||
|
controller: &'static NvmeController,
|
||||||
|
nsid: u32,
|
||||||
|
) -> Result<&'static NvmeDrive, NvmeError> {
|
||||||
|
let admin_q = controller.admin_q.get();
|
||||||
|
let identify = admin_q.request(IdentifyNamespaceRequest { nsid })?.await?;
|
||||||
|
|
||||||
|
let current_lba_format_idx = identify.current_lba_fmt_idx();
|
||||||
|
let current_lba_format = identify.lba_fmt(current_lba_format_idx).unwrap();
|
||||||
|
let lba_size = current_lba_format.lba_data_size().unwrap();
|
||||||
|
let total_lba_count = identify.total_lba_count();
|
||||||
|
|
||||||
|
debugln!(
|
||||||
|
"ns = {}, lba = {}B, size = {}M",
|
||||||
|
nsid,
|
||||||
|
lba_size,
|
||||||
|
(total_lba_count * lba_size) / (1024 * 1024)
|
||||||
|
);
|
||||||
|
|
||||||
|
let dev = Box::leak(Box::new(NvmeDrive {
|
||||||
|
controller,
|
||||||
|
nsid,
|
||||||
|
total_lba_count,
|
||||||
|
lba_size,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// TODO add the drive as a block device
|
||||||
|
let node_name = format!("nvme{}n{}", controller.controller_id.get(), nsid);
|
||||||
|
devfs::add_named_block_device(dev, node_name).ok();
|
||||||
|
|
||||||
|
// TODO probe partitions
|
||||||
|
|
||||||
|
Ok(dev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO proper interface for reading/writing blocks
|
||||||
|
// pub async fn read_block(
|
||||||
|
// &self,
|
||||||
|
// lba: u64,
|
||||||
|
// block: &mut PhysicalRefMut<'_, [u8]>,
|
||||||
|
// ) -> Result<(), NvmeError> {
|
||||||
|
// self.controller.read_block(self.nsid, lba, block).await
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub async fn write_block(
|
||||||
|
// &self,
|
||||||
|
// lba: u64,
|
||||||
|
// block: &PhysicalRefMut<'_, [u8]>,
|
||||||
|
// ) -> Result<(), NvmeError> {
|
||||||
|
// self.controller.write_block(self.nsid, lba, block).await
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockDevice for NvmeDrive {
|
||||||
|
fn read(&'static self, _pos: u64, _buf: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&'static self, _pos: u64, _buf: &[u8]) -> Result<usize, Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> Result<u64, Error> {
|
||||||
|
Ok(self.lba_size * self.total_lba_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_request(&self, _req: &mut DeviceRequest) -> Result<(), Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
use core::{mem::size_of, time::Duration};
|
use core::{mem::size_of, time::Duration};
|
||||||
|
|
||||||
use abi::error::Error;
|
use abi::error::Error;
|
||||||
use alloc::vec::Vec;
|
use alloc::{collections::BTreeMap, vec::Vec};
|
||||||
use device_api::{interrupt::MsiHandler, Device};
|
use device_api::{interrupt::MsiHandler, Device};
|
||||||
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
|
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
|
||||||
use tock_registers::{
|
use tock_registers::{
|
||||||
@ -18,13 +18,17 @@ use crate::{
|
|||||||
capability::MsiXCapability, PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
capability::MsiXCapability, PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
||||||
},
|
},
|
||||||
nvme::{
|
nvme::{
|
||||||
command::IdentifyControllerRequest,
|
command::{
|
||||||
|
IdentifyActiveNamespaceIdListRequest, IdentifyControllerRequest, IoRead, IoWrite,
|
||||||
|
},
|
||||||
|
drive::NvmeDrive,
|
||||||
queue::{CompletionQueueEntry, SubmissionQueueEntry},
|
queue::{CompletionQueueEntry, SubmissionQueueEntry},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mem::{
|
mem::{
|
||||||
address::{FromRaw, IntoRaw},
|
address::{AsPhysicalAddress, FromRaw, IntoRaw},
|
||||||
device::{DeviceMemoryIo, DeviceMemoryIoMut},
|
device::{DeviceMemoryIo, DeviceMemoryIoMut},
|
||||||
|
pointer::PhysicalRefMut,
|
||||||
PhysicalAddress,
|
PhysicalAddress,
|
||||||
},
|
},
|
||||||
task::runtime,
|
task::runtime,
|
||||||
@ -39,6 +43,7 @@ use self::{
|
|||||||
use super::bus::pci::{capability::MsiXEntry, FromPciBus, PciDeviceInfo};
|
use super::bus::pci::{capability::MsiXEntry, FromPciBus, PciDeviceInfo};
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
|
mod drive;
|
||||||
mod error;
|
mod error;
|
||||||
mod queue;
|
mod queue;
|
||||||
|
|
||||||
@ -113,6 +118,8 @@ pub struct NvmeController {
|
|||||||
admin_q: OneTimeInit<QueuePair<'static>>,
|
admin_q: OneTimeInit<QueuePair<'static>>,
|
||||||
ioqs: OneTimeInit<Vec<QueuePair<'static>>>,
|
ioqs: OneTimeInit<Vec<QueuePair<'static>>>,
|
||||||
vector_table: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [MsiXEntry]>>,
|
vector_table: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [MsiXEntry]>>,
|
||||||
|
drive_table: IrqSafeSpinlock<BTreeMap<u32, &'static NvmeDrive>>,
|
||||||
|
controller_id: OneTimeInit<usize>,
|
||||||
|
|
||||||
doorbell_shift: usize,
|
doorbell_shift: usize,
|
||||||
}
|
}
|
||||||
@ -120,17 +127,21 @@ pub struct NvmeController {
|
|||||||
impl Regs {
|
impl Regs {
|
||||||
unsafe fn doorbell_ptr(&self, shift: usize, completion: bool, queue_index: usize) -> *mut u32 {
|
unsafe fn doorbell_ptr(&self, shift: usize, completion: bool, queue_index: usize) -> *mut u32 {
|
||||||
let doorbell_base = (self as *const Regs as *mut Regs).addr() + 0x1000;
|
let doorbell_base = (self as *const Regs as *mut Regs).addr() + 0x1000;
|
||||||
let offset = (queue_index << shift) + completion as usize * 4;
|
let offset = ((queue_index << shift) + completion as usize) * 4;
|
||||||
(doorbell_base + offset) as *mut u32
|
(doorbell_base + offset) as *mut u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NvmeController {
|
impl NvmeController {
|
||||||
async fn late_init(&'static self) -> Result<(), NvmeError> {
|
async fn late_init(&'static self) -> Result<(), NvmeError> {
|
||||||
// runtime::spawn(self.poll_task()).expect("Couldn't spawn NVMe poll task");
|
register_nvme_controller(self);
|
||||||
|
|
||||||
let admin_q = self.admin_q.get();
|
let admin_q = self.admin_q.get();
|
||||||
|
|
||||||
infoln!("SETUP");
|
// Identify the controller
|
||||||
|
let _identify = admin_q.request(IdentifyControllerRequest)?.await?;
|
||||||
|
|
||||||
|
// TODO do something with identify_controller
|
||||||
|
|
||||||
// Request a CQ/SQ pair for I/O
|
// Request a CQ/SQ pair for I/O
|
||||||
admin_q
|
admin_q
|
||||||
@ -140,12 +151,7 @@ impl NvmeController {
|
|||||||
// Allocate the queue
|
// Allocate the queue
|
||||||
let (sq_doorbell, cq_doorbell) = unsafe { self.doorbell_pair(1) };
|
let (sq_doorbell, cq_doorbell) = unsafe { self.doorbell_pair(1) };
|
||||||
let io_q =
|
let io_q =
|
||||||
QueuePair::new(0, 32, sq_doorbell, cq_doorbell).map_err(NvmeError::MemoryError)?;
|
QueuePair::new(1, 0, 32, sq_doorbell, cq_doorbell).map_err(NvmeError::MemoryError)?;
|
||||||
|
|
||||||
// Identify the controller
|
|
||||||
let identify = admin_q
|
|
||||||
.request(IdentifyControllerRequest { nsid: 0 })?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Create the queue on the device side
|
// Create the queue on the device side
|
||||||
admin_q
|
admin_q
|
||||||
@ -165,7 +171,86 @@ impl NvmeController {
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
loop {}
|
self.ioqs.init(Vec::from_iter([io_q]));
|
||||||
|
|
||||||
|
// Identify namespaces
|
||||||
|
self.enumerate_namespaces().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn enumerate_namespaces(&'static self) -> Result<(), NvmeError> {
|
||||||
|
let admin_q = self.admin_q.get();
|
||||||
|
|
||||||
|
let namespaces = admin_q
|
||||||
|
.request(IdentifyActiveNamespaceIdListRequest { start_id: 0 })?
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let count = namespaces.entries.iter().position(|&x| x == 0).unwrap();
|
||||||
|
let list = &namespaces.entries[..count];
|
||||||
|
|
||||||
|
for &nsid in list {
|
||||||
|
match NvmeDrive::create(self, nsid).await {
|
||||||
|
Ok(drive) => {
|
||||||
|
self.drive_table.lock().insert(nsid, drive);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
warnln!("Could not create nvme drive, nsid={}: {:?}", nsid, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_block(
|
||||||
|
&'static self,
|
||||||
|
nsid: u32,
|
||||||
|
lba: u64,
|
||||||
|
buffer: &mut PhysicalRefMut<'_, [u8]>,
|
||||||
|
) -> Result<(), NvmeError> {
|
||||||
|
let ioq = &self.ioqs.get()[0];
|
||||||
|
let buffer_address = unsafe { buffer.as_physical_address() };
|
||||||
|
|
||||||
|
debugln!("read nsid={}, lba={:#x}", nsid, lba);
|
||||||
|
let cmd_id = ioq.submit(
|
||||||
|
IoRead {
|
||||||
|
nsid,
|
||||||
|
lba,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
&[buffer_address],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
ioq.wait_for_completion(cmd_id, ()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_block(
|
||||||
|
&'static self,
|
||||||
|
nsid: u32,
|
||||||
|
lba: u64,
|
||||||
|
buffer: &PhysicalRefMut<'_, [u8]>,
|
||||||
|
) -> Result<(), NvmeError> {
|
||||||
|
let ioq = &self.ioqs.get()[0];
|
||||||
|
let buffer_address = unsafe { buffer.as_physical_address() };
|
||||||
|
|
||||||
|
debugln!("write nsid={}, lba={:#x}", nsid, lba);
|
||||||
|
let cmd_id = ioq.submit(
|
||||||
|
IoWrite {
|
||||||
|
nsid,
|
||||||
|
lba,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
&[buffer_address],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
ioq.wait_for_completion(cmd_id, ()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn doorbell_pair(&self, idx: usize) -> (*mut u32, *mut u32) {
|
unsafe fn doorbell_pair(&self, idx: usize) -> (*mut u32, *mut u32) {
|
||||||
@ -177,9 +262,15 @@ impl NvmeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MsiHandler for NvmeController {
|
impl MsiHandler for NvmeController {
|
||||||
fn handle_msi(&self, vector: usize) -> bool {
|
fn handle_msi(&self, _vector: usize) -> bool {
|
||||||
debugln!("handle_msi {}", vector);
|
// TODO check MSI-X pending bits
|
||||||
self.admin_q.get().process_completions() != 0
|
self.admin_q.get().process_completions();
|
||||||
|
if let Some(qs) = self.ioqs.try_get() {
|
||||||
|
for q in qs {
|
||||||
|
q.process_completions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +302,9 @@ impl Device for NvmeController {
|
|||||||
// Setup the admin queue (index 0)
|
// Setup the admin queue (index 0)
|
||||||
let admin_sq_doorbell = unsafe { regs.doorbell_ptr(self.doorbell_shift, false, 0) };
|
let admin_sq_doorbell = unsafe { regs.doorbell_ptr(self.doorbell_shift, false, 0) };
|
||||||
let admin_cq_doorbell = unsafe { regs.doorbell_ptr(self.doorbell_shift, true, 0) };
|
let admin_cq_doorbell = unsafe { regs.doorbell_ptr(self.doorbell_shift, true, 0) };
|
||||||
|
debugln!("sq_doorbell for adminq = {:p}", admin_sq_doorbell);
|
||||||
let admin_q = QueuePair::new(
|
let admin_q = QueuePair::new(
|
||||||
|
0,
|
||||||
0,
|
0,
|
||||||
queue_slots as usize,
|
queue_slots as usize,
|
||||||
admin_sq_doorbell,
|
admin_sq_doorbell,
|
||||||
@ -296,14 +389,26 @@ impl FromPciBus for NvmeController {
|
|||||||
// Disable the controller
|
// Disable the controller
|
||||||
regs.CC.modify(CC::ENABLE::CLEAR);
|
regs.CC.modify(CC::ENABLE::CLEAR);
|
||||||
|
|
||||||
let doorbell_shift = regs.CAP.read(CAP::DSTRD) as usize + 2;
|
let doorbell_shift = regs.CAP.read(CAP::DSTRD) as usize + 1;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
regs: IrqSafeSpinlock::new(regs),
|
regs: IrqSafeSpinlock::new(regs),
|
||||||
admin_q: OneTimeInit::new(),
|
admin_q: OneTimeInit::new(),
|
||||||
ioqs: OneTimeInit::new(),
|
ioqs: OneTimeInit::new(),
|
||||||
vector_table: IrqSafeSpinlock::new(vt),
|
vector_table: IrqSafeSpinlock::new(vt),
|
||||||
|
drive_table: IrqSafeSpinlock::new(BTreeMap::new()),
|
||||||
|
controller_id: OneTimeInit::new(),
|
||||||
doorbell_shift,
|
doorbell_shift,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NVME_CONTROLLERS: IrqSafeSpinlock<Vec<&'static NvmeController>> =
|
||||||
|
IrqSafeSpinlock::new(Vec::new());
|
||||||
|
|
||||||
|
pub fn register_nvme_controller(ctrl: &'static NvmeController) {
|
||||||
|
let mut list = NVME_CONTROLLERS.lock();
|
||||||
|
let id = list.len();
|
||||||
|
list.push(ctrl);
|
||||||
|
ctrl.controller_id.init(id);
|
||||||
|
}
|
||||||
|
@ -50,7 +50,7 @@ pub struct CommandDword0(u32);
|
|||||||
pub struct SubmissionQueueEntry {
|
pub struct SubmissionQueueEntry {
|
||||||
pub command: CommandDword0, // 0
|
pub command: CommandDword0, // 0
|
||||||
pub nsid: u32, // 1
|
pub nsid: u32, // 1
|
||||||
_0: [u32; 2], // 2, 3
|
pub io_data: [u32; 2], // 2, 3
|
||||||
pub metadata_pointer: u64, // 4, 5
|
pub metadata_pointer: u64, // 4, 5
|
||||||
pub data_pointer: [PhysicalRegionPage; 2], // 6, 7, 8, 9
|
pub data_pointer: [PhysicalRegionPage; 2], // 6, 7, 8, 9
|
||||||
pub command_specific: [u32; 6], // 10, 11, 12, 13, 14, 15
|
pub command_specific: [u32; 6], // 10, 11, 12, 13, 14, 15
|
||||||
@ -89,12 +89,13 @@ struct Inner<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO PageBox<T>?
|
// TODO PageBox<T>?
|
||||||
|
#[allow(unused)]
|
||||||
pub struct QueuePair<'a> {
|
pub struct QueuePair<'a> {
|
||||||
base: PhysicalAddress,
|
base: PhysicalAddress,
|
||||||
page_count: usize,
|
page_count: usize,
|
||||||
|
id: u32,
|
||||||
|
|
||||||
vector: usize,
|
vector: usize,
|
||||||
|
|
||||||
sq_base: PhysicalAddress,
|
sq_base: PhysicalAddress,
|
||||||
cq_base: PhysicalAddress,
|
cq_base: PhysicalAddress,
|
||||||
|
|
||||||
@ -110,8 +111,8 @@ impl PhysicalRegionPage {
|
|||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn with_addr(addr: u64) -> Self {
|
pub const fn with_addr(address: PhysicalAddress) -> Self {
|
||||||
Self(addr)
|
Self(address.into_raw())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,13 +241,14 @@ impl<'a, T> Queue<'a, T> {
|
|||||||
wrapped
|
wrapped
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
// pub fn is_empty(&self) -> bool {
|
||||||
self.head == self.tail
|
// self.head == self.tail
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> QueuePair<'a> {
|
impl<'a> QueuePair<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
id: u32,
|
||||||
vector: usize,
|
vector: usize,
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
sq_doorbell: *mut u32,
|
sq_doorbell: *mut u32,
|
||||||
@ -281,6 +283,7 @@ impl<'a> QueuePair<'a> {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
completion_notify: QueueWaker::new(),
|
completion_notify: QueueWaker::new(),
|
||||||
|
|
||||||
|
id,
|
||||||
vector,
|
vector,
|
||||||
base,
|
base,
|
||||||
page_count,
|
page_count,
|
||||||
@ -350,7 +353,7 @@ impl<'a> QueuePair<'a> {
|
|||||||
|
|
||||||
match ranges.len() {
|
match ranges.len() {
|
||||||
1 => {
|
1 => {
|
||||||
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(ranges[0].into_raw());
|
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(ranges[0]);
|
||||||
sqe.data_pointer[1] = PhysicalRegionPage::null();
|
sqe.data_pointer[1] = PhysicalRegionPage::null();
|
||||||
}
|
}
|
||||||
0 => {
|
0 => {
|
||||||
@ -397,7 +400,7 @@ impl<'a> QueuePair<'a> {
|
|||||||
'r: 'a,
|
'r: 'a,
|
||||||
{
|
{
|
||||||
assert_ne!(size_of::<R::Response>(), 0);
|
assert_ne!(size_of::<R::Response>(), 0);
|
||||||
assert!(size_of::<R::Response>() < 0x1000);
|
assert!(size_of::<R::Response>() <= 0x1000);
|
||||||
|
|
||||||
let page = phys::alloc_page().map_err(NvmeError::MemoryError)?;
|
let page = phys::alloc_page().map_err(NvmeError::MemoryError)?;
|
||||||
// TODO PageBox
|
// TODO PageBox
|
||||||
@ -424,8 +427,8 @@ impl<'a> QueuePair<'a> {
|
|||||||
n += 1;
|
n += 1;
|
||||||
|
|
||||||
let sub_queue_id = cmp.sub_queue_id();
|
let sub_queue_id = cmp.sub_queue_id();
|
||||||
// TODO support queues other than admin q
|
// TODO allow several sqs receive completions through one cq?
|
||||||
assert_eq!(sub_queue_id, 0);
|
assert_eq!(sub_queue_id, self.id);
|
||||||
|
|
||||||
let sub_queue_head = cmp.sub_queue_head();
|
let sub_queue_head = cmp.sub_queue_head();
|
||||||
let cmp = *cmp;
|
let cmp = *cmp;
|
||||||
@ -442,7 +445,6 @@ impl<'a> QueuePair<'a> {
|
|||||||
let command_id = cmp.command_id();
|
let command_id = cmp.command_id();
|
||||||
|
|
||||||
if inner.pending.remove(&command_id) {
|
if inner.pending.remove(&command_id) {
|
||||||
debugln!("Insert completion: {}", command_id);
|
|
||||||
inner.completed.insert(command_id, cmp);
|
inner.completed.insert(command_id, cmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use alloc::{format, string::String};
|
|||||||
use kernel_util::util::OneTimeInit;
|
use kernel_util::util::OneTimeInit;
|
||||||
use vfs::{
|
use vfs::{
|
||||||
impls::{read_fn_node, MemoryDirectory},
|
impls::{read_fn_node, MemoryDirectory},
|
||||||
CharDevice, Node, NodeFlags, NodeRef,
|
BlockDevice, CharDevice, Node, NodeFlags, NodeRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::proc::random;
|
use crate::proc::random;
|
||||||
@ -37,7 +37,8 @@ pub fn root() -> &'static NodeRef {
|
|||||||
DEVFS_ROOT.get()
|
DEVFS_ROOT.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _add_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> {
|
/// Adds a character device with a custom name
|
||||||
|
pub fn add_named_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> {
|
||||||
infoln!("Add char device: {}", name);
|
infoln!("Add char device: {}", name);
|
||||||
|
|
||||||
let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS);
|
let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS);
|
||||||
@ -45,6 +46,19 @@ fn _add_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Er
|
|||||||
DEVFS_ROOT.get().add_child(name, node)
|
DEVFS_ROOT.get().add_child(name, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a block device with a custom name
|
||||||
|
pub fn add_named_block_device<S: Into<String>>(
|
||||||
|
dev: &'static dyn BlockDevice,
|
||||||
|
name: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let name = name.into();
|
||||||
|
infoln!("Add block device: {}", name);
|
||||||
|
|
||||||
|
let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS);
|
||||||
|
|
||||||
|
DEVFS_ROOT.get().add_child(name, node)
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a character device to the devfs
|
/// Adds a character device to the devfs
|
||||||
pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Result<(), Error> {
|
pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Result<(), Error> {
|
||||||
static TTY_COUNT: AtomicUsize = AtomicUsize::new(0);
|
static TTY_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
@ -58,7 +72,7 @@ pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Re
|
|||||||
let value = count.fetch_add(1, Ordering::AcqRel);
|
let value = count.fetch_add(1, Ordering::AcqRel);
|
||||||
let name = format!("{}{}", prefix, value);
|
let name = format!("{}{}", prefix, value);
|
||||||
|
|
||||||
_add_char_device(dev, name)
|
add_named_char_device(dev, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds "pseudo"-devices to the filesystem (i.e. /dev/random)
|
/// Adds "pseudo"-devices to the filesystem (i.e. /dev/random)
|
||||||
|
@ -25,7 +25,6 @@ fn setup_root() -> Result<NodeRef, Error> {
|
|||||||
/// initialization has finished.
|
/// initialization has finished.
|
||||||
pub fn kinit() -> Result<(), Error> {
|
pub fn kinit() -> Result<(), Error> {
|
||||||
infoln!("In main");
|
infoln!("In main");
|
||||||
loop {}
|
|
||||||
|
|
||||||
#[cfg(feature = "fb_console")]
|
#[cfg(feature = "fb_console")]
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
strict_provenance,
|
strict_provenance,
|
||||||
slice_ptr_get,
|
slice_ptr_get,
|
||||||
slice_split_once,
|
slice_split_once,
|
||||||
iter_collect_into
|
iter_collect_into,
|
||||||
|
iter_next_chunk,
|
||||||
|
exact_size_is_empty
|
||||||
)]
|
)]
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::new_without_default,
|
clippy::new_without_default,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user