diff --git a/src/libos/src/fs/dev_sgx.rs b/src/libos/src/fs/dev_sgx.rs new file mode 100644 index 00000000..8cf7b841 --- /dev/null +++ b/src/libos/src/fs/dev_sgx.rs @@ -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::(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::()?; + *arg = 0; // no support for now + } + _ => { + return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"); + } + } + Ok(()) + } + + fn as_any(&self) -> &Any { + self + } +} diff --git a/src/libos/src/fs/file.rs b/src/libos/src/fs/file.rs index cc57f9dc..7bc9f53d 100644 --- a/src/libos/src/fs/file.rs +++ b/src/libos/src/fs/file.rs @@ -67,6 +67,10 @@ pub trait File: Debug + Sync + Send + Any { Ok(()) } + fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> { + return_op_unsupported_error!("ioctl") + } + fn as_any(&self) -> &Any; } @@ -387,6 +391,32 @@ impl File for StdoutFile { 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 { self } diff --git a/src/libos/src/fs/ioctl/builtin.rs b/src/libos/src/fs/ioctl/builtin.rs new file mode 100644 index 00000000..cd4cd9ba --- /dev/null +++ b/src/libos/src/fs/ioctl/builtin.rs @@ -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, +} diff --git a/src/libos/src/fs/ioctl/macros.rs b/src/libos/src/fs/ioctl/macros.rs new file mode 100644 index 00000000..535e652c --- /dev/null +++ b/src/libos/src/fs/ioctl/macros.rs @@ -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 { + 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> { + 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> { + 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> { + 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 + }; +} diff --git a/src/libos/src/fs/ioctl/mod.rs b/src/libos/src/fs/ioctl/mod.rs new file mode 100644 index 00000000..501bdb2f --- /dev/null +++ b/src/libos/src/fs/ioctl/mod.rs @@ -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(()) + } +} diff --git a/src/libos/src/fs/ioctl/non_builtin.rs b/src/libos/src/fs/ioctl/non_builtin.rs new file mode 100644 index 00000000..8a2ce4a2 --- /dev/null +++ b/src/libos/src/fs/ioctl/non_builtin.rs @@ -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> { + 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(&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::() != 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(&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::() != 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( + 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::() <= (std::u16::MAX as usize)); + let arg_size = std::mem::size_of::() as u16; + StructuredIoctlNum { + cmd_id, + magic_char, + arg_size, + arg_type, + } + } + + pub fn from_u32(raw_cmd_num: u32) -> Result { + // 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 + } +} diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 0f788da5..76c17fb4 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -9,12 +9,14 @@ use {process, std}; pub use self::access::{do_access, do_faccessat, AccessFlags, AccessModes, AT_FDCWD}; use self::dev_null::DevNull; use self::dev_random::DevRandom; +use self::dev_sgx::DevSgx; use self::dev_zero::DevZero; pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile}; pub use self::file_table::{FileDesc, FileTable}; use self::inode_file::OpenOptions; pub use self::inode_file::{INodeExt, INodeFile}; pub use self::io_multiplexing::*; +pub use self::ioctl::*; pub use self::pipe::Pipe; pub use self::root_inode::ROOT_INODE; pub use self::socket_file::{AsSocket, SocketFile}; @@ -26,12 +28,14 @@ use std::mem::uninitialized; mod access; mod dev_null; mod dev_random; +mod dev_sgx; mod dev_zero; mod file; mod file_table; mod hostfs; mod inode_file; mod io_multiplexing; +mod ioctl; mod pipe; mod root_inode; mod sgx_impl; @@ -221,6 +225,14 @@ pub fn do_close(fd: FileDesc) -> Result<()> { 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]> { info!("pipe2: flags: {:#x}", flags); let flags = OpenFlags::from_bits_truncate(flags); @@ -434,6 +446,9 @@ impl Process { if path == "/dev/random" || path == "/dev/urandom" || path == "/dev/arandom" { return Ok(Box::new(DevRandom)); } + if path == "/dev/sgx" { + return Ok(Box::new(DevSgx)); + } let inode = if flags.contains(OpenFlags::CREATE) { let (dir_path, file_name) = split_path(&path); let dir_inode = self.lookup_inode(dir_path)?; diff --git a/src/libos/src/fs/socket_file.rs b/src/libos/src/fs/socket_file.rs index 772fceca..a4c90a5e 100644 --- a/src/libos/src/fs/socket_file.rs +++ b/src/libos/src/fs/socket_file.rs @@ -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 { self } diff --git a/src/libos/src/fs/unix_socket.rs b/src/libos/src/fs/unix_socket.rs index 9d77f509..dd984cbd 100644 --- a/src/libos/src/fs/unix_socket.rs +++ b/src/libos/src/fs/unix_socket.rs @@ -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 { self } @@ -118,11 +123,6 @@ impl UnixSocketFile { let mut inner = self.inner.lock().unwrap(); 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 { @@ -231,18 +231,19 @@ impl UnixSocket { Ok((r, w, false)) } - pub fn ioctl(&self, cmd: c_int, argp: *mut c_int) -> Result<()> { - const FIONREAD: c_int = 0x541B; // Get the number of bytes to read - if cmd == FIONREAD { - let bytes_to_read = self.channel()?.reader.bytes_to_read(); - unsafe { - argp.write(bytes_to_read as c_int); + pub fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> { + match cmd { + IoctlCmd::FIONREAD(arg) => { + let bytes_to_read = self + .channel()? + .reader + .bytes_to_read() + .min(std::i32::MAX as usize) as i32; + **arg = bytes_to_read; } - Ok(()) - } else { - warn!("ioctl for unix socket is unimplemented"); - return_errno!(ENOSYS, "ioctl for unix socket is unimplemented") + _ => return_errno!(EINVAL, "unknown ioctl cmd for unix socket"), } + Ok(()) } fn channel(&self) -> Result<&Channel> { diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 03e0ff64..ce64829c 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -106,7 +106,7 @@ pub extern "C" fn dispatch_syscall( arg3 as usize, ), 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 SYS_SELECT => do_select( @@ -962,22 +962,16 @@ fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result { fs::do_fcntl(fd, &cmd) } -fn do_ioctl(fd: FileDesc, cmd: c_int, argp: *mut c_int) -> Result { +fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result { info!("ioctl: fd: {}, cmd: {}, argp: {:?}", fd, cmd, argp); - let current_ref = process::get_current(); - let mut proc = current_ref.lock().unwrap(); - let file_ref = proc.get_files().lock().unwrap().get(fd as FileDesc)?; - 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") - } + let mut ioctl_cmd = unsafe { + if argp.is_null() == false { + check_mut_ptr(argp)?; + } + IoctlCmd::new(cmd, argp)? + }; + fs::do_ioctl(fd, &mut ioctl_cmd)?; + Ok(0) } fn do_arch_prctl(code: u32, addr: *mut usize) -> Result { diff --git a/test/Makefile b/test/Makefile index 7d2bf578..ac5f925f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,8 @@ TEST_DEPS := dev_null client # 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 \ 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 BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/ioctl/Makefile b/test/ioctl/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/ioctl/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/ioctl/main.c b/test/ioctl/main.c new file mode 100644 index 00000000..326bd319 --- /dev/null +++ b/test/ioctl/main.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#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)); +}