Migrate to new ioctl infrastructure
This commit is contained in:
parent
99fefb7adf
commit
4e76ed5fea
|
|
@ -6,8 +6,11 @@ use aster_rights::{Dup, Exec, Full, Read, Signal, TRightSet, TRights, Write};
|
|||
use aster_rights_proc::require;
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
use ostd::{
|
||||
mm::{Daddr, DmaStream, HasDaddr, HasPaddr, Paddr, PodOnce, VmIo, VmIoOnce},
|
||||
Pod, Result,
|
||||
mm::{
|
||||
io_util::{HasVmReaderWriter, VmReaderWriterTypes},
|
||||
Daddr, DmaStream, HasDaddr, HasPaddr, Paddr, PodOnce, VmIo, VmIoOnce,
|
||||
},
|
||||
Error, Pod, Result,
|
||||
};
|
||||
|
||||
/// Safe pointers.
|
||||
|
|
@ -180,7 +183,7 @@ impl<T, M: HasPaddr, R> HasPaddr for SafePtr<T, M, R> {
|
|||
|
||||
// =============== Read and write methods ==============
|
||||
impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
|
||||
/// Read the value from the pointer.
|
||||
/// Reads the value from the pointer.
|
||||
///
|
||||
/// # Access rights
|
||||
///
|
||||
|
|
@ -190,7 +193,7 @@ impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
|
|||
self.vm_obj.read_val(self.offset)
|
||||
}
|
||||
|
||||
/// Read a slice of values from the pointer.
|
||||
/// Reads a slice of values from the pointer.
|
||||
///
|
||||
/// # Access rights
|
||||
///
|
||||
|
|
@ -200,7 +203,7 @@ impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
|
|||
self.vm_obj.read_slice(self.offset, slice)
|
||||
}
|
||||
|
||||
/// Overwrite the value at the pointer.
|
||||
/// Overwrites the value at the pointer.
|
||||
///
|
||||
/// # Access rights
|
||||
///
|
||||
|
|
@ -210,7 +213,7 @@ impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
|
|||
self.vm_obj.write_val(self.offset, val)
|
||||
}
|
||||
|
||||
/// Overwrite a slice of values at the pointer.
|
||||
/// Overwrites a slice of values at the pointer.
|
||||
///
|
||||
/// # Access rights
|
||||
///
|
||||
|
|
@ -219,6 +222,35 @@ impl<T: Pod, M: VmIo, R: TRights> SafePtr<T, M, TRightSet<R>> {
|
|||
pub fn write_slice(&self, slice: &[T]) -> Result<()> {
|
||||
self.vm_obj.write_slice(self.offset, slice)
|
||||
}
|
||||
|
||||
/// Copies the value from another pointer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method will fail if
|
||||
/// - either pointer's underlying VM object is too short to hold `T` or
|
||||
/// - the memory copy between two pointers fails.
|
||||
///
|
||||
/// # Access rights
|
||||
///
|
||||
/// This method requires
|
||||
/// - the Write right for this pointer and
|
||||
/// - the Read right for another pointer.
|
||||
#[require(R > Write)]
|
||||
#[require(R1 > Read)]
|
||||
pub fn copy_from<M1: HasVmReaderWriter, R1: TRights>(
|
||||
&self,
|
||||
ptr: &SafePtr<T, M1, TRightSet<R1>>,
|
||||
) -> Result<()> {
|
||||
let mut reader = M1::Types::to_reader_result(ptr.vm_obj.reader())?.to_fallible();
|
||||
|
||||
if reader.remain() < size_of::<T>() {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
reader.limit(size_of::<T>());
|
||||
|
||||
self.vm_obj.write(self.offset, &mut reader)
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Read and write methods ==============
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@ use crate::{
|
|||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
util::ring_buffer::{RbConsumer, RbProducer, RingBuffer},
|
||||
util::{
|
||||
ioctl::RawIoctl,
|
||||
ring_buffer::{RbConsumer, RbProducer, RingBuffer},
|
||||
},
|
||||
};
|
||||
|
||||
pub(super) const EVDEV_BUFFER_SIZE: usize = 64;
|
||||
|
|
@ -289,7 +292,7 @@ impl FileIo for EvdevFile {
|
|||
false
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
// TODO: Support ioctl operations for evdev files.
|
||||
|
||||
// Most ioctl implementations return `ENOTTY` for invalid ioctl commands, representing "The
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@ use crate::{
|
|||
device::{Device, DeviceType},
|
||||
file_handle::Mappable,
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -202,6 +203,23 @@ struct FbCmapUser {
|
|||
pub transp: usize,
|
||||
}
|
||||
|
||||
mod ioctl_defs {
|
||||
use super::{FbCmapUser, FbFixScreenInfo, FbVarScreenInfo};
|
||||
use crate::util::ioctl::{ioc, InData, InOutData, NoData, OutData};
|
||||
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/fb.h#L13-L38>
|
||||
|
||||
pub(super) type GetVarScreenInfo = ioc!(FBIOGET_VSCREENINFO, 0x4600, OutData<FbVarScreenInfo>);
|
||||
pub(super) type PutVarScreenInfo = ioc!(FBIOPUT_VSCREENINFO, 0x4601, InOutData<FbVarScreenInfo>);
|
||||
pub(super) type GetFixScreenInfo = ioc!(FBIOGET_FSCREENINFO, 0x4602, OutData<FbFixScreenInfo>);
|
||||
pub(super) type GetColorMap = ioc!(FBIOGETCMAP, 0x4604, InData<FbCmapUser>);
|
||||
pub(super) type PutColorMap = ioc!(FBIOPUTCMAP, 0x4605, InData<FbCmapUser>);
|
||||
|
||||
// `NoData` is used below because they're not supported by efifb.
|
||||
pub(super) type PanDisplay = ioc!(FBIOPAN_DISPLAY, 0x4606, NoData);
|
||||
pub(super) type Blank = ioc!(FBIOBLANK, 0x4611, NoData);
|
||||
}
|
||||
|
||||
impl Device for Fb {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
|
|
@ -247,12 +265,8 @@ impl FbHandle {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles the [`IoctlCmd::GETVSCREENINFO`] ioctl command.
|
||||
///
|
||||
/// Arguments:
|
||||
/// - Input: None.
|
||||
/// - Output: [`FbVarScreenInfo`].
|
||||
fn handle_get_var_screen_info(&self, arg: usize) -> Result<i32> {
|
||||
/// Collects the information in the [`FbVarScreenInfo`].
|
||||
fn collect_var_screen_info(&self) -> FbVarScreenInfo {
|
||||
/// Default pixel clock calculation for efifb compatibility
|
||||
const DEFAULT_PIXEL_CLOCK_DIVISOR: u32 = 10_000_000;
|
||||
|
||||
|
|
@ -265,7 +279,7 @@ impl FbHandle {
|
|||
let pixel_format = self.framebuffer.pixel_format();
|
||||
let (red, green, blue, transp) = FbBitfield::from_pixel_format(pixel_format);
|
||||
|
||||
let screen_info = FbVarScreenInfo {
|
||||
FbVarScreenInfo {
|
||||
xres: self.framebuffer.width() as u32,
|
||||
yres: self.framebuffer.height() as u32,
|
||||
xres_virtual: self.framebuffer.width() as u32,
|
||||
|
|
@ -284,39 +298,27 @@ impl FbHandle {
|
|||
vsync_len: DEFAULT_VSYNC_LEN,
|
||||
hsync_len: (self.framebuffer.width() as u32 / 8) & 0xf8,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
current_userspace!().write_val(arg, &screen_info)?;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the [`IoctlCmd::GETFSCREENINFO`] ioctl command.
|
||||
///
|
||||
/// Arguments:
|
||||
/// - Input: None.
|
||||
/// - Output: [`FbFixScreenInfo`].
|
||||
fn handle_get_fix_screen_info(&self, arg: usize) -> Result<i32> {
|
||||
let screen_info = FbFixScreenInfo {
|
||||
/// Collects the information in the [`FbFixScreenInfo`].
|
||||
fn collect_fix_screen_info(&self) -> FbFixScreenInfo {
|
||||
FbFixScreenInfo {
|
||||
smem_start: self.framebuffer.io_mem().paddr() as u64,
|
||||
smem_len: self.framebuffer.io_mem().size() as u32,
|
||||
line_length: self.framebuffer.line_size() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
current_userspace!().write_val(arg, &screen_info)?;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the [`IoctlCmd::GETCMAP`] ioctl command.
|
||||
/// Handles the [`ioctl_defs::GetColorMap`] ioctl command.
|
||||
///
|
||||
/// Arguments:
|
||||
/// - Input: [`FbCmapUser`] (specifying the range).
|
||||
/// - Output: [`FbCmapUser`] (filled with color palette data).
|
||||
fn handle_get_cmap(&self, arg: usize) -> Result<i32> {
|
||||
let cmap_user: FbCmapUser = current_userspace!().read_val(arg)?;
|
||||
|
||||
fn handle_get_cmap(&self, cmap_user: &FbCmapUser) -> Result<()> {
|
||||
if cmap_user.len == 0 {
|
||||
return Ok(0);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let start = cmap_user.start as usize;
|
||||
|
|
@ -340,19 +342,17 @@ impl FbHandle {
|
|||
Self::write_color_maps_to_user(cmap_user.transp, &transp)?;
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles the [`IoctlCmd::PUTCMAP`] ioctl command.
|
||||
/// Handles the [`ioctl_defs::PutColorMap`] ioctl command.
|
||||
///
|
||||
/// Arguments:
|
||||
/// - Input: [`FbCmapUser`] (with color palette data).
|
||||
/// - Output: None.
|
||||
fn handle_set_cmap(&self, arg: usize) -> Result<i32> {
|
||||
let cmap_user: FbCmapUser = current_userspace!().read_val(arg)?;
|
||||
|
||||
fn handle_set_cmap(&self, cmap_user: &FbCmapUser) -> Result<()> {
|
||||
if cmap_user.len == 0 {
|
||||
return Ok(0);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let start = cmap_user.start as usize;
|
||||
|
|
@ -392,7 +392,7 @@ impl FbHandle {
|
|||
// Set color map entries in framebuffer
|
||||
self.framebuffer.set_color_map(start, &entries)?;
|
||||
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -475,19 +475,34 @@ impl FileIo for FbHandle {
|
|||
Ok(Mappable::IoMem(iomem.clone()))
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::GETVSCREENINFO => self.handle_get_var_screen_info(arg),
|
||||
IoctlCmd::GETFSCREENINFO => self.handle_get_fix_screen_info(arg),
|
||||
IoctlCmd::PUTVSCREENINFO => {
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
cmd @ GetVarScreenInfo => {
|
||||
cmd.write(&self.collect_var_screen_info())?;
|
||||
Ok(0)
|
||||
}
|
||||
cmd @ PutVarScreenInfo => {
|
||||
// EFI framebuffers do not support changing settings. Linux
|
||||
// will return the old settings to user space and succeed.
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/drivers/video/fbdev/core/fbmem.c#L276-L279>.
|
||||
self.handle_get_var_screen_info(arg)
|
||||
cmd.write(&self.collect_var_screen_info())?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::GETCMAP => self.handle_get_cmap(arg),
|
||||
IoctlCmd::PUTCMAP => self.handle_set_cmap(arg),
|
||||
IoctlCmd::PANDISPLAY | IoctlCmd::FBIOBLANK => {
|
||||
cmd @ GetFixScreenInfo => {
|
||||
cmd.write(&self.collect_fix_screen_info())?;
|
||||
Ok(0)
|
||||
}
|
||||
cmd @ GetColorMap => {
|
||||
self.handle_get_cmap(&cmd.read()?)?;
|
||||
Ok(0)
|
||||
}
|
||||
cmd @ PutColorMap => {
|
||||
self.handle_set_cmap(&cmd.read()?)?;
|
||||
Ok(0)
|
||||
}
|
||||
PanDisplay | Blank => {
|
||||
// These commands are not supported by efifb.
|
||||
// We return errors according to the Linux behavior.
|
||||
return_errno_with_message!(
|
||||
|
|
@ -497,15 +512,12 @@ impl FileIo for FbHandle {
|
|||
}
|
||||
_ => {
|
||||
log::debug!(
|
||||
"the ioctl command {:?} is not supported by framebuffer devices",
|
||||
cmd
|
||||
"the ioctl command {:#x} is unknown for framebuffer devices",
|
||||
raw_ioctl.cmd()
|
||||
);
|
||||
return_errno_with_message!(
|
||||
Errno::ENOTTY,
|
||||
"the ioctl command is not supported by framebuffer devices"
|
||||
)
|
||||
return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::sync::{Arc, Weak};
|
||||
use alloc::sync::Arc;
|
||||
use core::{
|
||||
mem::{offset_of, size_of},
|
||||
time::Duration,
|
||||
|
|
@ -10,10 +10,7 @@ use aster_util::{field_ptr, safe_ptr::SafePtr};
|
|||
use device_id::{DeviceId, MinorId};
|
||||
use ostd::{
|
||||
const_assert,
|
||||
mm::{
|
||||
io_util::HasVmReaderWriter, DmaCoherent, FrameAllocOptions, HasPaddr, HasSize, USegment,
|
||||
VmIo, PAGE_SIZE,
|
||||
},
|
||||
mm::{DmaCoherent, FrameAllocOptions, HasPaddr, HasSize, USegment, VmIo, PAGE_SIZE},
|
||||
sync::WaitQueue,
|
||||
};
|
||||
use tdx_guest::{
|
||||
|
|
@ -27,10 +24,11 @@ use crate::{
|
|||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
const TDX_GUEST_MINOR: u32 = 0x7b;
|
||||
|
|
@ -151,11 +149,26 @@ impl FileIo for TdxGuestFile {
|
|||
false
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TDXGETREPORT => handle_get_report(arg),
|
||||
_ => return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"),
|
||||
}
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
cmd @ GetTdxReport => {
|
||||
cmd.with_data_ptr(|data_ptr| {
|
||||
let inblob = {
|
||||
let inblob_ptr = field_ptr!(&data_ptr, TdxReportRequest, report_data);
|
||||
inblob_ptr.read()?
|
||||
};
|
||||
|
||||
let outblob = tdx_get_report(inblob.as_bytes())?;
|
||||
let outblob_ptr = field_ptr!(&data_ptr, TdxReportRequest, tdx_report);
|
||||
outblob_ptr.copy_from(&outblob)?;
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
_ => return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,17 +177,16 @@ pub fn tdx_get_quote(inblob: &[u8]) -> Result<Box<[u8]>> {
|
|||
const GET_QUOTE_BUF_SIZE: usize = 8 * 1024;
|
||||
|
||||
let report = tdx_get_report(inblob)?;
|
||||
|
||||
let buf = alloc_dma_buf(GET_QUOTE_BUF_SIZE)?;
|
||||
let report_ptr: SafePtr<TdxQuoteHdr, _, _> = SafePtr::new(&buf, 0);
|
||||
let payload_ptr: SafePtr<TdReport, _, _> = SafePtr::new(&buf, size_of::<TdxQuoteHdr>());
|
||||
|
||||
field_ptr!(&report_ptr, TdxQuoteHdr, version).write(&1u64)?;
|
||||
field_ptr!(&report_ptr, TdxQuoteHdr, status).write(&0u64)?;
|
||||
field_ptr!(&report_ptr, TdxQuoteHdr, in_len).write(&(size_of::<TdReport>() as u32))?;
|
||||
field_ptr!(&report_ptr, TdxQuoteHdr, out_len).write(&0u32)?;
|
||||
buf.write(
|
||||
size_of::<TdxQuoteHdr>(),
|
||||
report.reader().to_fallible().limit(size_of::<TdReport>()),
|
||||
)?;
|
||||
payload_ptr.copy_from(&report)?;
|
||||
|
||||
// FIXME: The `get_quote` API from the `tdx_guest` crate should have been marked `unsafe`
|
||||
// because it has no way to determine if the input physical address is safe or not.
|
||||
|
|
@ -198,7 +210,7 @@ pub fn tdx_get_quote(inblob: &[u8]) -> Result<Box<[u8]>> {
|
|||
// "Accept a pending private page and initialize it to all-0 using the TD ephemeral private key."
|
||||
let out_len = field_ptr!(&report_ptr, TdxQuoteHdr, out_len).read()?;
|
||||
let mut outblob = vec![0u8; out_len as usize].into_boxed_slice();
|
||||
buf.read_bytes(size_of::<TdxQuoteHdr>(), outblob.as_mut())?;
|
||||
payload_ptr.cast().read_slice(outblob.as_mut())?;
|
||||
Ok(outblob)
|
||||
}
|
||||
|
||||
|
|
@ -221,10 +233,10 @@ struct TdxReportRequest {
|
|||
tdx_report: TdReport,
|
||||
}
|
||||
|
||||
impl TdxReportRequest {
|
||||
fn report_inblob(&self) -> &[u8] {
|
||||
self.report_data.as_bytes()
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
struct ReportData {
|
||||
data: [u8; 64],
|
||||
}
|
||||
|
||||
/// TDX Report structure (`TDREPORT_STRUCT`) as defined in the Intel TDX Module Specification.
|
||||
|
|
@ -262,38 +274,24 @@ struct TdInfo {
|
|||
extension: [u8; 64],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
struct ReportData {
|
||||
data: [u8; 64],
|
||||
}
|
||||
|
||||
impl TdReport {
|
||||
const fn report_data_offset() -> usize {
|
||||
offset_of!(TdReport, report_mac) + offset_of!(ReportMac, report_data)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_get_report(arg: usize) -> Result<i32> {
|
||||
let current_task = ostd::task::Task::current().unwrap();
|
||||
let user_space = CurrentUserSpace::new(current_task.as_thread_local().unwrap());
|
||||
let user_request: TdxReportRequest = user_space.read_val(arg)?;
|
||||
mod ioctl_defs {
|
||||
use super::TdxReportRequest;
|
||||
use crate::util::ioctl::{ioc, InOutData};
|
||||
|
||||
let report = tdx_get_report(&user_request.report_inblob())?;
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/linux/tdx-guest.h#L40>
|
||||
|
||||
let tdx_report_vaddr = arg + offset_of!(TdxReportRequest, tdx_report);
|
||||
user_space.write_bytes(
|
||||
tdx_report_vaddr,
|
||||
report.reader().limit(size_of::<TdReport>()),
|
||||
)?;
|
||||
Ok(0)
|
||||
pub(super) type GetTdxReport =
|
||||
ioc!(TDX_CMD_GET_REPORT0, b'T', 0x01, InOutData<TdxReportRequest>);
|
||||
}
|
||||
|
||||
/// Gets the TDX report given the specified data in `inblob`.
|
||||
///
|
||||
/// The first `size_of::<TdReport>()` bytes of data in the returned `USegment` is the report.
|
||||
/// The rest in `USegment` should be ignored.
|
||||
fn tdx_get_report(inblob: &[u8]) -> Result<USegment> {
|
||||
fn tdx_get_report(inblob: &[u8]) -> Result<SafePtr<TdReport, USegment>> {
|
||||
if inblob.len() != size_of::<ReportData>() {
|
||||
return_errno_with_message!(Errno::EINVAL, "Invalid inblob length");
|
||||
}
|
||||
|
|
@ -321,7 +319,7 @@ fn tdx_get_report(inblob: &[u8]) -> Result<USegment> {
|
|||
// FIXME: The `get_report` API from the `tdx_guest` crate should have been marked `unsafe`
|
||||
// because it has no way to determine if the input physical address is safe or not.
|
||||
get_report(report.paddr() as u64, report_data_paddr as u64)?;
|
||||
Ok(report)
|
||||
Ok(SafePtr::new(report, 0))
|
||||
}
|
||||
|
||||
fn alloc_dma_buf(buf_len: usize) -> Result<DmaCoherent> {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ use crate::{
|
|||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
/// The file for a pseudoterminal slave.
|
||||
|
|
@ -81,7 +82,7 @@ impl InodeIo for PtySlaveFile {
|
|||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl FileIo for PtySlaveFile {
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32>;
|
||||
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
use alloc::format;
|
||||
|
||||
use ostd::{mm::VmIo, task::Task};
|
||||
use ostd::task::Task;
|
||||
|
||||
use super::{driver::PtyDriver, PtySlave};
|
||||
use crate::{
|
||||
current_userspace,
|
||||
device::tty::TtyFlags,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
|
|
@ -14,7 +13,7 @@ use crate::{
|
|||
file_table::FdFlags,
|
||||
fs_resolver::FsPath,
|
||||
inode_handle::FileIo,
|
||||
utils::{mkmod, AccessMode, InodeIo, IoctlCmd, OpenArgs, StatusFlags},
|
||||
utils::{mkmod, AccessMode, InodeIo, OpenArgs, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
|
|
@ -22,6 +21,7 @@ use crate::{
|
|||
signal::{PollHandle, Pollable},
|
||||
Terminal,
|
||||
},
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
const IO_CAPACITY: usize = 4096;
|
||||
|
|
@ -135,6 +135,17 @@ impl InodeIo for PtyMaster {
|
|||
}
|
||||
}
|
||||
|
||||
mod ioctl_defs {
|
||||
use crate::util::ioctl::{ioc, InData, NoData, OutData};
|
||||
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
|
||||
|
||||
pub(super) type SetPtyLock = ioc!(TIOCSPTLCK, b'T', 0x31, InData<i32>);
|
||||
pub(super) type GetPtyLock = ioc!(TIOCGPTLCK, b'T', 0x39, OutData<i32>);
|
||||
|
||||
pub(super) type OpenPtySlave = ioc!(TIOCGPTPEER, b'T', 0x41, NoData);
|
||||
}
|
||||
|
||||
impl FileIo for PtyMaster {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
return_errno_with_message!(Errno::ESPIPE, "the inode is a pty");
|
||||
|
|
@ -144,33 +155,37 @@ impl FileIo for PtyMaster {
|
|||
false
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS
|
||||
| IoctlCmd::TCSETS
|
||||
| IoctlCmd::TCSETSW
|
||||
| IoctlCmd::TCSETSF
|
||||
| IoctlCmd::TIOCGWINSZ
|
||||
| IoctlCmd::TIOCSWINSZ
|
||||
| IoctlCmd::TIOCGPTN => return self.slave.ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
let val = current_userspace!().read_val::<i32>(arg)?;
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
use crate::{device::tty::ioctl_defs::*, fs::utils::ioctl_defs::GetNumBytesToRead};
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
GetTermios | SetTermios | SetTermiosDrain | SetTermiosFlush | GetWinSize
|
||||
| SetWinSize | GetPtyNumber => {
|
||||
return self.slave.ioctl(raw_ioctl);
|
||||
}
|
||||
|
||||
cmd @ SetPtyLock => {
|
||||
let should_lock = cmd.read()? != 0;
|
||||
|
||||
let flags = self.master_flags();
|
||||
if val == 0 {
|
||||
flags.clear_pty_locked();
|
||||
} else {
|
||||
if should_lock {
|
||||
flags.set_pty_locked();
|
||||
} else {
|
||||
flags.clear_pty_locked();
|
||||
}
|
||||
}
|
||||
IoctlCmd::TIOCGPTLCK => {
|
||||
let val = if self.master_flags().is_pty_locked() {
|
||||
cmd @ GetPtyLock => {
|
||||
let is_locked = if self.master_flags().is_pty_locked() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
current_userspace!().write_val(arg, &val)?;
|
||||
|
||||
cmd.write(&is_locked)?;
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
_cmd @ OpenPtySlave => {
|
||||
let current_task = Task::current().unwrap();
|
||||
let thread_local = current_task.as_thread_local().unwrap();
|
||||
|
||||
|
|
@ -203,12 +218,14 @@ impl FileIo for PtyMaster {
|
|||
};
|
||||
return Ok(fd);
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
cmd @ GetNumBytesToRead => {
|
||||
let len = self.slave.driver().buffer_len() as i32;
|
||||
current_userspace!().write_val(arg, &len)?;
|
||||
|
||||
cmd.write(&len)?;
|
||||
}
|
||||
_ => (self.slave.clone() as Arc<dyn Terminal>).job_ioctl(cmd, arg, true)?,
|
||||
}
|
||||
|
||||
_ => (self.slave.clone() as Arc<dyn Terminal>).job_ioctl(raw_ioctl, true)?,
|
||||
});
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{
|
||||
termio::{CTermios, CWinSize},
|
||||
CFontOp,
|
||||
};
|
||||
use crate::util::ioctl::{ioc, InData, OutData, PassByVal};
|
||||
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
|
||||
|
||||
pub type GetTermios = ioc!(TCGETS, 0x5401, OutData<CTermios>);
|
||||
pub type SetTermios = ioc!(TCSETS, 0x5402, InData<CTermios>);
|
||||
pub type SetTermiosDrain = ioc!(TCSETSW, 0x5403, InData<CTermios>);
|
||||
pub type SetTermiosFlush = ioc!(TCSETSF, 0x5404, InData<CTermios>);
|
||||
|
||||
pub type GetWinSize = ioc!(TIOCGWINSZ, 0x5413, OutData<CWinSize>);
|
||||
pub type SetWinSize = ioc!(TIOCSWINSZ, 0x5414, InData<CWinSize>);
|
||||
|
||||
// TODO: Consider moving this to the `pty` module.
|
||||
pub type GetPtyNumber = ioc!(TIOCGPTN, b'T', 0x30, OutData<u32>);
|
||||
|
||||
pub type SetGraphicsMode = ioc!(KDSETMODE, 0x4B3A, InData<i32, PassByVal>);
|
||||
pub type GetGraphicsMode = ioc!(KDGETMODE, 0x4B3B, OutData<i32>);
|
||||
|
||||
pub type GetKeyboardMode = ioc!(KDGKBMODE, 0x4B44, OutData<i32>);
|
||||
pub type SetKeyboardMode = ioc!(KDSKBMODE, 0x4B45, InData<i32, PassByVal>);
|
||||
|
||||
pub type SetOrGetFont = ioc!(KDFONTOP, 0x4B72, InData<CFontOp>);
|
||||
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{IoctlCmd, StatusFlags},
|
||||
utils::StatusFlags,
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
|
|
@ -23,11 +23,13 @@ use crate::{
|
|||
signal::{PollHandle, Pollable, Pollee},
|
||||
JobControl, Terminal,
|
||||
},
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
mod device;
|
||||
mod driver;
|
||||
mod flags;
|
||||
pub(super) mod ioctl_defs;
|
||||
mod line_discipline;
|
||||
mod n_tty;
|
||||
mod termio;
|
||||
|
|
@ -300,27 +302,31 @@ impl<D: TtyDriver> Tty<D> {
|
|||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
pub fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
use crate::fs::utils::ioctl_defs::GetNumBytesToRead;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
cmd @ GetTermios => {
|
||||
let termios = *self.ldisc.lock().termios();
|
||||
|
||||
current_userspace!().write_val(arg, &termios)?;
|
||||
cmd.write(&termios)?;
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
let termios = current_userspace!().read_val(arg)?;
|
||||
cmd @ SetTermios => {
|
||||
let termios = cmd.read()?;
|
||||
|
||||
self.ldisc.lock().set_termios(termios);
|
||||
}
|
||||
IoctlCmd::TCSETSW => {
|
||||
let termios = current_userspace!().read_val(arg)?;
|
||||
cmd @ SetTermiosDrain => {
|
||||
let termios = cmd.read()?;
|
||||
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
ldisc.set_termios(termios);
|
||||
self.driver.drain_output();
|
||||
}
|
||||
IoctlCmd::TCSETSF => {
|
||||
let termios = current_userspace!().read_val(arg)?;
|
||||
cmd @ SetTermiosFlush => {
|
||||
let termios = cmd.read()?;
|
||||
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
ldisc.set_termios(termios);
|
||||
|
|
@ -329,66 +335,67 @@ impl<D: TtyDriver> Tty<D> {
|
|||
|
||||
self.pollee.invalidate();
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
cmd @ GetWinSize => {
|
||||
let winsize = self.ldisc.lock().window_size();
|
||||
|
||||
current_userspace!().write_val(arg, &winsize)?;
|
||||
cmd.write(&winsize)?;
|
||||
}
|
||||
IoctlCmd::TIOCSWINSZ => {
|
||||
let winsize = current_userspace!().read_val(arg)?;
|
||||
cmd @ SetWinSize => {
|
||||
let winsize = cmd.read()?;
|
||||
|
||||
self.ldisc.lock().set_window_size(winsize);
|
||||
}
|
||||
IoctlCmd::TIOCGPTN => {
|
||||
cmd @ GetPtyNumber => {
|
||||
let idx = self.index;
|
||||
|
||||
current_userspace!().write_val(arg, &idx)?;
|
||||
cmd.write(&idx)?;
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
cmd @ GetNumBytesToRead => {
|
||||
if self.tty_flags.is_other_closed() {
|
||||
return_errno_with_message!(Errno::EIO, "the TTY is closed");
|
||||
}
|
||||
|
||||
let buffer_len = self.ldisc.lock().buffer_len() as u32;
|
||||
let buffer_len = self.ldisc.lock().buffer_len() as i32;
|
||||
|
||||
current_userspace!().write_val(arg, &buffer_len)?;
|
||||
cmd.write(&buffer_len)?;
|
||||
}
|
||||
IoctlCmd::KDFONTOP => {
|
||||
let font_op = current_userspace!().read_val(arg)?;
|
||||
cmd @ SetOrGetFont => {
|
||||
let font_op = cmd.read()?;
|
||||
|
||||
self.handle_set_font(&font_op)?;
|
||||
}
|
||||
IoctlCmd::KDSETMODE => {
|
||||
cmd @ SetGraphicsMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = ConsoleMode::try_from(arg as i32)?;
|
||||
let mode = ConsoleMode::try_from(cmd.get())?;
|
||||
if !console.set_mode(mode) {
|
||||
return_errno_with_message!(Errno::EINVAL, "the console mode is not supported");
|
||||
}
|
||||
}
|
||||
IoctlCmd::KDGETMODE => {
|
||||
cmd @ GetGraphicsMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = console.mode().unwrap_or(ConsoleMode::Text);
|
||||
current_userspace!().write_val(arg, &(mode as i32))?;
|
||||
cmd.write(&(mode as i32))?;
|
||||
}
|
||||
IoctlCmd::KDSKBMODE => {
|
||||
cmd @ SetKeyboardMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = KeyboardMode::try_from(arg as i32)?;
|
||||
let mode = KeyboardMode::try_from(cmd.get())?;
|
||||
if !console.set_keyboard_mode(mode) {
|
||||
return_errno_with_message!(Errno::EINVAL, "the keyboard mode is not supported");
|
||||
}
|
||||
}
|
||||
IoctlCmd::KDGKBMODE => {
|
||||
cmd @ GetKeyboardMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = console.keyboard_mode().unwrap_or(KeyboardMode::Xlate);
|
||||
current_userspace!().write_val(arg, &(mode as i32))?;
|
||||
cmd.write(&(mode as i32))?;
|
||||
}
|
||||
|
||||
_ => (self.weak_self.upgrade().unwrap() as Arc<dyn Terminal>)
|
||||
.job_ioctl(cmd, arg, false)?,
|
||||
}
|
||||
.job_ioctl(raw_ioctl, false)?,
|
||||
});
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ use crate::{
|
|||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
/// The driver for VT (virtual terminal) devices.
|
||||
|
|
@ -131,7 +132,7 @@ impl<D: TtyDriver> InodeIo for TtyFile<D> {
|
|||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl<D: TtyDriver> FileIo for TtyFile<D> {
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32>;
|
||||
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ impl CCtrlCharId {
|
|||
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h#L30>.
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
pub(super) struct CTermios {
|
||||
pub struct CTermios {
|
||||
c_iflags: CInputFlags,
|
||||
c_oflags: COutputFlags,
|
||||
c_cflags: CCtrlFlags,
|
||||
|
|
@ -321,7 +321,7 @@ impl CTermios {
|
|||
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termios.h#L15>.
|
||||
#[derive(Debug, Clone, Copy, Default, Pod)]
|
||||
#[repr(C)]
|
||||
pub(super) struct CWinSize {
|
||||
pub struct CWinSize {
|
||||
ws_row: u16,
|
||||
ws_col: u16,
|
||||
ws_xpixel: u16,
|
||||
|
|
@ -333,7 +333,7 @@ pub(super) struct CWinSize {
|
|||
/// Reference: <https://elixir.bootlin.com/linux/v6.15/source/include/uapi/linux/kd.h#L159>.
|
||||
#[derive(Debug, Clone, Copy, Default, Pod)]
|
||||
#[repr(C)]
|
||||
pub(super) struct CFontOp {
|
||||
pub struct CFontOp {
|
||||
pub(super) op: u32,
|
||||
pub(super) flags: u32,
|
||||
pub(super) width: u32,
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ use crate::{
|
|||
file_table::{get_file_fast, FdFlags, FileDesc},
|
||||
path::RESERVED_MOUNT_ID,
|
||||
pseudofs::anon_inodefs_shared_inode,
|
||||
utils::{CreationFlags, Inode, IoctlCmd},
|
||||
utils::{CreationFlags, Inode},
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
posix_thread::ThreadLocal,
|
||||
signal::{PollHandle, Pollable},
|
||||
},
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
/// A file-like object that provides epoll API.
|
||||
|
|
@ -264,8 +265,8 @@ impl FileLike for EpollFile {
|
|||
return_errno_with_message!(Errno::EINVAL, "epoll files do not support write");
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
return_errno_with_message!(Errno::EINVAL, "epoll files do not support ioctl");
|
||||
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
return_errno_with_message!(Errno::ENOTTY, "epoll files do not support ioctl");
|
||||
}
|
||||
|
||||
fn inode(&self) -> &Arc<dyn Inode> {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ use super::{inode_handle::InodeHandle, path::Path};
|
|||
use crate::{
|
||||
fs::{
|
||||
file_table::FdFlags,
|
||||
utils::{AccessMode, FallocMode, Inode, IoctlCmd, SeekFrom, StatusFlags},
|
||||
utils::{AccessMode, FallocMode, Inode, SeekFrom, StatusFlags},
|
||||
},
|
||||
net::socket::Socket,
|
||||
prelude::*,
|
||||
process::signal::Pollable,
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
/// The basic operations defined on a file
|
||||
|
|
@ -50,7 +51,7 @@ pub trait FileLike: Pollable + Send + Sync + Any {
|
|||
return_errno_with_message!(Errno::ESPIPE, "write_at is not supported");
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
// `ENOTTY` means that "The specified operation does not apply to the kind of object that
|
||||
// the file descriptor references".
|
||||
// Reference: <https://man7.org/linux/man-pages/man2/ioctl.2.html>.
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ use crate::{
|
|||
path::Path,
|
||||
utils::{
|
||||
AccessMode, CreationFlags, DirentVisitor, FallocMode, FlockItem, Inode, InodeType,
|
||||
IoctlCmd, RangeLockItem, RangeLockType, SeekFrom, StatusFlags,
|
||||
RangeLockItem, RangeLockType, SeekFrom, StatusFlags,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
pub struct InodeHandle(HandleInner, Rights);
|
||||
|
|
@ -157,11 +158,11 @@ impl FileLike for InodeHandle {
|
|||
self.0.write_at(offset, reader)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
if self.1.is_empty() {
|
||||
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
||||
}
|
||||
self.0.ioctl(cmd, arg)
|
||||
self.0.ioctl(raw_ioctl)
|
||||
}
|
||||
|
||||
fn mappable(&self) -> Result<Mappable> {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@ use crate::{
|
|||
file_handle::Mappable,
|
||||
path::Path,
|
||||
utils::{
|
||||
DirentVisitor, FallocMode, FileRange, FlockItem, FlockList, Inode, InodeType, IoctlCmd,
|
||||
DirentVisitor, FallocMode, FileRange, FlockItem, FlockList, Inode, InodeType,
|
||||
RangeLockItem, RangeLockList, RangeLockType, SeekFrom, StatusFlags, OFFSET_MAX,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
struct HandleInner {
|
||||
|
|
@ -186,9 +187,9 @@ impl HandleInner {
|
|||
)
|
||||
}
|
||||
|
||||
pub(self) fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
pub(self) fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
if let Some(ref file_io) = self.file_io {
|
||||
return file_io.ioctl(cmd, arg);
|
||||
return file_io.ioctl(raw_ioctl);
|
||||
}
|
||||
|
||||
return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported");
|
||||
|
|
@ -318,7 +319,7 @@ pub trait FileIo: Pollable + InodeIo + Send + Sync + 'static {
|
|||
return_errno_with_message!(Errno::EINVAL, "the file is not mappable");
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,11 @@ use core::{
|
|||
use bitflags::bitflags;
|
||||
use hashbrown::HashMap;
|
||||
use ostd::{
|
||||
mm::{VmIo, VmWriter},
|
||||
mm::VmWriter,
|
||||
sync::{Mutex, SpinLock},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
current_userspace,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
file_handle::FileLike,
|
||||
|
|
@ -27,11 +26,12 @@ use crate::{
|
|||
notify::{FsEventSubscriber, FsEvents},
|
||||
path::{Path, RESERVED_MOUNT_ID},
|
||||
pseudofs::anon_inodefs_shared_inode,
|
||||
utils::{AccessMode, CreationFlags, Inode, IoctlCmd, StatusFlags},
|
||||
utils::{AccessMode, CreationFlags, Inode, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
return_errno_with_message,
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -347,16 +347,18 @@ impl FileLike for InotifyFile {
|
|||
}
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::FIONREAD => {
|
||||
let size = self.get_all_event_size();
|
||||
let size_addr = arg;
|
||||
current_userspace!().write_val(size_addr, &size)?;
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
use crate::fs::utils::ioctl_defs::GetNumBytesToRead;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
cmd @ GetNumBytesToRead => {
|
||||
let size = self.get_all_event_size() as i32;
|
||||
|
||||
cmd.write(&size)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "ioctl is not supported"),
|
||||
}
|
||||
_ => return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown"),
|
||||
})
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ use crate::{
|
|||
tmpfs::TmpFs,
|
||||
utils::{
|
||||
chmod, mkmod, AccessMode, CachePage, CreationFlags, Extension, FallocMode, FileSystem,
|
||||
Inode, InodeIo, InodeMode, InodeType, IoctlCmd, Metadata, OpenArgs, PageCacheBackend,
|
||||
SeekFrom, StatusFlags, XattrName, XattrNamespace, XattrSetFlags,
|
||||
Inode, InodeIo, InodeMode, InodeType, Metadata, OpenArgs, PageCacheBackend, SeekFrom,
|
||||
StatusFlags, XattrName, XattrNamespace, XattrSetFlags,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
|
|
@ -36,6 +36,7 @@ use crate::{
|
|||
signal::{PollHandle, Pollable},
|
||||
Gid, Uid,
|
||||
},
|
||||
util::ioctl::RawIoctl,
|
||||
vm::{perms::VmPerms, vmo::Vmo},
|
||||
};
|
||||
|
||||
|
|
@ -462,7 +463,7 @@ impl FileLike for MemfdFile {
|
|||
Ok(Mappable::Inode(self.memfd_inode.clone()))
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
fn ioctl(&self, _raw_ioctl: RawIoctl) -> Result<i32> {
|
||||
if self.rights.is_empty() {
|
||||
return_errno_with_message!(Errno::EBADF, "the file is opened as a path");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::util::ioctl::{ioc, OutData};
|
||||
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
|
||||
|
||||
pub type GetNumBytesToRead = ioc!(FIONREAD, 0x541B, OutData<i32>);
|
||||
|
|
@ -39,6 +39,7 @@ mod fs;
|
|||
mod id_bitmap;
|
||||
mod inode;
|
||||
mod inode_mode;
|
||||
pub mod ioctl_defs;
|
||||
mod open_args;
|
||||
mod page_cache;
|
||||
mod random_test;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use ostd::mm::VmIo;
|
||||
|
||||
use super::{session::SessionGuard, JobControl, Pgid, Process, Session, Sid};
|
||||
use super::{session::SessionGuard, JobControl, Pgid, Process, Session};
|
||||
use crate::{
|
||||
current_userspace,
|
||||
fs::{device::Device, utils::IoctlCmd},
|
||||
fs::device::Device,
|
||||
prelude::{current, return_errno_with_message, warn, Errno, Error, Result},
|
||||
process::process_table,
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
/// A terminal.
|
||||
|
|
@ -21,19 +19,29 @@ pub trait Terminal: Device {
|
|||
fn job_control(&self) -> &JobControl;
|
||||
}
|
||||
|
||||
impl dyn Terminal {
|
||||
pub fn job_ioctl(self: Arc<Self>, cmd: IoctlCmd, arg: usize, via_master: bool) -> Result<()> {
|
||||
match cmd {
|
||||
// Commands about foreground process groups
|
||||
IoctlCmd::TIOCSPGRP => {
|
||||
let pgid = current_userspace!().read_val::<Pgid>(arg)?;
|
||||
if pgid.cast_signed() < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "negative PGIDs are not valid");
|
||||
}
|
||||
mod ioctl_defs {
|
||||
use crate::{
|
||||
process::{Pgid, Sid},
|
||||
util::ioctl::{ioc, InData, NoData, OutData, PassByVal},
|
||||
};
|
||||
|
||||
self.set_foreground(pgid, ¤t!())
|
||||
}
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
|
||||
|
||||
pub(super) type GetForegroundPgid = ioc!(TIOCGPGRP, 0x540F, OutData<Pgid>);
|
||||
pub(super) type SetForegroundPgid = ioc!(TIOCSPGRP, 0x5410, InData<Pgid>);
|
||||
|
||||
pub(super) type SetControlTty = ioc!(TIOCSCTTY, 0x540E, InData<i32, PassByVal>);
|
||||
pub(super) type SetControlNoTty = ioc!(TIOCNOTTY, 0x5422, NoData);
|
||||
pub(super) type GetControlSid = ioc!(TIOCGSID, 0x5429, OutData<Sid>);
|
||||
}
|
||||
|
||||
impl dyn Terminal {
|
||||
pub fn job_ioctl(self: Arc<Self>, raw_ioctl: RawIoctl, via_master: bool) -> Result<()> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
// Commands about foreground process groups
|
||||
cmd @ GetForegroundPgid => {
|
||||
let operate = || {
|
||||
self.job_control()
|
||||
.foreground()
|
||||
|
|
@ -46,18 +54,26 @@ impl dyn Terminal {
|
|||
self.is_control_and(¤t!(), |_, _| Ok(operate()))?
|
||||
};
|
||||
|
||||
Ok(current_userspace!().write_val::<Pgid>(arg, &pgid)?)
|
||||
cmd.write(&pgid)
|
||||
}
|
||||
cmd @ SetForegroundPgid => {
|
||||
let pgid = cmd.read()?;
|
||||
if pgid.cast_signed() < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "negative PGIDs are not valid");
|
||||
}
|
||||
|
||||
self.set_foreground(pgid, ¤t!())
|
||||
}
|
||||
|
||||
// Commands about sessions
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
if arg == 1 {
|
||||
cmd @ SetControlTty => {
|
||||
if cmd.get() == 1 {
|
||||
warn!("stealing TTY from another session is not supported");
|
||||
}
|
||||
|
||||
self.set_control(¤t!())
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
_cmd @ SetControlNoTty => {
|
||||
if via_master {
|
||||
return_errno_with_message!(
|
||||
Errno::ENOTTY,
|
||||
|
|
@ -67,7 +83,7 @@ impl dyn Terminal {
|
|||
|
||||
self.unset_control(¤t!())
|
||||
}
|
||||
IoctlCmd::TIOCGSID => {
|
||||
cmd @ GetControlSid => {
|
||||
let sid = if via_master {
|
||||
self.job_control()
|
||||
.session()
|
||||
|
|
@ -82,14 +98,14 @@ impl dyn Terminal {
|
|||
self.is_control_and(¤t!(), |session, _| Ok(session.sid()))?
|
||||
};
|
||||
|
||||
Ok(current_userspace!().write_val::<Sid>(arg, &sid)?)
|
||||
cmd.write(&sid)
|
||||
}
|
||||
|
||||
// Commands that are invalid or not supported
|
||||
_ => {
|
||||
return_errno_with_message!(Errno::EINVAL, "the `ioctl` command is invalid")
|
||||
return_errno_with_message!(Errno::ENOTTY, "the ioctl command is unknown")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the terminal to be the controlling terminal of the process.
|
||||
|
|
|
|||
|
|
@ -1,73 +1,121 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use ostd::mm::VmIo;
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{
|
||||
fs::{
|
||||
file_handle::FileLike,
|
||||
file_table::{get_file_fast, FdFlags, FileDesc, WithFileTable},
|
||||
utils::{IoctlCmd, StatusFlags},
|
||||
utils::StatusFlags,
|
||||
},
|
||||
prelude::*,
|
||||
process::posix_thread::FileTableRefMut,
|
||||
util::ioctl::{dispatch_ioctl, RawIoctl},
|
||||
};
|
||||
|
||||
pub fn sys_ioctl(fd: FileDesc, cmd: u32, arg: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
|
||||
let ioctl_cmd = IoctlCmd::try_from(cmd)?;
|
||||
debug!(
|
||||
"fd = {}, ioctl_cmd = {:?}, arg = 0x{:x}",
|
||||
fd, ioctl_cmd, arg
|
||||
);
|
||||
let raw_ioctl = RawIoctl::new(cmd, arg);
|
||||
debug!("fd = {}, raw_ioctl = {:#x?}", fd, raw_ioctl,);
|
||||
|
||||
let mut file_table = ctx.thread_local.borrow_file_table_mut();
|
||||
|
||||
// First, handle the ioctl command that affects the file descriptor.
|
||||
if let Some(res) = handle_fd_ioctl(&mut file_table, fd, raw_ioctl) {
|
||||
res?;
|
||||
return Ok(SyscallReturn::Return(0));
|
||||
}
|
||||
|
||||
let file = get_file_fast!(&mut file_table, fd);
|
||||
let res = match ioctl_cmd {
|
||||
IoctlCmd::FIONBIO => {
|
||||
let is_nonblocking = ctx.user_space().read_val::<i32>(arg)? != 0;
|
||||
let mut flags = file.status_flags();
|
||||
flags.set(StatusFlags::O_NONBLOCK, is_nonblocking);
|
||||
file.set_status_flags(flags)?;
|
||||
0
|
||||
}
|
||||
IoctlCmd::FIOASYNC => {
|
||||
let is_async = ctx.user_space().read_val::<i32>(arg)? != 0;
|
||||
let mut flags = file.status_flags();
|
||||
|
||||
// Set `O_ASYNC` flags will send `SIGIO` signal to a process when
|
||||
// I/O is possible, user should call `fcntl(fd, F_SETOWN, pid)`
|
||||
// first to let the kernel know just whom to notify.
|
||||
flags.set(StatusFlags::O_ASYNC, is_async);
|
||||
file.set_status_flags(flags)?;
|
||||
0
|
||||
}
|
||||
IoctlCmd::FIOCLEX => {
|
||||
// Sets the close-on-exec flag of the file.
|
||||
// Follow the implementation of fcntl()
|
||||
|
||||
file_table.read_with(|inner| {
|
||||
let entry = inner.get_entry(fd)?;
|
||||
entry.set_flags(entry.flags() | FdFlags::CLOEXEC);
|
||||
Ok::<_, Error>(0)
|
||||
})?
|
||||
}
|
||||
IoctlCmd::FIONCLEX => {
|
||||
// Clears the close-on-exec flag of the file.
|
||||
// Follow the implementation of fcntl()
|
||||
|
||||
file_table.read_with(|inner| {
|
||||
let entry = inner.get_entry(fd)?;
|
||||
entry.set_flags(entry.flags() - FdFlags::CLOEXEC);
|
||||
Ok::<_, Error>(0)
|
||||
})?
|
||||
}
|
||||
// FIXME: ioctl operations involving blocking I/O should be able to restart if interrupted
|
||||
_ => {
|
||||
let file_owned = file.into_owned();
|
||||
// We have to drop `file_table` because some I/O command will modify the file table
|
||||
// (e.g., TIOCGPTPEER).
|
||||
drop(file_table);
|
||||
|
||||
file_owned.ioctl(ioctl_cmd, arg)?
|
||||
}
|
||||
// Then, handle the ioctl command the affects the file description.
|
||||
let res = if let Some(res) = handle_file_ioctl(&**file, raw_ioctl) {
|
||||
res?;
|
||||
0
|
||||
} else {
|
||||
let file_owned = file.into_owned();
|
||||
// We have to drop `file_table` because some I/O command will modify the file table
|
||||
// (e.g., TIOCGPTPEER).
|
||||
drop(file_table);
|
||||
file_owned.ioctl(raw_ioctl)?
|
||||
};
|
||||
|
||||
Ok(SyscallReturn::Return(res as _))
|
||||
}
|
||||
|
||||
mod ioctl_defs {
|
||||
use crate::util::ioctl::{ioc, InData, NoData};
|
||||
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
|
||||
|
||||
pub(super) type SetNonBlocking = ioc!(FIONBIO, 0x5421, InData<i32>);
|
||||
pub(super) type SetAsync = ioc!(FIOASYNC, 0x5452, InData<i32>);
|
||||
|
||||
pub(super) type SetNotCloseOnExec = ioc!(FIONCLEX, 0x5450, NoData);
|
||||
pub(super) type SetCloseOnExec = ioc!(FIOCLEX, 0x5451, NoData);
|
||||
}
|
||||
|
||||
fn handle_fd_ioctl(
|
||||
file_table: &mut FileTableRefMut,
|
||||
fd: FileDesc,
|
||||
raw_ioctl: RawIoctl,
|
||||
) -> Option<Result<()>> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
SetNotCloseOnExec => {
|
||||
// Clears the close-on-exec flag of the file.
|
||||
// Follow the implementation of `fcntl()`.
|
||||
|
||||
Some(file_table.read_with(|inner| {
|
||||
let entry = inner.get_entry(fd)?;
|
||||
// FIXME: This is racy.
|
||||
entry.set_flags(entry.flags() - FdFlags::CLOEXEC);
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
SetCloseOnExec => {
|
||||
// Sets the close-on-exec flag of the file.
|
||||
// Follow the implementation of `fcntl()`.
|
||||
|
||||
Some(file_table.read_with(|inner| {
|
||||
let entry = inner.get_entry(fd)?;
|
||||
// FIXME: This is racy.
|
||||
entry.set_flags(entry.flags() | FdFlags::CLOEXEC);
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_file_ioctl(file: &dyn FileLike, raw_ioctl: RawIoctl) -> Option<Result<()>> {
|
||||
use ioctl_defs::*;
|
||||
|
||||
dispatch_ioctl!(match raw_ioctl {
|
||||
cmd @ SetNonBlocking => {
|
||||
let handler = || {
|
||||
let is_nonblocking = cmd.read()? != 0;
|
||||
|
||||
let mut flags = file.status_flags();
|
||||
flags.set(StatusFlags::O_NONBLOCK, is_nonblocking);
|
||||
// FIXME: This is racy.
|
||||
file.set_status_flags(flags)
|
||||
};
|
||||
Some(handler())
|
||||
}
|
||||
cmd @ SetAsync => {
|
||||
let handler = || {
|
||||
let is_async = cmd.read()? != 0;
|
||||
|
||||
// Setting the `O_ASYNC` flag will cause the kernel to send the owner process a
|
||||
// `SIGIO` signal when input/output is possible. The user should first call
|
||||
// `fcntl(fd, F_SETOWN, pid)` to specify the process to be notified.
|
||||
let mut flags = file.status_flags();
|
||||
flags.set(StatusFlags::O_ASYNC, is_async);
|
||||
// FIXME: This is racy.
|
||||
file.set_status_flags(flags)
|
||||
};
|
||||
Some(handler())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue