diff --git a/nvmed/src/main.rs b/nvmed/src/main.rs index d8c911aad5..daaa633fe7 100644 --- a/nvmed/src/main.rs +++ b/nvmed/src/main.rs @@ -1,4 +1,3 @@ -//#![deny(warnings)] #![feature(asm)] extern crate bitflags; @@ -6,25 +5,17 @@ extern crate spin; extern crate syscall; use std::{env, usize}; +use std::fs::File; +use std::io::{ErrorKind, Read, Write}; +use std::os::unix::io::{RawFd, FromRawFd}; - - -use syscall::{EVENT_READ, PHYSMAP_NO_CACHE, PHYSMAP_WRITE, Event, Packet, Result, Scheme}; +use syscall::{EVENT_READ, PHYSMAP_NO_CACHE, PHYSMAP_WRITE, Event, Packet, Result, SchemeBlockMut}; use self::nvme::Nvme; +use self::scheme::DiskScheme; mod nvme; - -/* -fn create_scheme_fallback<'a>(name: &'a str, fallback: &'a str) -> Result<(&'a str, RawFd)> { - if let Ok(fd) = syscall::open(&format!(":{}", name), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK) { - Ok((name, fd)) - } else { - syscall::open(&format!(":{}", fallback), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK) - .map(|fd| (fallback, fd)) - } -} -*/ +mod scheme; fn main() { let mut args = env::args().skip(1); @@ -45,48 +36,84 @@ fn main() { //TODO: Figure out correct size of mapping, not just 256 * 1024 let address = unsafe { syscall::physmap(bar, 256 * 1024, PHYSMAP_WRITE | PHYSMAP_NO_CACHE).expect("nvmed: failed to map address") }; { - let mut nvme = Nvme::new(address).expect("nvmed: failed to allocate queues"); - unsafe { nvme.init(); } - /* - let (_scheme_name, socket_fd) = create_scheme_fallback("disk", &name).expect("nvmed: failed to create disk scheme"); - let mut socket = unsafe { File::from_raw_fd(socket_fd) }; - syscall::fevent(socket_fd, EVENT_READ).expect("nvmed: failed to fevent disk scheme"); + let event_fd = syscall::open("event:", syscall::O_RDWR | syscall::O_CLOEXEC) + .expect("nvmed: failed to open event queue"); + let mut event_file = unsafe { File::from_raw_fd(event_fd as RawFd) }; - let mut irq_file = File::open(&format!("irq:{}", irq)).expect("nvmed: failed to open irq file"); - let irq_fd = irq_file.as_raw_fd(); - syscall::fevent(irq_fd, EVENT_READ).expect("nvmed: failed to fevent irq file"); + let irq_fd = syscall::open( + &format!("irq:{}", irq), + syscall::O_RDWR | syscall::O_NONBLOCK | syscall::O_CLOEXEC + ).expect("nvmed: failed to open irq file"); + syscall::write(event_fd, &syscall::Event { + id: irq_fd, + flags: syscall::EVENT_READ, + data: 0, + }).expect("nvmed: failed to watch irq file events"); + let mut irq_file = unsafe { File::from_raw_fd(irq_fd as RawFd) }; - let mut event_file = File::open("event:").expect("nvmed: failed to open event file"); - - let scheme = DiskScheme::new(nvme::disks(address, &name)); + let scheme_name = format!("disk/{}", name); + let socket_fd = syscall::open( + &format!(":{}", scheme_name), + syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK | syscall::O_CLOEXEC + ).expect("nvmed: failed to create disk scheme"); + syscall::write(event_fd, &syscall::Event { + id: socket_fd, + flags: syscall::EVENT_READ, + data: 1, + }).expect("nvmed: failed to watch disk scheme events"); + let mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) }; syscall::setrens(0, 0).expect("nvmed: failed to enter null namespace"); - loop { + let mut nvme = Nvme::new(address).expect("nvmed: failed to allocate driver data"); + let namespaces = unsafe { nvme.init() }; + let mut scheme = DiskScheme::new(scheme_name, nvme, namespaces); + let mut todo = Vec::new(); + 'events: loop { let mut event = Event::default(); - if event_file.read(&mut event).expect("nvmed: failed to read event file") == 0 { + if event_file.read(&mut event).expect("nvmed: failed to read event queue") == 0 { break; } - if event.id == socket_fd { - loop { - let mut packet = Packet::default(); - if socket.read(&mut packet).expect("nvmed: failed to read disk scheme") == 0 { - break; + + match event.data { + 0 => { + let mut irq = [0; 8]; + if irq_file.read(&mut irq).expect("nvmed: failed to read irq file") >= irq.len() { + if scheme.irq() { + irq_file.write(&irq).expect("nvmed: failed to write irq file"); + } } - scheme.handle(&mut packet); - socket.write(&mut packet).expect("nvmed: failed to write disk scheme"); + }, + 1 => loop { + let mut packet = Packet::default(); + match socket_file.read(&mut packet) { + Ok(0) => break 'events, + Ok(_) => (), + Err(err) => match err.kind() { + ErrorKind::WouldBlock => break, + _ => Err(err).expect("nvmed: failed to read disk scheme"), + } + } + todo.push(packet); + }, + unknown => { + panic!("nvmed: unknown event data {}", unknown); + }, + } + + let mut i = 0; + while i < todo.len() { + if let Some(a) = scheme.handle(&todo[i]) { + let mut packet = todo.remove(i); + packet.a = a; + socket_file.write(&packet).expect("nvmed: failed to write disk scheme"); + } else { + i += 1; } - } else if event.id == irq_fd { - let mut irq = [0; 8]; - if irq_file.read(&mut irq).expect("nvmed: failed to read irq file") >= irq.len() { - //TODO : Test for IRQ - //irq_file.write(&irq).expect("nvmed: failed to write irq file"); - } - } else { - println!("Unknown event {}", event.id); } } - */ + + //TODO: destroy NVMe stuff } unsafe { let _ = syscall::physunmap(address); } } diff --git a/nvmed/src/nvme.rs b/nvmed/src/nvme.rs index 46a32cdda7..8bce947fce 100644 --- a/nvmed/src/nvme.rs +++ b/nvmed/src/nvme.rs @@ -1,6 +1,7 @@ -use std::{mem, thread}; +use std::thread; +use std::collections::BTreeMap; use syscall::io::{Dma, Io, Mmio}; -use syscall::error::Result; +use syscall::error::{Error, Result, EINVAL}; #[derive(Clone, Copy)] #[repr(packed)] @@ -124,7 +125,7 @@ impl NvmeCmd { } } - pub fn io_read(cid: u16, nsid: u32, lba: u64, count: u16, ptr: usize) -> Self { + pub fn io_read(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self { Self { opcode: 2, flags: 1 << 6, @@ -132,17 +133,17 @@ impl NvmeCmd { nsid: nsid, _rsvd: 0, mptr: 0, - dptr: [ptr as u64, (count as u64) << 9], + dptr: [ptr0, ptr1], cdw10: lba as u32, cdw11: (lba >> 32) as u32, - cdw12: count.saturating_sub(1) as u32, //TODO: Prevent count of 0 + cdw12: blocks_1 as u32, cdw13: 0, cdw14: 0, cdw15: 0, } } - pub fn io_write(cid: u16, nsid: u32, lba: u64, count: u16, ptr: usize) -> Self { + pub fn io_write(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self { Self { opcode: 1, flags: 1 << 6, @@ -150,10 +151,10 @@ impl NvmeCmd { nsid: nsid, _rsvd: 0, mptr: 0, - dptr: [ptr as u64, (count as u64) << 9], + dptr: [ptr0, ptr1], cdw10: lba as u32, cdw11: (lba >> 32) as u32, - cdw12: count.saturating_sub(1) as u32, //TODO: Prevent count of 0 + cdw12: blocks_1 as u32, cdw13: 0, cdw14: 0, cdw15: 0, @@ -261,10 +262,18 @@ impl NvmeCompQueue { } } +pub struct NvmeNamespace { + pub id: u32, + pub blocks: u64, + pub block_size: u64, +} + pub struct Nvme { regs: &'static mut NvmeRegs, submission_queues: [NvmeCmdQueue; 2], completion_queues: [NvmeCompQueue; 2], + buffer: Dma<[u8; 512 * 4096]>, // 2MB of buffer + buffer_prp: Dma<[u64; 512]>, // 4KB of PRP for the buffer } impl Nvme { @@ -273,6 +282,8 @@ impl Nvme { regs: unsafe { &mut *(address as *mut NvmeRegs) }, submission_queues: [NvmeCmdQueue::new()?, NvmeCmdQueue::new()?], completion_queues: [NvmeCompQueue::new()?, NvmeCompQueue::new()?], + buffer: Dma::zeroed()?, + buffer_prp: Dma::zeroed()?, }) } @@ -292,7 +303,11 @@ impl Nvme { self.doorbell(2 * (qid as usize) + 1).write(head as u32) } - pub unsafe fn init(&mut self) { + pub unsafe fn init(&mut self) -> BTreeMap { + for i in 0..self.buffer_prp.len() { + self.buffer_prp[i] = (self.buffer.physical() + i * 4096) as u64; + } + println!(" - CAPS: {:X}", self.regs.cap.read()); println!(" - VS: {:X}", self.regs.vs.read()); println!(" - CC: {:X}", self.regs.cc.read()); @@ -317,7 +332,7 @@ impl Nvme { { let asq = &self.submission_queues[0]; let acq = &self.completion_queues[0]; - self.regs.aqa.write(((acq.data.len() as u32) << 16) | (asq.data.len() as u32)); + self.regs.aqa.write(((acq.data.len() as u32 - 1) << 16) | (asq.data.len() as u32 - 1)); self.regs.asq.write(asq.data.physical() as u64); self.regs.acq.write(acq.data.physical() as u64); @@ -337,6 +352,7 @@ impl Nvme { } { + //TODO: Use buffer let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap(); println!(" - Attempting to identify controller"); @@ -391,6 +407,7 @@ impl Nvme { let mut nsids = Vec::new(); { + //TODO: Use buffer let data: Dma<[u32; 1024]> = Dma::zeroed().unwrap(); println!(" - Attempting to retrieve namespace ID list"); @@ -422,7 +439,9 @@ impl Nvme { } } + let mut namespaces = BTreeMap::new(); for &nsid in nsids.iter() { + //TODO: Use buffer let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap(); println!(" - Attempting to identify namespace {}", nsid); @@ -454,6 +473,12 @@ impl Nvme { println!(" - Capacity: {}", capacity); //TODO: Read block size + + namespaces.insert(nsid, NvmeNamespace { + id: nsid, + blocks: size, + block_size: 512, // TODO + }); } for io_qid in 1..self.completion_queues.len() { @@ -468,7 +493,7 @@ impl Nvme { let queue = &mut self.submission_queues[qid]; let cid = queue.i as u16; let entry = NvmeCmd::create_io_completion_queue( - cid, io_qid as u16, ptr, len as u16 + cid, io_qid as u16, ptr, (len - 1) as u16 ); let tail = queue.submit(entry); self.submission_queue_tail(qid as u16, tail as u16); @@ -496,7 +521,7 @@ impl Nvme { let cid = queue.i as u16; //TODO: Get completion queue ID through smarter mechanism let entry = NvmeCmd::create_io_submission_queue( - cid, io_qid as u16, ptr, len as u16, io_qid as u16 + cid, io_qid as u16, ptr, (len - 1) as u16, io_qid as u16 ); let tail = queue.submit(entry); self.submission_queue_tail(qid as u16, tail as u16); @@ -510,5 +535,113 @@ impl Nvme { self.completion_queue_head(qid as u16, head as u16); } } + + namespaces + } + + unsafe fn namespace_rw(&mut self, nsid: u32, lba: u64, blocks_1: u16, write: bool) -> Result<()> { + let (ptr0, ptr1) = if blocks_1 == 0 { + (self.buffer_prp[0], 0) + } else if blocks_1 == 1 { + (self.buffer_prp[0], self.buffer_prp[1]) + } else { + (self.buffer_prp[0], (self.buffer_prp.physical() + 8) as u64) + }; + + { + let qid = 1; + let queue = &mut self.submission_queues[qid]; + let cid = queue.i as u16; + //TODO: Get completion queue ID through smarter mechanism + let entry = if write { + NvmeCmd::io_write( + cid, nsid, lba, blocks_1, ptr0, ptr1 + ) + } else { + NvmeCmd::io_read( + cid, nsid, lba, blocks_1, ptr0, ptr1 + ) + }; + let tail = queue.submit(entry); + self.submission_queue_tail(qid as u16, tail as u16); + } + + { + let qid = 1; + let queue = &mut self.completion_queues[qid]; + let (head, entry) = queue.complete_spin(); + //TODO: Handle errors + self.completion_queue_head(qid as u16, head as u16); + } + + Ok(()) + } + + pub unsafe fn namespace_read(&mut self, nsid: u32, lba: u64, buf: &mut [u8]) -> Result> { + //TODO: Use interrupts + + //TODO: Get real block size + let block_size = 512; + + //TODO: Support this + if buf.len() % block_size != 0 { + return Err(Error::new(EINVAL)); + } + + //TODO: Support this + if buf.len() > self.buffer.len() { + return Err(Error::new(EINVAL)); + } + + let blocks = buf.len() / block_size; + + if blocks == 0 { + return Ok(Some(0)); + } + + //TODO: Support this + if blocks > 0x1_0000 { + return Err(Error::new(EINVAL)); + } + + self.namespace_rw(nsid, lba, (blocks - 1) as u16, false)?; + + buf.copy_from_slice(&self.buffer[..buf.len()]); + + Ok(Some(buf.len())) + } + + pub unsafe fn namespace_write(&mut self, nsid: u32, lba: u64, buf: &[u8]) -> Result> { + //TODO: Use interrupts + + //TODO: Get real block size + let block_size = 512; + + //TODO: Support this + if buf.len() % block_size != 0 { + return Err(Error::new(EINVAL)); + } + + //TODO: Support this + if buf.len() > self.buffer.len() { + return Err(Error::new(EINVAL)); + } + + let blocks = buf.len() / block_size; + + if blocks == 0 { + return Ok(Some(0)); + } + + //TODO: Support this + if blocks > 0x1_0000 { + return Err(Error::new(EINVAL)); + } + + self.buffer[..buf.len()].copy_from_slice(buf); + + self.namespace_rw(nsid, lba, (blocks - 1) as u16, true)?; + + Ok(Some(buf.len())) } } diff --git a/nvmed/src/scheme.rs b/nvmed/src/scheme.rs new file mode 100644 index 0000000000..58edb0dae2 --- /dev/null +++ b/nvmed/src/scheme.rs @@ -0,0 +1,221 @@ +use std::collections::BTreeMap; +use std::{cmp, str}; +use std::fmt::Write; +use std::io::Read; +use syscall::{ + Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, Result, + Io, SchemeBlockMut, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY, + O_STAT, SEEK_CUR, SEEK_END, SEEK_SET}; + +use crate::nvme::{Nvme, NvmeNamespace}; + +#[derive(Clone)] +enum Handle { + List(Vec, usize), + Disk(u32, usize) +} + +pub struct DiskScheme { + scheme_name: String, + nvme: Nvme, + disks: BTreeMap, + handles: BTreeMap, + next_id: usize +} + +impl DiskScheme { + pub fn new(scheme_name: String, nvme: Nvme, disks: BTreeMap) -> DiskScheme { + DiskScheme { + scheme_name, + nvme, + disks, + handles: BTreeMap::new(), + next_id: 0 + } + } +} + +impl DiskScheme { + pub fn irq(&mut self) -> bool { + // TODO: implement + false + } +} + +impl SchemeBlockMut for DiskScheme { + fn open(&mut self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result> { + if uid == 0 { + let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_matches('/'); + if path_str.is_empty() { + if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT { + let mut list = String::new(); + + for (nsid, _disk) in self.disks.iter() { + write!(list, "{}\n", nsid).unwrap(); + } + + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::List(list.into_bytes(), 0)); + Ok(Some(id)) + } else { + Err(Error::new(EISDIR)) + } + } else { + let nsid = path_str.parse::().or(Err(Error::new(ENOENT)))?; + + if self.disks.contains_key(&nsid) { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::Disk(nsid, 0)); + Ok(Some(id)) + } else { + Err(Error::new(ENOENT)) + } + } + } else { + Err(Error::new(EACCES)) + } + } + + fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { + if ! buf.is_empty() { + return Err(Error::new(EINVAL)); + } + + let new_handle = { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + let new_id = self.next_id; + self.next_id += 1; + self.handles.insert(new_id, new_handle); + Ok(Some(new_id)) + } + + fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result> { + match *self.handles.get(&id).ok_or(Error::new(EBADF))? { + Handle::List(ref data, _) => { + stat.st_mode = MODE_DIR; + stat.st_size = data.len() as u64; + Ok(Some(0)) + }, + Handle::Disk(number, _) => { + let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; + stat.st_mode = MODE_FILE; + stat.st_size = disk.blocks * disk.block_size; + Ok(Some(0)) + } + } + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + let mut i = 0; + + let scheme_name = self.scheme_name.as_bytes(); + let mut j = 0; + while i < buf.len() && j < scheme_name.len() { + buf[i] = scheme_name[j]; + i += 1; + j += 1; + } + + if i < buf.len() { + buf[i] = b':'; + i += 1; + } + + match *handle { + Handle::List(_, _) => (), + Handle::Disk(number, _) => { + let number_str = format!("{}", number); + let number_bytes = number_str.as_bytes(); + j = 0; + while i < buf.len() && j < number_bytes.len() { + buf[i] = number_bytes[j]; + i += 1; + j += 1; + } + } + } + + Ok(Some(i)) + } + + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { + match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::List(ref mut handle, ref mut size) => { + let count = (&handle[*size..]).read(buf).unwrap(); + *size += count; + Ok(Some(count)) + }, + Handle::Disk(number, ref mut size) => { + let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; + let block_size = disk.block_size; + if let Some(count) = unsafe { + self.nvme.namespace_read(disk.id, (*size as u64)/block_size, buf)? + } { + *size += count; + Ok(Some(count)) + } else { + Ok(None) + } + } + } + } + + fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::List(_, _) => { + Err(Error::new(EBADF)) + }, + Handle::Disk(number, ref mut size) => { + let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; + let block_size = disk.block_size; + if let Some(count) = unsafe { + self.nvme.namespace_write(disk.id, (*size as u64)/block_size, buf)? + } { + *size += count; + Ok(Some(count)) + } else { + Ok(None) + } + } + } + } + + fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result> { + match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::List(ref mut handle, ref mut size) => { + let len = handle.len() as usize; + *size = match whence { + SEEK_SET => cmp::min(len, pos), + SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(Some(*size)) + }, + Handle::Disk(number, ref mut size) => { + let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; + let len = (disk.blocks * disk.block_size) as usize; + *size = match whence { + SEEK_SET => cmp::min(len, pos), + SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(Some(*size)) + } + } + } + + fn close(&mut self, id: usize) -> Result> { + self.handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0))) + } +}