Introduce new infrastructure for ioctls
1. Introduce the new infrastructure for ioctl support 2. Refactor the old ioctls to use the new infrastructure 3. Implement builtin ioctls (e.g., TIOCGWINSZ and TIOCSWINSZ for stdout) 4. Implement non-builtin, driver-specific ioctls (e.g., ioctls for /dev/sgx)
This commit is contained in:
parent
1024360b8c
commit
9c4391b32d
34
src/libos/src/fs/dev_sgx.rs
Normal file
34
src/libos/src/fs/dev_sgx.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DevSgx;
|
||||||
|
|
||||||
|
const SGX_MAGIC_CHAR: u8 = 's' as u8;
|
||||||
|
|
||||||
|
/// Ioctl to check if EDMM (Enclave Dynamic Memory Management) is supported
|
||||||
|
const SGX_CMD_NUM_IS_EDMM_SUPPORTED: u32 =
|
||||||
|
StructuredIoctlNum::new::<i32>(0, SGX_MAGIC_CHAR, StructuredIoctlArgType::Output).as_u32();
|
||||||
|
|
||||||
|
impl File for DevSgx {
|
||||||
|
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
|
let nonbuiltin_cmd = match cmd {
|
||||||
|
IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd,
|
||||||
|
_ => return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"),
|
||||||
|
};
|
||||||
|
let cmd_num = nonbuiltin_cmd.cmd_num().as_u32();
|
||||||
|
match cmd_num {
|
||||||
|
SGX_CMD_NUM_IS_EDMM_SUPPORTED => {
|
||||||
|
let arg = nonbuiltin_cmd.arg_mut::<i32>()?;
|
||||||
|
*arg = 0; // no support for now
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
@ -67,6 +67,10 @@ pub trait File: Debug + Sync + Send + Any {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
|
return_op_unsupported_error!("ioctl")
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &Any;
|
fn as_any(&self) -> &Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +391,32 @@ impl File for StdoutFile {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
|
let can_delegate_to_host = match cmd {
|
||||||
|
IoctlCmd::TIOCGWINSZ(_) => true,
|
||||||
|
IoctlCmd::TIOCSWINSZ(_) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if !can_delegate_to_host {
|
||||||
|
return_errno!(EINVAL, "unknown ioctl cmd for stdout");
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmd_bits = cmd.cmd_num() as c_int;
|
||||||
|
let cmd_arg_ptr = cmd.arg_ptr() as *const c_int;
|
||||||
|
let host_stdout_fd = {
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
self.inner.as_raw_fd() as i32
|
||||||
|
};
|
||||||
|
try_libc!(libc::ocall::ioctl_arg1(
|
||||||
|
host_stdout_fd,
|
||||||
|
cmd_bits,
|
||||||
|
cmd_arg_ptr
|
||||||
|
));
|
||||||
|
cmd.validate_arg_val()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &Any {
|
fn as_any(&self) -> &Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
12
src/libos/src/fs/ioctl/builtin.rs
Normal file
12
src/libos/src/fs/ioctl/builtin.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//! Built-in ioctls.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct WinSize {
|
||||||
|
pub ws_row: u16,
|
||||||
|
pub ws_col: u16,
|
||||||
|
pub ws_xpixel: u16,
|
||||||
|
pub ws_ypixel: u16,
|
||||||
|
}
|
167
src/libos/src/fs/ioctl/macros.rs
Normal file
167
src/libos/src/fs/ioctl/macros.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
//! Macros to implement `BuiltinIoctlNum` and `IoctlCmd` given a list of ioctl
|
||||||
|
//! names, numbers, and argument types.
|
||||||
|
|
||||||
|
/// Implement `BuiltinIoctlNum` and `IoctlCmd`.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_ioctl_nums_and_cmds {
|
||||||
|
($( $ioctl_name: ident => ( $ioctl_num: expr, $($ioctl_type_tt: tt)* ) ),+,) => {
|
||||||
|
// Implement BuiltinIoctlNum given ioctl names and their numbers
|
||||||
|
impl_builtin_ioctl_nums! {
|
||||||
|
$(
|
||||||
|
$ioctl_name => ( $ioctl_num, has_arg!($($ioctl_type_tt)*) ),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement IoctlCmd given ioctl names and their argument types
|
||||||
|
impl_ioctl_cmds! {
|
||||||
|
$(
|
||||||
|
$ioctl_name => ( $($ioctl_type_tt)*),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// BuiltinIoctlNum
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
macro_rules! impl_builtin_ioctl_nums {
|
||||||
|
($($ioctl_name: ident => ($ioctl_num: expr, $ioctl_has_arg: expr)),+,) => {
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum BuiltinIoctlNum {
|
||||||
|
$(
|
||||||
|
$ioctl_name = $ioctl_num,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinIoctlNum {
|
||||||
|
pub fn from_u32(raw_cmd_num: u32) -> Option<BuiltinIoctlNum> {
|
||||||
|
let cmd_num = match raw_cmd_num {
|
||||||
|
$(
|
||||||
|
$ioctl_num => BuiltinIoctlNum::$ioctl_name,
|
||||||
|
)*
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(cmd_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn require_arg(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
BuiltinIoctlNum::$ioctl_name => $ioctl_has_arg,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// IoctlCmd
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
macro_rules! impl_ioctl_cmds {
|
||||||
|
($( $ioctl_name: ident => ( $($ioctl_type_tt: tt)* ) ),+,) => {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum IoctlCmd<'a> {
|
||||||
|
$(
|
||||||
|
$ioctl_name( get_arg_type!($($ioctl_type_tt)*) ),
|
||||||
|
)*
|
||||||
|
NonBuiltin(NonBuiltinIoctlCmd<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IoctlCmd<'a> {
|
||||||
|
pub unsafe fn new(cmd_num: u32, arg_ptr: *mut u8) -> Result<IoctlCmd<'a>> {
|
||||||
|
if let Some(builtin_cmd_num) = BuiltinIoctlNum::from_u32(cmd_num) {
|
||||||
|
unsafe { Self::new_builtin_cmd(builtin_cmd_num, arg_ptr) }
|
||||||
|
} else {
|
||||||
|
unsafe { Self::new_nonbuiltin_cmd(cmd_num, arg_ptr) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn new_builtin_cmd(cmd_num: BuiltinIoctlNum, arg_ptr: *mut u8) -> Result<IoctlCmd<'a>> {
|
||||||
|
if cmd_num.require_arg() && arg_ptr.is_null() {
|
||||||
|
return_errno!(EINVAL, "arg_ptr cannot be null");
|
||||||
|
}
|
||||||
|
// Note that we do allow the caller to give an non-enull arg even
|
||||||
|
// when the ioctl cmd does not take an arguement
|
||||||
|
|
||||||
|
let cmd = match cmd_num {
|
||||||
|
$(
|
||||||
|
BuiltinIoctlNum::$ioctl_name => {
|
||||||
|
let arg = get_arg!($($ioctl_type_tt)*, arg_ptr);
|
||||||
|
IoctlCmd::$ioctl_name(arg)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
Ok(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn new_nonbuiltin_cmd(cmd_num: u32, arg_ptr: *mut u8) -> Result<IoctlCmd<'a>> {
|
||||||
|
let structured_cmd_num = StructuredIoctlNum::from_u32(cmd_num)?;
|
||||||
|
let inner_cmd = unsafe { NonBuiltinIoctlCmd::new(structured_cmd_num, arg_ptr)? };
|
||||||
|
Ok(IoctlCmd::NonBuiltin(inner_cmd))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg_ptr(&self) -> *const u8 {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
IoctlCmd::$ioctl_name(arg_ref) => get_arg_ptr!($($ioctl_type_tt)*, arg_ref),
|
||||||
|
)*
|
||||||
|
IoctlCmd::NonBuiltin(inner) => inner.arg_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmd_num(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
IoctlCmd::$ioctl_name(_) => BuiltinIoctlNum::$ioctl_name as u32,
|
||||||
|
)*
|
||||||
|
IoctlCmd::NonBuiltin(inner) => inner.cmd_num().as_u32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! has_arg {
|
||||||
|
(()) => {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
($($ioctl_type_tt: tt)*) => {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_arg_type {
|
||||||
|
(()) => {
|
||||||
|
()
|
||||||
|
};
|
||||||
|
($($ioctl_type_tt: tt)*) => {
|
||||||
|
&'a $($ioctl_type_tt)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_arg {
|
||||||
|
((), $arg_ptr: ident) => {
|
||||||
|
()
|
||||||
|
};
|
||||||
|
(mut $type: ty, $arg_ptr: ident) => {
|
||||||
|
unsafe { &mut *($arg_ptr as *mut $type) }
|
||||||
|
};
|
||||||
|
($type: ty, $arg_ptr: ident) => {
|
||||||
|
unsafe { &*($arg_ptr as *const $type) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_arg_ptr {
|
||||||
|
((), $arg_ref: ident) => {
|
||||||
|
std::ptr::null() as *const u8
|
||||||
|
};
|
||||||
|
(mut $type: ty, $arg_ref: ident) => {
|
||||||
|
(*$arg_ref as *const $type) as *const u8
|
||||||
|
};
|
||||||
|
($type: ty, $arg_ref: ident) => {
|
||||||
|
(*$arg_ref as *const $type) as *const u8
|
||||||
|
};
|
||||||
|
}
|
65
src/libos/src/fs/ioctl/mod.rs
Normal file
65
src/libos/src/fs/ioctl/mod.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//! Define builtin ioctls and provide utilities for non-builtin ioctls.
|
||||||
|
//!
|
||||||
|
//! A builtin ioctl is defined as part of the OS kernel and is used by various
|
||||||
|
//! OS sub-system. In contrast, an non-builtin ioctl is specific to a device or
|
||||||
|
//! driver.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use self::builtin::WinSize;
|
||||||
|
pub use self::non_builtin::{NonBuiltinIoctlCmd, StructuredIoctlArgType, StructuredIoctlNum};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
mod builtin;
|
||||||
|
mod non_builtin;
|
||||||
|
|
||||||
|
/// This is the centralized place to define built-in ioctls.
|
||||||
|
///
|
||||||
|
/// By giving the names, numbers, and argument types of built-in ioctls,
|
||||||
|
/// the macro below generates the corresponding code of `BuiltinIoctlNum` and
|
||||||
|
/// `IoctlCmd`.
|
||||||
|
///
|
||||||
|
/// To add a new built-in ioctl, just follow the convention as shown
|
||||||
|
/// by existing built-in ioctls.
|
||||||
|
impl_ioctl_nums_and_cmds! {
|
||||||
|
// Format:
|
||||||
|
// ioctl_name => (ioctl_num, ioctl_type_arg)
|
||||||
|
|
||||||
|
// Get window size
|
||||||
|
TIOCGWINSZ => (0x5413, mut WinSize),
|
||||||
|
// Set window size
|
||||||
|
TIOCSWINSZ => (0x5414, WinSize),
|
||||||
|
// If the given terminal was the controlling terminal of the calling process, give up this
|
||||||
|
// controlling terminal. If the process was session leader, then send SIGHUP and SIGCONT to
|
||||||
|
// the foreground process group and all processes in the current session lose their controlling
|
||||||
|
// terminal
|
||||||
|
TIOCNOTTY => (0x5422, ()),
|
||||||
|
// Get the number of bytes in the input buffer
|
||||||
|
FIONREAD => (0x541B, mut i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the centralized place to add sanity checks for the argument values
|
||||||
|
/// of built-in ioctls.
|
||||||
|
///
|
||||||
|
/// Sanity checks are mostly useful when the argument values are returned from
|
||||||
|
/// the untrusted host OS.
|
||||||
|
impl<'a> IoctlCmd<'a> {
|
||||||
|
pub fn validate_arg_val(&self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
IoctlCmd::TIOCGWINSZ(winsize_ref) => {
|
||||||
|
// ws_row and ws_col are not supposed to be zeros
|
||||||
|
if winsize_ref.ws_row == 0 || winsize_ref.ws_col == 0 {
|
||||||
|
return_errno!(EINVAL, "invalid data from host");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IoctlCmd::FIONREAD(nread_ref) => {
|
||||||
|
if (**nread_ref < 0) {
|
||||||
|
return_errno!(EINVAL, "invalid data from host");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
176
src/libos/src/fs/ioctl/non_builtin.rs
Normal file
176
src/libos/src/fs/ioctl/non_builtin.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
//! Non-builtin ioctls.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NonBuiltinIoctlCmd<'a> {
|
||||||
|
cmd_num: StructuredIoctlNum,
|
||||||
|
arg_buf: Option<&'a mut [u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NonBuiltinIoctlCmd<'a> {
|
||||||
|
pub unsafe fn new(
|
||||||
|
cmd_num: StructuredIoctlNum,
|
||||||
|
arg_ptr: *mut u8,
|
||||||
|
) -> Result<NonBuiltinIoctlCmd<'a>> {
|
||||||
|
let arg_buf = if cmd_num.require_arg() {
|
||||||
|
if arg_ptr.is_null() {
|
||||||
|
return_errno!(EINVAL, "arg_ptr must be provided for the ioctl");
|
||||||
|
}
|
||||||
|
let arg_size = cmd_num.arg_size();
|
||||||
|
let arg_slice = unsafe { std::slice::from_raw_parts_mut::<'a>(arg_ptr, arg_size) };
|
||||||
|
Some(arg_slice)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(NonBuiltinIoctlCmd { cmd_num, arg_buf })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmd_num(&self) -> &StructuredIoctlNum {
|
||||||
|
&self.cmd_num
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg<T>(&self) -> Result<&T> {
|
||||||
|
if self.cmd_num.arg_type().can_be_input() == false {
|
||||||
|
return_errno!(EINVAL, "cannot get a constant argument");
|
||||||
|
}
|
||||||
|
if std::mem::size_of::<T>() != self.cmd_num.arg_size() {
|
||||||
|
return_errno!(
|
||||||
|
EINVAL,
|
||||||
|
"the size of target type does not match the given buf size"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_ref = unsafe { &*(self.arg_buf.as_ref().unwrap().as_ptr() as *const T) };
|
||||||
|
Ok(arg_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg_mut<T>(&mut self) -> Result<&mut T> {
|
||||||
|
if self.cmd_num.arg_type().can_be_output() == false {
|
||||||
|
return_errno!(EINVAL, "cannot get a mutable argument");
|
||||||
|
}
|
||||||
|
if std::mem::size_of::<T>() != self.cmd_num.arg_size() {
|
||||||
|
return_errno!(
|
||||||
|
EINVAL,
|
||||||
|
"the size of target type does not match the given buf size"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_mut = unsafe { &mut *(self.arg_buf.as_mut().unwrap().as_mut_ptr() as *mut T) };
|
||||||
|
Ok(arg_mut)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg_ptr(&self) -> *const u8 {
|
||||||
|
self.arg_buf
|
||||||
|
.as_ref()
|
||||||
|
.map_or(std::ptr::null(), |arg_slice| arg_slice.as_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct StructuredIoctlNum {
|
||||||
|
cmd_id: u8,
|
||||||
|
magic_char: u8,
|
||||||
|
arg_size: u16,
|
||||||
|
arg_type: StructuredIoctlArgType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructuredIoctlNum {
|
||||||
|
pub const fn new<T>(
|
||||||
|
cmd_id: u8,
|
||||||
|
magic_char: u8,
|
||||||
|
arg_type: StructuredIoctlArgType,
|
||||||
|
) -> StructuredIoctlNum {
|
||||||
|
// TODO: make sure the size of T is not too big
|
||||||
|
// assert!(std::mem::size_of::<T>() <= (std::u16::MAX as usize));
|
||||||
|
let arg_size = std::mem::size_of::<T>() as u16;
|
||||||
|
StructuredIoctlNum {
|
||||||
|
cmd_id,
|
||||||
|
magic_char,
|
||||||
|
arg_size,
|
||||||
|
arg_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_u32(raw_cmd_num: u32) -> Result<StructuredIoctlNum> {
|
||||||
|
// bits: [0, 8)
|
||||||
|
let cmd_id = (raw_cmd_num >> 0) as u8;
|
||||||
|
// bits: [8, 16)
|
||||||
|
let magic_char = (raw_cmd_num >> 8) as u8;
|
||||||
|
// bits: [16, 30)
|
||||||
|
let arg_size = ((raw_cmd_num >> 16) as u16) & 0x3FFF_u16;
|
||||||
|
// bits: [30, 32)
|
||||||
|
let arg_type = {
|
||||||
|
let type_bits = ((raw_cmd_num) >> 30) as u8;
|
||||||
|
match type_bits {
|
||||||
|
0 => StructuredIoctlArgType::Void,
|
||||||
|
1 => StructuredIoctlArgType::Input,
|
||||||
|
2 => StructuredIoctlArgType::Output,
|
||||||
|
3 => StructuredIoctlArgType::InputOutput,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if arg_type == StructuredIoctlArgType::Void {
|
||||||
|
if arg_size != 0 {
|
||||||
|
return_errno!(EINVAL, "invalid combination between type and size");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if arg_size == 0 {
|
||||||
|
return_errno!(EINVAL, "invalid combination between type and size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(StructuredIoctlNum {
|
||||||
|
cmd_id,
|
||||||
|
magic_char,
|
||||||
|
arg_size,
|
||||||
|
arg_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn as_u32(&self) -> u32 {
|
||||||
|
(self.cmd_id as u32)
|
||||||
|
| (self.magic_char as u32) << 8
|
||||||
|
| (self.arg_size as u32) << 16
|
||||||
|
| (self.arg_type as u32) << 30
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn require_arg(&self) -> bool {
|
||||||
|
self.arg_type != StructuredIoctlArgType::Void
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmd_id(&self) -> u8 {
|
||||||
|
self.cmd_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn magic_char(&self) -> u8 {
|
||||||
|
self.magic_char
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg_size(&self) -> usize {
|
||||||
|
self.arg_size as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg_type(&self) -> StructuredIoctlArgType {
|
||||||
|
self.arg_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum StructuredIoctlArgType {
|
||||||
|
Void = 0,
|
||||||
|
Output = 1,
|
||||||
|
Input = 2,
|
||||||
|
InputOutput = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructuredIoctlArgType {
|
||||||
|
pub fn can_be_input(&self) -> bool {
|
||||||
|
*self == StructuredIoctlArgType::Input || *self == StructuredIoctlArgType::InputOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_be_output(&self) -> bool {
|
||||||
|
*self == StructuredIoctlArgType::Output || *self == StructuredIoctlArgType::InputOutput
|
||||||
|
}
|
||||||
|
}
|
@ -9,12 +9,14 @@ use {process, std};
|
|||||||
pub use self::access::{do_access, do_faccessat, AccessFlags, AccessModes, AT_FDCWD};
|
pub use self::access::{do_access, do_faccessat, AccessFlags, AccessModes, AT_FDCWD};
|
||||||
use self::dev_null::DevNull;
|
use self::dev_null::DevNull;
|
||||||
use self::dev_random::DevRandom;
|
use self::dev_random::DevRandom;
|
||||||
|
use self::dev_sgx::DevSgx;
|
||||||
use self::dev_zero::DevZero;
|
use self::dev_zero::DevZero;
|
||||||
pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile};
|
pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile};
|
||||||
pub use self::file_table::{FileDesc, FileTable};
|
pub use self::file_table::{FileDesc, FileTable};
|
||||||
use self::inode_file::OpenOptions;
|
use self::inode_file::OpenOptions;
|
||||||
pub use self::inode_file::{INodeExt, INodeFile};
|
pub use self::inode_file::{INodeExt, INodeFile};
|
||||||
pub use self::io_multiplexing::*;
|
pub use self::io_multiplexing::*;
|
||||||
|
pub use self::ioctl::*;
|
||||||
pub use self::pipe::Pipe;
|
pub use self::pipe::Pipe;
|
||||||
pub use self::root_inode::ROOT_INODE;
|
pub use self::root_inode::ROOT_INODE;
|
||||||
pub use self::socket_file::{AsSocket, SocketFile};
|
pub use self::socket_file::{AsSocket, SocketFile};
|
||||||
@ -26,12 +28,14 @@ use std::mem::uninitialized;
|
|||||||
mod access;
|
mod access;
|
||||||
mod dev_null;
|
mod dev_null;
|
||||||
mod dev_random;
|
mod dev_random;
|
||||||
|
mod dev_sgx;
|
||||||
mod dev_zero;
|
mod dev_zero;
|
||||||
mod file;
|
mod file;
|
||||||
mod file_table;
|
mod file_table;
|
||||||
mod hostfs;
|
mod hostfs;
|
||||||
mod inode_file;
|
mod inode_file;
|
||||||
mod io_multiplexing;
|
mod io_multiplexing;
|
||||||
|
mod ioctl;
|
||||||
mod pipe;
|
mod pipe;
|
||||||
mod root_inode;
|
mod root_inode;
|
||||||
mod sgx_impl;
|
mod sgx_impl;
|
||||||
@ -221,6 +225,14 @@ pub fn do_close(fd: FileDesc) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_ioctl(fd: FileDesc, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
|
info!("ioctl: fd: {}, cmd: {:?}", fd, cmd);
|
||||||
|
let current_ref = process::get_current();
|
||||||
|
let current_process = current_ref.lock().unwrap();
|
||||||
|
let file_ref = current_process.get_files().lock().unwrap().get(fd)?;
|
||||||
|
file_ref.ioctl(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn do_pipe2(flags: u32) -> Result<[FileDesc; 2]> {
|
pub fn do_pipe2(flags: u32) -> Result<[FileDesc; 2]> {
|
||||||
info!("pipe2: flags: {:#x}", flags);
|
info!("pipe2: flags: {:#x}", flags);
|
||||||
let flags = OpenFlags::from_bits_truncate(flags);
|
let flags = OpenFlags::from_bits_truncate(flags);
|
||||||
@ -434,6 +446,9 @@ impl Process {
|
|||||||
if path == "/dev/random" || path == "/dev/urandom" || path == "/dev/arandom" {
|
if path == "/dev/random" || path == "/dev/urandom" || path == "/dev/arandom" {
|
||||||
return Ok(Box::new(DevRandom));
|
return Ok(Box::new(DevRandom));
|
||||||
}
|
}
|
||||||
|
if path == "/dev/sgx" {
|
||||||
|
return Ok(Box::new(DevSgx));
|
||||||
|
}
|
||||||
let inode = if flags.contains(OpenFlags::CREATE) {
|
let inode = if flags.contains(OpenFlags::CREATE) {
|
||||||
let (dir_path, file_name) = split_path(&path);
|
let (dir_path, file_name) = split_path(&path);
|
||||||
let dir_inode = self.lookup_inode(dir_path)?;
|
let dir_inode = self.lookup_inode(dir_path)?;
|
||||||
|
@ -119,6 +119,15 @@ impl File for SocketFile {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
|
let cmd_num = cmd.cmd_num() as c_int;
|
||||||
|
let cmd_arg_ptr = cmd.arg_ptr() as *const c_int;
|
||||||
|
try_libc!(libc::ocall::ioctl_arg1(self.fd(), cmd_num, cmd_arg_ptr));
|
||||||
|
// FIXME: add sanity checks for results returned for socket-related ioctls
|
||||||
|
cmd.validate_arg_val()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &Any {
|
fn as_any(&self) -> &Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,11 @@ impl File for UnixSocketFile {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.ioctl(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &Any {
|
fn as_any(&self) -> &Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -118,11 +123,6 @@ impl UnixSocketFile {
|
|||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
inner.poll()
|
inner.poll()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ioctl(&self, cmd: c_int, argp: *mut c_int) -> Result<()> {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.ioctl(cmd, argp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for UnixSocketFile {
|
impl Debug for UnixSocketFile {
|
||||||
@ -231,18 +231,19 @@ impl UnixSocket {
|
|||||||
Ok((r, w, false))
|
Ok((r, w, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ioctl(&self, cmd: c_int, argp: *mut c_int) -> Result<()> {
|
pub fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> {
|
||||||
const FIONREAD: c_int = 0x541B; // Get the number of bytes to read
|
match cmd {
|
||||||
if cmd == FIONREAD {
|
IoctlCmd::FIONREAD(arg) => {
|
||||||
let bytes_to_read = self.channel()?.reader.bytes_to_read();
|
let bytes_to_read = self
|
||||||
unsafe {
|
.channel()?
|
||||||
argp.write(bytes_to_read as c_int);
|
.reader
|
||||||
|
.bytes_to_read()
|
||||||
|
.min(std::i32::MAX as usize) as i32;
|
||||||
|
**arg = bytes_to_read;
|
||||||
|
}
|
||||||
|
_ => return_errno!(EINVAL, "unknown ioctl cmd for unix socket"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
warn!("ioctl for unix socket is unimplemented");
|
|
||||||
return_errno!(ENOSYS, "ioctl for unix socket is unimplemented")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn channel(&self) -> Result<&Channel> {
|
fn channel(&self) -> Result<&Channel> {
|
||||||
|
@ -106,7 +106,7 @@ pub extern "C" fn dispatch_syscall(
|
|||||||
arg3 as usize,
|
arg3 as usize,
|
||||||
),
|
),
|
||||||
SYS_FCNTL => do_fcntl(arg0 as FileDesc, arg1 as u32, arg2 as u64),
|
SYS_FCNTL => do_fcntl(arg0 as FileDesc, arg1 as u32, arg2 as u64),
|
||||||
SYS_IOCTL => do_ioctl(arg0 as FileDesc, arg1 as c_int, arg2 as *mut c_int),
|
SYS_IOCTL => do_ioctl(arg0 as FileDesc, arg1 as u32, arg2 as *mut u8),
|
||||||
|
|
||||||
// IO multiplexing
|
// IO multiplexing
|
||||||
SYS_SELECT => do_select(
|
SYS_SELECT => do_select(
|
||||||
@ -962,22 +962,16 @@ fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result<isize> {
|
|||||||
fs::do_fcntl(fd, &cmd)
|
fs::do_fcntl(fd, &cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_ioctl(fd: FileDesc, cmd: c_int, argp: *mut c_int) -> Result<isize> {
|
fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result<isize> {
|
||||||
info!("ioctl: fd: {}, cmd: {}, argp: {:?}", fd, cmd, argp);
|
info!("ioctl: fd: {}, cmd: {}, argp: {:?}", fd, cmd, argp);
|
||||||
let current_ref = process::get_current();
|
let mut ioctl_cmd = unsafe {
|
||||||
let mut proc = current_ref.lock().unwrap();
|
if argp.is_null() == false {
|
||||||
let file_ref = proc.get_files().lock().unwrap().get(fd as FileDesc)?;
|
check_mut_ptr(argp)?;
|
||||||
if let Ok(socket) = file_ref.as_socket() {
|
|
||||||
let ret = try_libc!(libc::ocall::ioctl_arg1(socket.fd(), cmd, argp));
|
|
||||||
Ok(ret as isize)
|
|
||||||
} else if let Ok(unix_socket) = file_ref.as_unix_socket() {
|
|
||||||
// TODO: check argp
|
|
||||||
unix_socket.ioctl(cmd, argp)?;
|
|
||||||
Ok(0)
|
|
||||||
} else {
|
|
||||||
warn!("ioctl is unimplemented");
|
|
||||||
return_errno!(ENOSYS, "ioctl is unimplemented")
|
|
||||||
}
|
}
|
||||||
|
IoctlCmd::new(cmd, argp)?
|
||||||
|
};
|
||||||
|
fs::do_ioctl(fd, &mut ioctl_cmd)?;
|
||||||
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_arch_prctl(code: u32, addr: *mut usize) -> Result<isize> {
|
fn do_arch_prctl(code: u32, addr: *mut usize) -> Result<isize> {
|
||||||
|
@ -7,7 +7,8 @@ TEST_DEPS := dev_null client
|
|||||||
# Tests: need to be compiled and run by test-% target
|
# Tests: need to be compiled and run by test-% target
|
||||||
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
|
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
|
||||||
truncate readdir mkdir link tls pthread uname rlimit server \
|
truncate readdir mkdir link tls pthread uname rlimit server \
|
||||||
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group
|
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
|
||||||
|
ioctl
|
||||||
# Benchmarks: need to be compiled and run by bench-% target
|
# Benchmarks: need to be compiled and run by bench-% target
|
||||||
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
||||||
|
|
||||||
|
5
test/ioctl/Makefile
Normal file
5
test/ioctl/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include ../test_common.mk
|
||||||
|
|
||||||
|
EXTRA_C_FLAGS :=
|
||||||
|
EXTRA_LINK_FLAGS :=
|
||||||
|
BIN_ARGS :=
|
57
test/ioctl/main.c
Normal file
57
test/ioctl/main.c
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test cases for TTY ioctl
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
int test_tty_ioctl_TIOCGWINSZ(void) {
|
||||||
|
struct winsize winsize;
|
||||||
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) < 0) {
|
||||||
|
throw_error("failed to ioctl TIOCGWINSZ");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test cases for SGX ioctl
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#define SGXIOC_IS_EDDM_SUPPORTED _IOR('s', 0, int)
|
||||||
|
|
||||||
|
int test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED(void) {
|
||||||
|
int sgx_fd;
|
||||||
|
if ((sgx_fd = open("/dev/sgx", O_RDONLY)) < 0) {
|
||||||
|
throw_error("failed to open /dev/sgx ");
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_edmm_supported = 0;
|
||||||
|
if (ioctl(sgx_fd, SGXIOC_IS_EDDM_SUPPORTED, &is_edmm_supported) < 0) {
|
||||||
|
throw_error("failed to ioctl /dev/sgx");
|
||||||
|
}
|
||||||
|
if (is_edmm_supported != 0) {
|
||||||
|
throw_error("SGX EDMM supported are not expected to be enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sgx_fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test suite
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static test_case_t test_cases[] = {
|
||||||
|
TEST_CASE(test_tty_ioctl_TIOCGWINSZ),
|
||||||
|
TEST_CASE(test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED)
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user