Merge branch 'unify_disk_scheme_handling' into 'master'

Mostly unify disk scheme implementations

See merge request redox-os/drivers!249
This commit is contained in:
Jeremy Soller
2025-03-08 22:45:40 +00:00
21 changed files with 619 additions and 2002 deletions
Generated
+4 -6
View File
@@ -52,7 +52,6 @@ dependencies = [
"log",
"pcid",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
]
@@ -180,7 +179,6 @@ dependencies = [
"fdt 0.2.0-alpha1",
"libredox",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
]
@@ -392,7 +390,9 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
name = "driver-block"
version = "0.1.0"
dependencies = [
"libredox",
"partitionlib",
"redox-scheme 0.3.0",
"redox_syscall",
]
@@ -680,7 +680,6 @@ dependencies = [
"log",
"pcid",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
]
@@ -781,9 +780,9 @@ name = "lived"
version = "0.1.0"
dependencies = [
"anyhow",
"driver-block",
"libredox",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
]
@@ -856,7 +855,6 @@ dependencies = [
"log",
"pcid",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
"smallvec 1.13.2",
@@ -1673,7 +1671,7 @@ dependencies = [
"log",
"pcid",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
"spin",
"static_assertions",
+2 -2
View File
@@ -72,13 +72,13 @@ impl<T: NetworkAdapter> NetworkScheme<T> {
&mut self.adapter
}
/// Process pending and new packets.
/// Process pending and new requests.
///
/// This needs to be called each time there is a new event on the scheme
/// file and each time a new network packet has been received by the
/// driver.
// FIXME maybe split into one method for events on the scheme fd and one
// to call when an irq is received to indicate that blocked packets can
// to call when an irq is received to indicate that blocked requests can
// be processed.
pub fn tick(&mut self) -> io::Result<()> {
// Handle any blocked requests
-1
View File
@@ -14,5 +14,4 @@ common = { path = "../../common" }
driver-block = { path = "../driver-block" }
pcid = { path = "../../pcid" }
libredox = "0.1.3"
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
redox_event = "0.4"
+32 -92
View File
@@ -7,18 +7,14 @@ use std::io::{Read, Write};
use std::os::fd::AsRawFd;
use std::usize;
use common::io::Io;
use driver_block::DiskScheme;
use event::{EventFlags, RawEventQueue};
use pcid_interface::PciFunctionHandle;
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
use syscall::error::{Error, ENODEV};
use log::{error, info};
use syscall::{EAGAIN, EWOULDBLOCK};
use crate::scheme::DiskScheme;
pub mod ahci;
pub mod scheme;
fn main() {
redox_daemon::Daemon::new(daemon).expect("ahcid: failed to daemonize");
@@ -48,18 +44,27 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
let address = unsafe { pcid_handle.map_bar(5) }.ptr.as_ptr() as usize;
{
let (hba_mem, disks) = ahci::disks(address as usize, &name);
let scheme_name = format!("disk.{}", name);
let socket = Socket::nonblock(&scheme_name).expect("ahcid: failed to create disk scheme");
let mut scheme = DiskScheme::new(
scheme_name,
disks
.into_iter()
.enumerate()
.map(|(i, disk)| (i as u32, disk))
.collect(),
);
let mut irq_file = irq.irq_handle("ahcid");
let irq_fd = irq_file.as_raw_fd() as usize;
let mut event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue");
let event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue");
libredox::call::setrens(0, 0).expect("ahcid: failed to enter null namespace");
event_queue
.subscribe(socket.inner().raw(), 1, EventFlags::READ)
.subscribe(scheme.event_handle().raw(), 1, EventFlags::READ)
.expect("ahcid: failed to event scheme socket");
event_queue
.subscribe(irq_fd, 1, EventFlags::READ)
@@ -67,52 +72,10 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
daemon.ready().expect("ahcid: failed to notify parent");
let (hba_mem, disks) = ahci::disks(address as usize, &name);
let mut scheme = DiskScheme::new(scheme_name, hba_mem, disks);
let mut mounted = true;
let mut todo = Vec::new();
while mounted {
let Some(event) = event_queue
.next()
.transpose()
.expect("ahcid: failed to read event file")
else {
break;
};
if event.fd == socket.inner().raw() {
loop {
let sqe = match socket.next_request(SignalBehavior::Interrupt) {
Ok(None) => {
mounted = false;
break;
}
Ok(Some(s)) => {
if let RequestKind::Call(call) = s.kind() {
call
} else {
// TODO: Support e.g. cancellation
continue;
}
}
Err(err) => {
if err.errno == EWOULDBLOCK || err.errno == EAGAIN {
break;
} else {
panic!("ahcid: failed to read disk scheme: {}", err);
}
}
};
if let Some(response) = sqe.handle_scheme_block(&mut scheme) {
// TODO: handle full CQE?
socket
.write_response(response, SignalBehavior::Restart)
.expect("ahcid: failed to write disk scheme");
} else {
todo.push(sqe);
}
}
for event in event_queue {
let event = event.unwrap();
if event.fd == scheme.event_handle().raw() {
scheme.tick().unwrap();
} else if event.fd == irq_fd {
let mut irq = [0; 8];
if irq_file
@@ -120,52 +83,29 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
.expect("ahcid: failed to read irq file")
>= irq.len()
{
if scheme.irq() {
let is = hba_mem.is.read();
if is > 0 {
let pi = hba_mem.pi.read();
let pi_is = pi & is;
for i in 0..hba_mem.ports.len() {
if pi_is & 1 << i > 0 {
let port = &mut hba_mem.ports[i];
let is = port.is.read();
port.is.write(is);
}
}
hba_mem.is.write(is);
irq_file
.write(&irq)
.expect("ahcid: failed to write irq file");
// Handle todos in order to finish previous packets if possible
let mut i = 0;
while i < todo.len() {
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
let _sqe = todo.remove(i);
socket
.write_response(resp, SignalBehavior::Restart)
.expect("ahcid: failed to write disk scheme");
} else {
i += 1;
}
}
scheme.tick().unwrap();
}
}
} else {
error!("Unknown event {}", event.fd);
}
// Handle todos to start new packets if possible
let mut i = 0;
while i < todo.len() {
if let Some(response) = todo[i].handle_scheme_block(&mut scheme) {
let _sqe = todo.remove(i);
socket
.write_response(response, SignalBehavior::Restart)
.expect("ahcid: failed to write disk scheme");
} else {
i += 1;
}
}
if !mounted {
for sqe in todo.drain(..) {
socket
.write_response(
Response::new(&sqe, Err(Error::new(ENODEV))),
SignalBehavior::Restart,
)
.expect("ahcid: failed to write disk scheme");
}
}
}
}
-329
View File
@@ -1,329 +0,0 @@
use std::collections::BTreeMap;
use std::fmt::Write;
use std::str;
use common::io::Io as _;
use driver_block::{Disk, DiskWrapper};
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
use syscall::schemev2::NewFdFlags;
use syscall::{
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
O_STAT,
};
use crate::ahci::hba::HbaMem;
enum Handle {
List(Vec<u8>), // Dir contents buffer
Disk(usize), // Disk index
Partition(usize, u32), // Disk index, partition index
}
pub struct DiskScheme {
scheme_name: String,
hba_mem: &'static mut HbaMem,
disks: Box<[DiskWrapper<Box<dyn Disk>>]>,
handles: BTreeMap<usize, Handle>,
next_id: usize,
}
impl DiskScheme {
pub fn new(
scheme_name: String,
hba_mem: &'static mut HbaMem,
disks: Vec<Box<dyn Disk>>,
) -> DiskScheme {
DiskScheme {
scheme_name,
hba_mem,
disks: disks
.into_iter()
.map(DiskWrapper::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
handles: BTreeMap::new(),
next_id: 0,
}
}
pub fn irq(&mut self) -> bool {
let is = self.hba_mem.is.read();
if is > 0 {
let pi = self.hba_mem.pi.read();
let pi_is = pi & is;
for i in 0..self.hba_mem.ports.len() {
if pi_is & 1 << i > 0 {
let port = &mut self.hba_mem.ports[i];
let is = port.is.read();
port.is.write(is);
}
}
self.hba_mem.is.write(is);
true
} else {
false
}
}
// Checks if any conflicting handles already exist
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
for (_, handle) in self.handles.iter() {
match handle {
Handle::Disk(i) => {
if disk_i == *i {
return Err(Error::new(ENOLCK));
}
}
Handle::Partition(i, p) => {
if disk_i == *i {
match part_i_opt {
Some(part_i) => {
if part_i == *p {
return Err(Error::new(ENOLCK));
}
}
None => {
return Err(Error::new(ENOLCK));
}
}
}
}
_ => (),
}
}
Ok(())
}
}
impl SchemeBlock for DiskScheme {
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<Option<OpenResult>> {
if ctx.uid != 0 {
return Err(Error::new(EACCES));
}
let path_str = path.trim_matches('/');
let handle = if path_str.is_empty() {
if flags & O_DIRECTORY != O_DIRECTORY && flags & O_STAT != O_STAT {
return Err(Error::new(EISDIR));
}
let mut list = String::new();
for (disk_index, disk) in self.disks.iter().enumerate() {
write!(list, "{}\n", disk_index).unwrap();
if disk.pt.is_none() {
continue;
}
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
}
}
Handle::List(list.into_bytes())
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
let disk_id_str = &path_str[..p_pos];
if p_pos + 1 >= path_str.len() {
return Err(Error::new(ENOENT));
}
let part_id_str = &path_str[p_pos + 1..];
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
let disk = self.disks.get(i).ok_or(Error::new(ENOENT))?;
if disk.pt.is_none()
|| disk
.pt
.as_ref()
.unwrap()
.partitions
.get(p as usize)
.is_none()
{
return Err(Error::new(ENOENT));
}
self.check_locks(i, Some(p))?;
Handle::Partition(i, p)
} else {
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
if self.disks.get(i).is_none() {
return Err(Error::new(ENOENT));
}
self.check_locks(i, None)?;
Handle::Disk(i)
};
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, handle);
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
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.size();
stat.st_blksize = disk.block_size();
Ok(Some(0))
}
Handle::Partition(disk_id, part_num) => {
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
let size = {
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
let partition = pt
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
partition.size
};
stat.st_mode = MODE_FILE; // TODO: Block device?
stat.st_size = size * u64::from(disk.block_size());
stat.st_blksize = disk.block_size();
stat.st_blocks = size;
Ok(Some(0))
}
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
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;
}
}
Handle::Partition(disk_num, part_num) => {
let path = format!("{}p{}", disk_num, part_num);
let path_bytes = path.as_bytes();
j = 0;
while i < buf.len() && j < path_bytes.len() {
buf[i] = path_bytes[j];
i += 1;
j += 1;
}
}
}
Ok(Some(i))
}
fn read(
&mut self,
id: usize,
buf: &mut [u8],
offset: u64,
_fcntl_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| handle.get(o..))
.unwrap_or(&[]);
let byte_count = core::cmp::min(src.len(), buf.len());
buf[..byte_count].copy_from_slice(&src[..byte_count]);
Ok(Some(byte_count))
}
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(Some(part_num as usize), block, buf)
}
}
}
fn write(
&mut self,
id: usize,
buf: &[u8],
offset: u64,
_fcntl_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_) => Err(Error::new(EBADF)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(Some(part_num as usize), block, buf)
}
}
}
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
Ok(Some(
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref mut handle) => handle.len() as u64,
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
disk.size()
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block_count = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?
.size;
u64::from(disk.block_size()) * block_count
}
},
))
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}
-1
View File
@@ -13,5 +13,4 @@ driver-block = { path = "../driver-block" }
redox-daemon = "0.1"
libredox = "0.1.3"
redox_syscall = { version = "0.5", features = ["std"] }
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
redox_event = "0.4"
+19 -85
View File
@@ -1,27 +1,11 @@
#![feature(let_chains)]
use std::{
fs::File,
io::{Read, Write},
process,
};
use std::process;
use driver_block::Disk;
use driver_block::{Disk, DiskScheme};
use event::{EventFlags, RawEventQueue};
use fdt::{node::FdtNode, Fdt};
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
use syscall::{
data::{Event, Packet},
error::{Error, ENODEV},
flag::EVENT_READ,
io::Io,
scheme::SchemeBlockMut,
EAGAIN, EINTR, EWOULDBLOCK,
};
use fdt::Fdt;
use crate::scheme::DiskScheme;
mod scheme;
mod sd;
#[cfg(target_os = "redox")]
@@ -114,82 +98,32 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
*/
}
let scheme_name = "disk.mmc";
let socket_fd = Socket::create(&scheme_name).expect("mmcd: failed to create disk scheme");
let mut disks = Vec::new();
disks.push(Box::new(sdhci) as Box<dyn Disk>);
let mut scheme = DiskScheme::new(
"disk.mmc".to_string(),
disks
.into_iter()
.enumerate()
.map(|(i, disk)| (i as u32, disk))
.collect(),
);
let mut event_queue = RawEventQueue::new().expect("mmcd: failed to open event file");
let event_queue = RawEventQueue::new().expect("mmcd: failed to open event file");
event_queue
.subscribe(socket_fd.inner().raw(), 0, EventFlags::READ)
.subscribe(scheme.event_handle().raw(), 0, EventFlags::READ)
.expect("mmcd: failed to event disk scheme");
libredox::call::setrens(0, 0).expect("mmcd: failed to enter null namespace");
daemon.ready().expect("mmcd: failed to notify parent");
let mut todo = Vec::new();
let mut disks = Vec::new();
disks.push(Box::new(sdhci) as Box<dyn Disk>);
let mut scheme = DiskScheme::new(scheme_name.to_string(), disks);
'outer: loop {
let Some(event) = event_queue
.next()
.transpose()
.expect("mmcd: failed to read event file")
else {
break;
};
if event.fd == socket_fd.inner().raw() {
loop {
let req = match socket_fd.next_request(SignalBehavior::Interrupt) {
Ok(None) => break 'outer,
Ok(Some(r)) => {
if let RequestKind::Call(c) = r.kind() {
c
} else {
continue;
}
}
Err(err) => {
if matches!(err.errno, EAGAIN | EWOULDBLOCK | EINTR) {
break;
} else {
panic!("mmcd: failed to read disk scheme: {}", err);
}
}
};
if let Some(resp) = req.handle_scheme_block(&mut scheme) {
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("mmcd: failed to write disk scheme");
} else {
todo.push(req);
}
}
for event in event_queue {
let event = event.unwrap();
if event.fd == scheme.event_handle().raw() {
scheme.tick().unwrap();
} else {
println!("Unknown event {}", event.fd);
}
// Handle todos to start new packets if possible
let mut i = 0;
while i < todo.len() {
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("mmcd: failed to write disk scheme");
} else {
i += 1;
}
}
for req in todo.drain(..) {
socket_fd
.write_response(
Response::new(&req, Err(Error::new(ENODEV))),
SignalBehavior::Restart,
)
.expect("mmcd: failed to write disk scheme");
}
}
process::exit(0);
}
-309
View File
@@ -1,309 +0,0 @@
use std::collections::BTreeMap;
use std::fmt::Write;
use std::str;
use driver_block::{Disk, DiskWrapper};
use syscall::schemev2::NewFdFlags;
use syscall::{
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
O_STAT,
};
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
enum Handle {
List(Vec<u8>), // Dir contents buffer
Disk(usize), // Disk index
Partition(usize, u32), // Disk index, partition index
}
pub struct DiskScheme {
scheme_name: String,
disks: Box<[DiskWrapper<Box<dyn Disk>>]>,
handles: BTreeMap<usize, Handle>,
next_id: usize,
}
impl DiskScheme {
pub fn new(scheme_name: String, disks: Vec<Box<dyn Disk>>) -> DiskScheme {
DiskScheme {
scheme_name,
disks: disks
.into_iter()
.map(DiskWrapper::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
handles: BTreeMap::new(),
next_id: 0,
}
}
// Checks if any conflicting handles already exist
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
for (_, handle) in self.handles.iter() {
match handle {
Handle::Disk(i) => {
if disk_i == *i {
return Err(Error::new(ENOLCK));
}
}
Handle::Partition(i, p) => {
if disk_i == *i {
match part_i_opt {
Some(part_i) => {
if part_i == *p {
return Err(Error::new(ENOLCK));
}
}
None => {
return Err(Error::new(ENOLCK));
}
}
}
}
_ => (),
}
}
Ok(())
}
}
impl SchemeBlock for DiskScheme {
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<Option<OpenResult>> {
if ctx.uid == 0 {
let path_str = path.trim_matches('/');
if path_str.is_empty() {
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
let mut list = String::new();
for (disk_index, disk) in self.disks.iter().enumerate() {
write!(list, "{}\n", disk_index).unwrap();
if disk.pt.is_none() {
continue;
}
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
}
}
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::List(list.into_bytes()));
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
Err(Error::new(EISDIR))
}
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
let disk_id_str = &path_str[..p_pos];
if p_pos + 1 >= path_str.len() {
return Err(Error::new(ENOENT));
}
let part_id_str = &path_str[p_pos + 1..];
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if let Some(disk) = self.disks.get(i) {
if disk.pt.is_none()
|| disk
.pt
.as_ref()
.unwrap()
.partitions
.get(p as usize)
.is_none()
{
return Err(Error::new(ENOENT));
}
self.check_locks(i, Some(p))?;
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Partition(i, p));
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
Err(Error::new(ENOENT))
}
} else {
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
if self.disks.get(i).is_some() {
self.check_locks(i, None)?;
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Disk(i));
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
Err(Error::new(ENOENT))
}
}
} else {
Err(Error::new(EACCES))
}
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
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.size();
stat.st_blksize = disk.block_size();
Ok(Some(0))
}
Handle::Partition(disk_id, part_num) => {
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
let size = {
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
let partition = pt
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
partition.size
};
stat.st_mode = MODE_FILE; // TODO: Block device?
stat.st_size = size * u64::from(disk.block_size());
stat.st_blksize = disk.block_size();
stat.st_blocks = size;
Ok(Some(0))
}
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
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;
}
}
Handle::Partition(disk_num, part_num) => {
let path = format!("{}p{}", disk_num, part_num);
let path_bytes = path.as_bytes();
j = 0;
while i < buf.len() && j < path_bytes.len() {
buf[i] = path_bytes[j];
i += 1;
j += 1;
}
}
}
Ok(Some(i))
}
fn read(
&mut self,
id: usize,
buf: &mut [u8],
offset: u64,
_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| handle.get(o..))
.unwrap_or(&[]);
let bytes = src.len().min(buf.len());
buf[..bytes].copy_from_slice(&src[..bytes]);
Ok(Some(bytes))
}
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(Some(part_num as usize), block, buf)
}
}
}
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _flags: u32) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_) => Err(Error::new(EBADF)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(Some(part_num as usize), block, buf)
}
}
}
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref mut handle) => Ok(Some(handle.len() as u64)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
Ok(Some(disk.size()))
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block_count = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?
.size;
Ok(Some(u64::from(disk.block_size()) * block_count))
}
}
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}
+2
View File
@@ -6,4 +6,6 @@ edition = "2021"
[dependencies]
partitionlib = { path = "../partitionlib" }
libredox = "0.1.3"
redox_syscall = "0.5"
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
+385 -4
View File
@@ -1,9 +1,21 @@
use std::cmp;
use std::io::Error;
use std::io::{self, Read, Seek, SeekFrom};
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::fmt::Write;
use std::str;
use libredox::Fd;
use partitionlib::{LogicalBlockSize, PartitionTable};
use syscall::{EBADF, EOVERFLOW};
use redox_scheme::{
CallRequest, CallerCtx, OpenResult, RequestKind, Response, SchemeBlock, SignalBehavior, Socket,
};
use syscall::schemev2::NewFdFlags;
use syscall::{
Error, Result, Stat, EACCES, EAGAIN, EBADF, EISDIR, ENOENT, ENOLCK, EOPNOTSUPP, EOVERFLOW,
MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT,
};
/// Split the read operation into a series of block reads.
/// `read_fn` will be called with a block number to be read, and a buffer to be filled.
@@ -13,8 +25,8 @@ fn block_read(
offset: u64,
blksize: u32,
buf: &mut [u8],
mut read_fn: impl FnMut(u64, &mut [u8]) -> Result<(), Error>,
) -> Result<usize, Error> {
mut read_fn: impl FnMut(u64, &mut [u8]) -> io::Result<()>,
) -> io::Result<usize> {
// TODO: Yield sometimes, perhaps after a few blocks or something.
if buf.len() == 0 {
@@ -57,6 +69,8 @@ pub trait Disk {
fn block_size(&self) -> u32;
fn size(&self) -> u64;
// These operate on a whole multiple of the block size
// FIXME maybe only operate on a single block worth of data?
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>>;
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>>;
}
@@ -231,3 +245,370 @@ impl<T: Disk> DiskWrapper<T> {
}
}
}
enum Handle {
List(Vec<u8>), // entries
Disk(u32), // disk num
Partition(u32, u32), // disk num, part num
}
pub struct DiskScheme<T> {
scheme_name: String,
socket: Socket,
disks: BTreeMap<u32, DiskWrapper<T>>,
handles: BTreeMap<usize, Handle>,
next_id: usize,
blocked: Vec<CallRequest>,
}
impl<T: Disk> DiskScheme<T> {
pub fn new(scheme_name: String, disks: BTreeMap<u32, T>) -> Self {
assert!(scheme_name.starts_with("disk"));
let socket = Socket::nonblock(&scheme_name).expect("failed to create disk scheme");
Self {
scheme_name,
socket,
disks: disks
.into_iter()
.map(|(k, disk)| (k, DiskWrapper::new(disk)))
.collect(),
next_id: 0,
handles: BTreeMap::new(),
blocked: vec![],
}
}
pub fn event_handle(&self) -> &Fd {
self.socket.inner()
}
/// Process pending and new requests.
///
/// This needs to be called each time there is a new event on the scheme
/// file and each time a read or write operation has completed.
// FIXME maybe split into one method for events on the scheme fd and one
// to call when an irq is received to indicate that blocked packets can
// be processed.
pub fn tick(&mut self) -> io::Result<()> {
// Handle any blocked requests
let mut i = 0;
while i < self.blocked.len() {
if let Some(resp) = self.blocked[i].handle_scheme_block(self) {
self.socket
.write_response(resp, SignalBehavior::Restart)
.expect("driver-block: failed to write scheme");
self.blocked.remove(i);
} else {
i += 1;
}
}
// Handle new scheme requests
loop {
let request = match self.socket.next_request(SignalBehavior::Restart) {
Ok(Some(request)) => request,
Ok(None) => {
// Scheme likely got unmounted
std::process::exit(0);
}
Err(err) if err.errno == EAGAIN => break,
Err(err) => return Err(err.into()),
};
match request.kind() {
RequestKind::Call(call_request) => {
if let Some(resp) = call_request.handle_scheme_block(self) {
self.socket.write_response(resp, SignalBehavior::Restart)?;
} else {
self.blocked.push(call_request);
}
}
RequestKind::SendFd(sendfd_request) => {
self.socket.write_response(
Response::for_sendfd(&sendfd_request, Err(syscall::Error::new(EOPNOTSUPP))),
SignalBehavior::Restart,
)?;
}
RequestKind::Cancellation(_cancellation_request) => {
// FIXME implement cancellation
}
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => {
unreachable!()
}
}
}
Ok(())
}
// Checks if any conflicting handles already exist
fn check_locks(&self, disk_i: u32, part_i_opt: Option<u32>) -> Result<()> {
for (_, handle) in self.handles.iter() {
match handle {
Handle::Disk(i) => {
if disk_i == *i {
return Err(Error::new(ENOLCK));
}
}
Handle::Partition(i, p) => {
if disk_i == *i {
match part_i_opt {
Some(part_i) => {
if part_i == *p {
return Err(Error::new(ENOLCK));
}
}
None => {
return Err(Error::new(ENOLCK));
}
}
}
}
_ => (),
}
}
Ok(())
}
}
impl<T: Disk> SchemeBlock for DiskScheme<T> {
fn xopen(
&mut self,
path_str: &str,
flags: usize,
ctx: &CallerCtx,
) -> Result<Option<OpenResult>> {
if ctx.uid != 0 {
return Err(Error::new(EACCES));
}
let path_str = path_str.trim_matches('/');
let handle = 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();
if disk.pt.is_none() {
continue;
}
for part_num in 0..disk.pt.as_ref().unwrap().partitions.len() {
write!(list, "{}p{}\n", nsid, part_num).unwrap();
}
}
Handle::List(list.into_bytes())
} else {
return Err(Error::new(EISDIR));
}
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
let nsid_str = &path_str[..p_pos];
if p_pos + 1 >= path_str.len() {
return Err(Error::new(ENOENT));
}
let part_num_str = &path_str[p_pos + 1..];
let nsid = nsid_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
let part_num = part_num_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if let Some(disk) = self.disks.get(&nsid) {
if disk
.pt
.as_ref()
.ok_or(Error::new(ENOENT))?
.partitions
.get(part_num as usize)
.is_some()
{
self.check_locks(nsid, Some(part_num))?;
Handle::Partition(nsid, part_num)
} else {
return Err(Error::new(ENOENT));
}
} else {
return Err(Error::new(ENOENT));
}
} else {
let nsid = path_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if self.disks.contains_key(&nsid) {
self.check_locks(nsid, None)?;
Handle::Disk(nsid)
} else {
return Err(Error::new(ENOENT));
}
};
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, handle);
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
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_blocks = disk.disk().size() / u64::from(disk.block_size());
stat.st_blksize = disk.block_size();
stat.st_size = disk.size();
Ok(Some(0))
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
stat.st_mode = MODE_FILE;
stat.st_size = part.size * u64::from(disk.block_size());
stat.st_blocks = part.size;
stat.st_blksize = disk.block_size();
Ok(Some(0))
}
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
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;
}
}
Handle::Partition(disk_num, part_num) => {
let number_str = format!("{}p{}", disk_num, part_num);
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],
offset: u64,
_fcntl_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| handle.get(o..))
.unwrap_or(&[]);
let count = core::cmp::min(src.len(), buf.len());
buf[..count].copy_from_slice(&src[..count]);
Ok(Some(count))
}
Handle::Disk(number) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(Some(part_num as usize), block, buf)
}
}
}
fn write(
&mut self,
id: usize,
buf: &[u8],
offset: u64,
_fcntl_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_) => Err(Error::new(EBADF)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(Some(part_num as usize), block, buf)
}
}
}
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
Ok(Some(
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => handle.len() as u64,
Handle::Disk(number) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
disk.size()
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
part.size * u64::from(disk.block_size())
}
},
))
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}
-1
View File
@@ -11,5 +11,4 @@ log = "0.4"
pcid = { path = "../../pcid" }
redox-daemon = "0.1"
redox_syscall = { version = "0.5", features = ["std"] }
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
redox_event = "0.4"
+30 -102
View File
@@ -1,10 +1,9 @@
use common::io::Io as _;
use driver_block::Disk;
use driver_block::{Disk, DiskScheme};
use event::{EventFlags, RawEventQueue};
use libredox::flag;
use log::{error, info};
use pcid_interface::PciFunctionHandle;
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
use std::{
fs::File,
io::{Read, Write},
@@ -13,15 +12,10 @@ use std::{
thread::{self, sleep},
time::Duration,
};
use syscall::error::{Error, EAGAIN, EINTR, ENODEV, EWOULDBLOCK};
use crate::{
ide::{AtaCommand, AtaDisk, Channel},
scheme::DiskScheme,
};
use crate::ide::{AtaCommand, AtaDisk, Channel};
pub mod ide;
pub mod scheme;
fn main() {
redox_daemon::Daemon::new(daemon).expect("ided: failed to daemonize");
@@ -193,7 +187,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
}
let scheme_name = format!("disk.{}", name);
let socket_fd = Socket::nonblock(&scheme_name).expect("ided: failed to create disk scheme");
let mut scheme = DiskScheme::new(
scheme_name,
disks
.into_iter()
.enumerate()
.map(|(i, disk)| (i as u32, disk))
.collect(),
);
let primary_irq_fd = libredox::call::open(
&format!("/scheme/irq/{}", primary_irq),
@@ -211,14 +212,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
.expect("ided: failed to open irq file");
let mut secondary_irq_file = unsafe { File::from_raw_fd(secondary_irq_fd as RawFd) };
let mut event_queue = RawEventQueue::new().expect("ided: failed to open event file");
let event_queue = RawEventQueue::new().expect("ided: failed to open event file");
libredox::call::setrens(0, 0).expect("ided: failed to enter null namespace");
daemon.ready().expect("ided: failed to notify parent");
event_queue
.subscribe(socket_fd.inner().raw(), 0, EventFlags::READ)
.subscribe(scheme.event_handle().raw(), 0, EventFlags::READ)
.expect("ided: failed to event disk scheme");
event_queue
@@ -229,44 +230,10 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
.subscribe(secondary_irq_fd, 0, EventFlags::READ)
.expect("ided: failed to event irq scheme");
let mut scheme = DiskScheme::new(scheme_name, chans, disks);
let mut todo = Vec::new();
'outer: loop {
let Some(event) = event_queue
.next()
.transpose()
.expect("ided: failed to read event file")
else {
break;
};
if event.fd == socket_fd.inner().raw() {
loop {
let req = match socket_fd.next_request(SignalBehavior::Interrupt) {
Ok(None) => break 'outer,
Ok(Some(r)) => {
if let RequestKind::Call(c) = r.kind() {
c
} else {
continue;
}
}
Err(err) => {
if matches!(err.errno, EAGAIN | EWOULDBLOCK | EINTR) {
break;
} else {
panic!("ided: failed to read disk scheme: {}", err);
}
}
};
if let Some(resp) = req.handle_scheme_block(&mut scheme) {
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("ided: failed to write disk scheme");
} else {
todo.push(req);
}
}
for event in event_queue {
let event = event.unwrap();
if event.fd == scheme.event_handle().raw() {
scheme.tick().unwrap();
} else if event.fd == primary_irq_fd {
let mut irq = [0; 8];
if primary_irq_file
@@ -274,23 +241,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
.expect("ided: failed to read irq file")
>= irq.len()
{
if scheme.irq(0) {
primary_irq_file
.write(&irq)
.expect("ided: failed to write irq file");
let _chan = chans[0].lock().unwrap();
//TODO: check chan for irq
// Handle todos in order to finish previous packets if possible
let mut i = 0;
while i < todo.len() {
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("ided: failed to write disk scheme");
} else {
i += 1;
}
}
}
primary_irq_file
.write(&irq)
.expect("ided: failed to write irq file");
scheme.tick().unwrap();
}
} else if event.fd == secondary_irq_fd {
let mut irq = [0; 8];
@@ -299,48 +257,18 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
.expect("ided: failed to read irq file")
>= irq.len()
{
if scheme.irq(1) {
secondary_irq_file
.write(&irq)
.expect("ided: failed to write irq file");
let _chan = chans[1].lock().unwrap();
//TODO: check chan for irq
// Handle todos in order to finish previous packets if possible
let mut i = 0;
while i < todo.len() {
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("ided: failed to write disk scheme");
} else {
i += 1;
}
}
}
secondary_irq_file
.write(&irq)
.expect("ided: failed to write irq file");
scheme.tick().unwrap();
}
} else {
error!("Unknown event {}", event.fd);
}
// Handle todos to start new packets if possible
let mut i = 0;
while i < todo.len() {
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("ided: failed to write disk scheme");
} else {
i += 1;
}
}
for req in todo.drain(..) {
socket_fd
.write_response(
Response::new(&req, Err(Error::new(ENODEV))),
SignalBehavior::Restart,
)
.expect("ided: failed to write disk scheme");
}
}
std::process::exit(0);
-323
View File
@@ -1,323 +0,0 @@
use std::collections::BTreeMap;
use std::fmt::Write;
use std::str;
use std::sync::{Arc, Mutex};
use driver_block::{Disk, DiskWrapper};
use syscall::schemev2::NewFdFlags;
use syscall::{
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
O_STAT,
};
use crate::ide::Channel;
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
enum Handle {
List(Vec<u8>), // Dir contents buffer
Disk(usize), // Disk index
Partition(usize, u32), // Disk index, partition index
}
pub struct DiskScheme {
scheme_name: String,
chans: Box<[Arc<Mutex<Channel>>]>,
disks: Box<[DiskWrapper<Box<dyn Disk>>]>,
handles: BTreeMap<usize, Handle>,
next_id: usize,
}
impl DiskScheme {
pub fn new(
scheme_name: String,
chans: Vec<Arc<Mutex<Channel>>>,
disks: Vec<Box<dyn Disk>>,
) -> DiskScheme {
DiskScheme {
scheme_name,
chans: chans.into_boxed_slice(),
disks: disks
.into_iter()
.map(DiskWrapper::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
handles: BTreeMap::new(),
next_id: 0,
}
}
pub fn irq(&mut self, chan_i: usize) -> bool {
let _chan = self.chans[chan_i].lock().unwrap();
//TODO: check chan for irq
true
}
// Checks if any conflicting handles already exist
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
for (_, handle) in self.handles.iter() {
match handle {
Handle::Disk(i) => {
if disk_i == *i {
return Err(Error::new(ENOLCK));
}
}
Handle::Partition(i, p) => {
if disk_i == *i {
match part_i_opt {
Some(part_i) => {
if part_i == *p {
return Err(Error::new(ENOLCK));
}
}
None => {
return Err(Error::new(ENOLCK));
}
}
}
}
_ => (),
}
}
Ok(())
}
}
impl SchemeBlock for DiskScheme {
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<Option<OpenResult>> {
if ctx.uid == 0 {
let path_str = path.trim_matches('/');
if path_str.is_empty() {
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
let mut list = String::new();
for (disk_index, disk) in self.disks.iter().enumerate() {
write!(list, "{}\n", disk_index).unwrap();
if disk.pt.is_none() {
continue;
}
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
}
}
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::List(list.into_bytes()));
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
Err(Error::new(EISDIR))
}
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
let disk_id_str = &path_str[..p_pos];
if p_pos + 1 >= path_str.len() {
return Err(Error::new(ENOENT));
}
let part_id_str = &path_str[p_pos + 1..];
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if let Some(disk) = self.disks.get(i) {
if disk.pt.is_none()
|| disk
.pt
.as_ref()
.unwrap()
.partitions
.get(p as usize)
.is_none()
{
return Err(Error::new(ENOENT));
}
self.check_locks(i, Some(p))?;
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Partition(i, p));
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
Err(Error::new(ENOENT))
}
} else {
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
if self.disks.get(i).is_some() {
self.check_locks(i, None)?;
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Disk(i));
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
Err(Error::new(ENOENT))
}
}
} else {
Err(Error::new(EACCES))
}
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
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.size();
stat.st_blksize = disk.block_size();
Ok(Some(0))
}
Handle::Partition(disk_id, part_num) => {
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
let size = {
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
let partition = pt
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
partition.size
};
stat.st_mode = MODE_FILE; // TODO: Block device?
stat.st_size = size * u64::from(disk.block_size());
stat.st_blksize = disk.block_size();
stat.st_blocks = size;
Ok(Some(0))
}
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
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;
}
}
Handle::Partition(disk_num, part_num) => {
let path = format!("{}p{}", disk_num, part_num);
let path_bytes = path.as_bytes();
j = 0;
while i < buf.len() && j < path_bytes.len() {
buf[i] = path_bytes[j];
i += 1;
j += 1;
}
}
}
Ok(Some(i))
}
fn read(
&mut self,
id: usize,
buf: &mut [u8],
offset: u64,
_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| handle.get(o..))
.unwrap_or(&[]);
let bytes = src.len().min(buf.len());
buf[..bytes].copy_from_slice(&src[..bytes]);
Ok(Some(bytes))
}
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(Some(part_num as usize), block, buf)
}
}
}
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _flags: u32) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_) => Err(Error::new(EBADF)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(Some(part_num as usize), block, buf)
}
}
}
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref mut handle) => Ok(Some(handle.len() as u64)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
Ok(Some(disk.size()))
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
let block_count = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?
.size;
Ok(Some(u64::from(disk.block_size()) * block_count))
}
}
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}
+1 -1
View File
@@ -12,5 +12,5 @@ anyhow = "1"
libredox = "0.1.3"
redox-daemon = "0.1"
redox_syscall = { version = "0.5", features = ["std"] }
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
redox_event = "0.4"
driver-block = { path = "../driver-block" }
+62 -136
View File
@@ -2,46 +2,26 @@
#![feature(int_roundings)]
use std::collections::BTreeMap;
use std::fs::File;
use std::os::fd::AsRawFd;
use std::str;
use driver_block::{Disk, DiskScheme};
use libredox::call::MmapArgs;
use libredox::flag;
use redox_scheme::{CallerCtx, OpenResult, RequestKind, Scheme, SignalBehavior, Socket};
use syscall::data::Stat;
use syscall::error::*;
use syscall::flag::{MODE_DIR, MODE_FILE};
use syscall::schemev2::NewFdFlags;
use syscall::PAGE_SIZE;
use anyhow::{anyhow, bail, Context};
const LIST: [u8; 2] = *b"0\n";
#[repr(usize)]
enum HandleType {
TopLevel = 0,
TheData = 1,
}
impl HandleType {
fn try_from_raw(raw: usize) -> Option<Self> {
Some(match raw {
0 => Self::TopLevel,
1 => Self::TheData,
_ => return None,
})
}
}
pub struct DiskScheme {
struct LiveDisk {
the_data: &'static mut [u8],
}
impl DiskScheme {
pub fn new() -> anyhow::Result<DiskScheme> {
impl LiveDisk {
fn new() -> anyhow::Result<LiveDisk> {
let mut phys = 0;
let mut size = 0;
@@ -93,133 +73,79 @@ impl DiskScheme {
std::slice::from_raw_parts_mut(base as *mut u8, size)
};
Ok(DiskScheme { the_data })
Ok(LiveDisk { the_data })
}
}
impl Scheme for DiskScheme {
fn fsize(&mut self, id: usize) -> Result<u64> {
Ok(
match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
HandleType::TopLevel => LIST.len() as u64,
HandleType::TheData => self.the_data.len() as u64,
},
)
impl Disk for LiveDisk {
fn block_size(&self) -> u32 {
512
}
fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
Ok(0)
fn size(&self) -> u64 {
self.the_data.len() as u64
}
fn fsync(&mut self, _id: usize) -> Result<usize> {
Ok(0)
}
fn close(&mut self, _id: usize) -> Result<usize> {
Ok(0)
}
fn xopen(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
if ctx.uid != 0 {
return Err(Error::new(EACCES));
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
let block = block as usize;
let block_size = self.block_size() as usize;
if block * block_size + buffer.len() > self.size() as usize {
return Err(syscall::Error::new(EOVERFLOW));
}
let path_trimmed = path.trim_matches('/');
Ok(OpenResult::ThisScheme {
number: match path_trimmed {
"" => HandleType::TopLevel as usize,
"0" => HandleType::TheData as usize,
_ => return Err(Error::new(ENOENT)),
},
flags: NewFdFlags::POSITIONED,
})
}
fn read(&mut self, id: usize, buf: &mut [u8], offset: u64, _flags: u32) -> Result<usize> {
let data = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
HandleType::TheData => &*self.the_data,
HandleType::TopLevel => &LIST,
};
let src = usize::try_from(offset)
.ok()
.and_then(|o| data.get(o..))
.unwrap_or(&[]);
let byte_count = std::cmp::min(src.len(), buf.len());
buf[..byte_count].copy_from_slice(&src[..byte_count]);
Ok(byte_count)
}
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _flags: u32) -> Result<usize> {
let data = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
HandleType::TheData => &mut *self.the_data,
HandleType::TopLevel => return Err(Error::new(EBADF)),
};
let dst = usize::try_from(offset)
.ok()
.and_then(|o| data.get_mut(o..))
.unwrap_or(&mut []);
let byte_count = std::cmp::min(dst.len(), buf.len());
dst[..byte_count].copy_from_slice(&buf[..byte_count]);
Ok(byte_count)
buffer
.copy_from_slice(&self.the_data[block * block_size..block * block_size + buffer.len()]);
Ok(Some(block_size))
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
let path = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
HandleType::TopLevel => "",
HandleType::TheData => "0",
};
let src = format!("disk.live:{}", path).into_bytes();
let byte_count = std::cmp::min(buf.len(), src.len());
buf[..byte_count].copy_from_slice(&src[..byte_count]);
Ok(byte_count)
}
fn fstat(&mut self, id: usize, stat_buf: &mut Stat) -> Result<usize> {
let (len, mode) = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
HandleType::TheData => (self.the_data.len(), MODE_FILE | 0o644),
HandleType::TopLevel => (LIST.len(), MODE_DIR | 0o755),
};
*stat_buf = Stat {
st_mode: mode,
st_uid: 0,
st_gid: 0,
st_size: len.try_into().map_err(|_| Error::new(EOVERFLOW))?,
..Stat::default()
};
Ok(0)
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
let block = block as usize;
let block_size = self.block_size() as usize;
if block * block_size + buffer.len() > self.size() as usize {
return Err(syscall::Error::new(EOVERFLOW));
}
self.the_data[block * block_size..block * block_size + buffer.len()]
.copy_from_slice(buffer);
Ok(Some(block_size))
}
}
fn main() -> anyhow::Result<()> {
redox_daemon::Daemon::new(move |daemon| {
let socket_fd = Socket::create("disk.live").expect("failed to open scheme");
let mut scheme = DiskScheme::new().unwrap_or_else(|err| {
eprintln!("failed to initialize livedisk scheme: {}", err);
std::process::exit(1)
});
let event_queue = event::EventQueue::new().unwrap();
event::user_data! {
enum Event {
Scheme,
}
};
let mut scheme = DiskScheme::new(
"disk.live".to_owned(),
BTreeMap::from([(
0,
LiveDisk::new().unwrap_or_else(|err| {
eprintln!("failed to initialize livedisk scheme: {}", err);
std::process::exit(1)
}),
)]),
);
libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
event_queue
.subscribe(
scheme.event_handle().raw(),
Event::Scheme,
event::EventFlags::READ,
)
.unwrap();
daemon.ready().expect("failed to signal readiness");
loop {
let req = match socket_fd
.next_request(SignalBehavior::Restart)
.expect("failed to get next request")
{
Some(r) => {
if let RequestKind::Call(c) = r.kind() {
c
} else {
continue;
}
}
None => break,
};
let resp = req.handle_scheme(&mut scheme);
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("failed to write packet");
for event in event_queue {
match event.unwrap().user_data {
Event::Scheme => scheme.tick().unwrap(),
}
}
std::process::exit(0);
+1 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "nvmed"
version = "0.1.0"
edition = "2018"
edition = "2021"
[dependencies]
arrayvec = "0.5"
@@ -11,7 +11,6 @@ futures = "0.3"
log = "0.4"
redox-daemon = "0.1"
redox_syscall = { version = "0.5", features = ["std"] }
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
redox_event = "0.4"
smallvec = "1"
+48 -35
View File
@@ -5,15 +5,15 @@ use std::ptr::NonNull;
use std::sync::Arc;
use std::{slice, usize};
use driver_block::{Disk, DiskScheme};
use pcid_interface::{PciFeature, PciFeatureInfo, PciFunction, PciFunctionHandle};
use redox_scheme::{RequestKind, SignalBehavior, Socket};
use syscall::Result;
use crate::nvme::NvmeNamespace;
use self::nvme::{InterruptMethod, InterruptSources, Nvme};
use self::scheme::DiskScheme;
mod nvme;
mod scheme;
/// Get the most optimal yet functional interrupt mechanism: either (in the order of preference):
/// MSI-X, MSI, and INTx# pin. Returns both runtime interrupt structures (MSI/MSI-X capability
@@ -140,6 +140,26 @@ fn get_int_method(
}
}
struct NvmeDisk(Arc<Nvme>, NvmeNamespace);
impl Disk for NvmeDisk {
fn block_size(&self) -> u32 {
self.1.block_size.try_into().unwrap()
}
fn size(&self) -> u64 {
self.1.blocks * self.1.block_size
}
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
self.0.namespace_read(self.1, block, buffer)
}
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
self.0.namespace_write(self.1, block, buffer)
}
}
fn main() {
redox_daemon::Daemon::new(daemon).expect("nvmed: failed to daemonize");
}
@@ -161,8 +181,6 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
let address = unsafe { pcid_handle.map_bar(0).ptr };
let socket = Socket::create(&scheme_name).expect("nvmed: failed to create disk scheme");
daemon.ready().expect("nvmed: failed to signal readiness");
let (reactor_sender, reactor_receiver) = crossbeam_channel::unbounded();
@@ -185,40 +203,35 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
reactor_receiver,
);
let namespaces = nvme.init_with_queues();
let event_queue = event::EventQueue::new().unwrap();
event::user_data! {
enum Event {
Scheme,
}
};
let mut scheme = DiskScheme::new(
scheme_name,
namespaces
.into_iter()
.map(|(k, ns)| (k, NvmeDisk(nvme.clone(), ns)))
.collect(),
);
libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
let mut scheme = DiskScheme::new(scheme_name, nvme, namespaces);
let mut todo = Vec::new();
loop {
// TODO: Use a proper event queue once interrupt support is back.
match socket
.next_request(SignalBehavior::Restart)
.expect("nvmed: failed to read disk scheme")
{
None => {
break;
}
Some(req) => {
if let RequestKind::Call(c) = req.kind() {
todo.push(c);
} else {
// TODO: cancellation
continue;
}
}
}
event_queue
.subscribe(
scheme.event_handle().raw(),
Event::Scheme,
event::EventFlags::READ,
)
.unwrap();
let mut i = 0;
while i < todo.len() {
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
let _req = todo.remove(i);
socket
.write_response(resp, SignalBehavior::Restart)
.expect("nvmed: failed to write disk scheme");
} else {
i += 1;
}
for event in event_queue {
match event.unwrap().user_data {
Event::Scheme => scheme.tick().unwrap(),
}
}
-336
View File
@@ -1,336 +0,0 @@
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::fmt::Write;
use std::str;
use std::sync::Arc;
use driver_block::{Disk, DiskWrapper};
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
use syscall::schemev2::NewFdFlags;
use syscall::{
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
O_STAT,
};
use crate::nvme::{Nvme, NvmeNamespace};
enum Handle {
List(Vec<u8>), // entries
Disk(u32), // disk num
Partition(u32, u32), // disk num, part num
}
struct NvmeDisk(Arc<Nvme>, NvmeNamespace);
impl Disk for NvmeDisk {
fn block_size(&self) -> u32 {
self.1.block_size.try_into().unwrap()
}
fn size(&self) -> u64 {
self.1.blocks * self.1.block_size
}
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
self.0.namespace_read(self.1, block, buffer)
}
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
self.0.namespace_write(self.1, block, buffer)
}
}
pub struct DiskScheme {
scheme_name: String,
disks: BTreeMap<u32, DiskWrapper<NvmeDisk>>,
handles: BTreeMap<usize, Handle>,
next_id: usize,
}
impl DiskScheme {
pub fn new(
scheme_name: String,
nvme: Arc<Nvme>,
disks: BTreeMap<u32, NvmeNamespace>,
) -> DiskScheme {
DiskScheme {
scheme_name,
disks: disks
.into_iter()
.map(|(k, ns)| (k, DiskWrapper::new(NvmeDisk(nvme.clone(), ns))))
.collect(),
handles: BTreeMap::new(),
next_id: 0,
}
}
// Checks if any conflicting handles already exist
fn check_locks(&self, disk_i: u32, part_i_opt: Option<u32>) -> Result<()> {
for (_, handle) in self.handles.iter() {
match handle {
Handle::Disk(i) => {
if disk_i == *i {
return Err(Error::new(ENOLCK));
}
}
Handle::Partition(i, p) => {
if disk_i == *i {
match part_i_opt {
Some(part_i) => {
if part_i == *p {
return Err(Error::new(ENOLCK));
}
}
None => {
return Err(Error::new(ENOLCK));
}
}
}
}
_ => (),
}
}
Ok(())
}
}
impl SchemeBlock for DiskScheme {
fn xopen(
&mut self,
path_str: &str,
flags: usize,
ctx: &CallerCtx,
) -> Result<Option<OpenResult>> {
if ctx.uid != 0 {
return Err(Error::new(EACCES));
}
let path_str = path_str.trim_matches('/');
let handle = 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();
if disk.pt.is_none() {
continue;
}
for part_num in 0..disk.pt.as_ref().unwrap().partitions.len() {
write!(list, "{}p{}\n", nsid, part_num).unwrap();
}
}
Handle::List(list.into_bytes())
} else {
return Err(Error::new(EISDIR));
}
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
let nsid_str = &path_str[..p_pos];
if p_pos + 1 >= path_str.len() {
return Err(Error::new(ENOENT));
}
let part_num_str = &path_str[p_pos + 1..];
let nsid = nsid_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
let part_num = part_num_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if let Some(disk) = self.disks.get(&nsid) {
if disk
.pt
.as_ref()
.ok_or(Error::new(ENOENT))?
.partitions
.get(part_num as usize)
.is_some()
{
self.check_locks(nsid, Some(part_num))?;
Handle::Partition(nsid, part_num)
} else {
return Err(Error::new(ENOENT));
}
} else {
return Err(Error::new(ENOENT));
}
} else {
let nsid = path_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if self.disks.contains_key(&nsid) {
self.check_locks(nsid, None)?;
Handle::Disk(nsid)
} else {
return Err(Error::new(ENOENT));
}
};
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, handle);
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
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_blocks = disk.disk().1.blocks;
stat.st_blksize = disk.block_size();
stat.st_size = disk.size();
Ok(Some(0))
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
stat.st_mode = MODE_FILE;
stat.st_size = part.size * u64::from(disk.block_size());
stat.st_blocks = part.size;
stat.st_blksize = disk.block_size();
Ok(Some(0))
}
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
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;
}
}
Handle::Partition(disk_num, part_num) => {
let number_str = format!("{}p{}", disk_num, part_num);
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],
offset: u64,
_fcntl_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| handle.get(o..))
.unwrap_or(&[]);
let count = core::cmp::min(src.len(), buf.len());
buf[..count].copy_from_slice(&src[..count]);
Ok(Some(count))
}
Handle::Disk(number) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.read(Some(part_num as usize), block, buf)
}
}
}
fn write(
&mut self,
id: usize,
buf: &[u8],
offset: u64,
_fcntl_flags: u32,
) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_) => Err(Error::new(EBADF)),
Handle::Disk(number) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(None, block, buf)
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let block = offset / u64::from(disk.block_size());
disk.write(Some(part_num as usize), block, buf)
}
}
}
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
Ok(Some(
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref handle) => handle.len() as u64,
Handle::Disk(number) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
disk.size()
}
Handle::Partition(disk_num, part_num) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
part.size * u64::from(disk.block_size())
}
},
))
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}
+1 -1
View File
@@ -13,8 +13,8 @@ futures = { version = "0.3.28", features = ["executor"] }
spin = "*"
redox-daemon = "0.1"
redox_event = "0.4"
redox_syscall = { version = "0.5", features = ["std"] }
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
common = { path = "../../common" }
driver-block = { path = "../driver-block" }
+30 -21
View File
@@ -1,8 +1,9 @@
#![deny(trivial_numeric_casts, unused_allocation)]
use std::collections::BTreeMap;
use std::sync::{Arc, Weak};
use redox_scheme::{RequestKind, SignalBehavior, Socket};
use driver_block::DiskScheme;
use static_assertions::const_assert_eq;
use pcid_interface::*;
@@ -15,6 +16,8 @@ mod scheme;
use thiserror::Error;
use crate::scheme::VirtioDisk;
#[derive(Debug, Error)]
pub enum Error {
#[error("capability {0:?} not found")]
@@ -140,31 +143,37 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
let scheme_name = format!("disk.{}", name);
let socket_fd = Socket::create(&scheme_name).map_err(Error::SyscallError)?;
let event_queue = event::EventQueue::new().unwrap();
let mut scheme = scheme::DiskScheme::new(queue, device_space);
event::user_data! {
enum Event {
Scheme,
}
};
let mut scheme = DiskScheme::new(
scheme_name,
BTreeMap::from([(0, VirtioDisk::new(queue, device_space))]),
);
libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
event_queue
.subscribe(
scheme.event_handle().raw(),
Event::Scheme,
event::EventFlags::READ,
)
.unwrap();
deamon.ready().expect("virtio-blkd: failed to deamonize");
loop {
let req = match socket_fd
.next_request(SignalBehavior::Restart)
.expect("virtio-blkd: failed to read disk scheme")
{
Some(r) => {
if let RequestKind::Call(c) = r.kind() {
c
} else {
continue;
}
}
None => break,
};
let resp = req.handle_scheme_block(&mut scheme).expect("TODO: block?");
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("virtio-blkd: failed to write to disk scheme");
for event in event_queue {
match event.unwrap().user_data {
Event::Scheme => scheme.tick().unwrap(),
}
}
Ok(())
}
+2 -215
View File
@@ -1,17 +1,6 @@
use std::collections::BTreeMap;
use std::fmt::Write;
use std::sync::Arc;
use common::dma::Dma;
use driver_block::DiskWrapper;
use redox_scheme::CallerCtx;
use redox_scheme::OpenResult;
use redox_scheme::SchemeBlock;
use syscall::error::*;
use syscall::flag::*;
use syscall::schemev2::NewFdFlags;
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
use virtio_core::transport::Queue;
@@ -19,8 +8,6 @@ use crate::BlockDeviceConfig;
use crate::BlockRequestTy;
use crate::BlockVirtRequest;
const BLK_SIZE: u64 = 512;
trait BlkExtension {
async fn read(&self, block: u64, target: &mut [u8]) -> usize;
async fn write(&self, block: u64, target: &[u8]) -> usize;
@@ -86,13 +73,13 @@ impl BlkExtension for Queue<'_> {
}
}
struct VirtioDisk<'a> {
pub(crate) struct VirtioDisk<'a> {
queue: Arc<Queue<'a>>,
cfg: BlockDeviceConfig,
}
impl<'a> VirtioDisk<'a> {
pub fn new(queue: Arc<Queue<'a>>, cfg: BlockDeviceConfig) -> Self {
pub(crate) fn new(queue: Arc<Queue<'a>>, cfg: BlockDeviceConfig) -> Self {
Self { queue, cfg }
}
}
@@ -118,203 +105,3 @@ impl driver_block::Disk for VirtioDisk<'_> {
)))
}
}
pub enum Handle {
Partition {
/// Partition Number
number: u32,
},
List {
entries: Vec<u8>,
},
Disk,
}
pub struct DiskScheme<'a> {
disk: DiskWrapper<VirtioDisk<'a>>,
next_id: usize,
handles: BTreeMap<usize, Handle>,
}
impl<'a> DiskScheme<'a> {
pub fn new(queue: Arc<Queue<'a>>, cfg: BlockDeviceConfig) -> Self {
Self {
disk: DiskWrapper::new(VirtioDisk::new(queue, cfg)),
next_id: 0,
handles: BTreeMap::new(),
}
}
}
impl<'a> SchemeBlock for DiskScheme<'a> {
fn xopen(
&mut self,
path: &str,
flags: usize,
_ctx: &CallerCtx,
) -> syscall::Result<Option<OpenResult>> {
log::info!("virtiod: open: {}", path);
let path_str = path.trim_matches('/');
if path_str.is_empty() {
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
let mut list = String::new();
// FIXME: The zero is the disk identifier (look in the nvmed scheme, it set's this
// to the namespace id).
write!(list, "{}\n", 0).unwrap();
if let Some(part_table) = &self.disk.pt {
for part_num in 0..part_table.partitions.len() {
write!(list, "{}p{}\n", 0, part_num).unwrap();
}
}
let id = self.next_id;
self.next_id += 1;
self.handles.insert(
id,
Handle::List {
entries: list.into_bytes(),
},
);
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
return Err(syscall::Error::new(EISDIR));
}
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
let _nsid_str = &path_str[..p_pos];
if p_pos + 1 >= path_str.len() {
return Err(Error::new(ENOENT));
}
let part_num_str = &path_str[p_pos + 1..];
let part_num = part_num_str.parse::<u32>().unwrap();
let part_table = self.disk.pt.as_ref().unwrap();
let _part = part_table.partitions.get(part_num as usize).unwrap();
let id = self.next_id;
self.next_id += 1;
self.handles
.insert(id, Handle::Partition { number: part_num });
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
} else {
let nsid = path_str.parse::<u32>().unwrap();
assert_eq!(nsid, 0);
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Disk);
Ok(Some(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::POSITIONED,
}))
}
}
fn read(
&mut self,
id: usize,
buf: &mut [u8],
offset: u64,
_fcntl_flags: u32,
) -> syscall::Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List { ref mut entries } => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| entries.get(o..))
.unwrap_or(&[]);
let count = core::cmp::min(src.len(), buf.len());
buf[..count].copy_from_slice(&src[..count]);
Ok(Some(count))
}
Handle::Disk => {
let block = offset / u64::from(self.disk.block_size());
self.disk.read(None, block, buf)
}
Handle::Partition { number } => {
let block = offset / u64::from(self.disk.block_size());
self.disk.read(Some(number as usize), block, buf)
}
}
}
fn write(
&mut self,
id: usize,
buf: &[u8],
offset: u64,
_fcntl_flags: u32,
) -> syscall::Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List { .. } => Err(Error::new(EBADF)),
Handle::Disk => {
let block = offset / u64::from(self.disk.block_size());
self.disk.write(None, block, buf)
}
Handle::Partition { number } => {
let block = offset / u64::from(self.disk.block_size());
self.disk.write(Some(number as usize), block, buf)
}
}
}
fn fsize(&mut self, id: usize) -> syscall::Result<Option<u64>> {
Ok(Some(
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List { ref entries } => {
let len = entries.len() as u64;
log::debug!("list: part_len={len:?}");
len
}
Handle::Partition { number } => {
let part_table = self.disk.pt.as_ref().unwrap();
let part = part_table
.partitions
.get(number as usize)
.ok_or(Error::new(EBADF))?;
// Partition size in bytes.
let len = part.size * BLK_SIZE;
log::debug!("part: part_len={len:?}");
len
}
Handle::Disk => self.disk.size(),
},
))
}
fn fpath(&mut self, _id: usize, _buf: &mut [u8]) -> syscall::Result<Option<usize>> {
todo!()
}
fn fstat(&mut self, id: usize, _stat: &mut syscall::Stat) -> syscall::Result<Option<usize>> {
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List { .. } => Ok(Some(0)),
Handle::Disk { .. } | Handle::Partition { .. } => todo!(),
}
}
fn close(&mut self, id: usize) -> syscall::Result<Option<usize>> {
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}