Refactor mem/misc char devices
This commit is contained in:
parent
6df2af2e17
commit
e407dc7ce9
|
|
@ -0,0 +1,243 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! A subsystem for character devices (or char devices for short).
|
||||
|
||||
#![expect(dead_code)]
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
use device_id::{DeviceId, MajorId};
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{add_node, Device, DeviceType},
|
||||
file_handle::Mappable,
|
||||
fs_resolver::FsResolver,
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
/// A character device.
|
||||
pub trait CharDevice: Send + Sync + Debug {
|
||||
/// Returns the name of this char device that should appear in devtmpfs (usually under `/dev`).
|
||||
fn devtmpfs_name(&self) -> DevtmpfsName<'_>;
|
||||
|
||||
/// Returns the device ID.
|
||||
fn id(&self) -> DeviceId;
|
||||
|
||||
/// Opens the char device, returning a file-like object that the userspace can interact with by doing I/O.
|
||||
///
|
||||
/// Multiple calls to this method return the same object (at least logically).
|
||||
fn open(&self) -> Result<Arc<dyn FileIo>>;
|
||||
}
|
||||
|
||||
static DEVICE_REGISTRY: Mutex<BTreeMap<u32, Arc<dyn CharDevice>>> = Mutex::new(BTreeMap::new());
|
||||
|
||||
/// Registers a new char device.
|
||||
pub fn register(device: Arc<dyn CharDevice>) -> Result<()> {
|
||||
let mut registry = DEVICE_REGISTRY.lock();
|
||||
let id = device.id().to_raw();
|
||||
if registry.contains_key(&id) {
|
||||
return_errno_with_message!(Errno::EEXIST, "char device already exists");
|
||||
}
|
||||
registry.insert(id, device);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unregisters an existing char device, returning the device if found.
|
||||
pub fn unregister(id: DeviceId) -> Result<Arc<dyn CharDevice>> {
|
||||
DEVICE_REGISTRY
|
||||
.lock()
|
||||
.remove(&id.to_raw())
|
||||
.ok_or(Error::with_message(
|
||||
Errno::ENOENT,
|
||||
"char device does not exist",
|
||||
))
|
||||
}
|
||||
|
||||
/// Collects all char devices.
|
||||
pub fn collect_all() -> Vec<Arc<dyn CharDevice>> {
|
||||
DEVICE_REGISTRY.lock().values().cloned().collect()
|
||||
}
|
||||
|
||||
/// Looks up a char device of a given device ID.
|
||||
pub fn lookup(id: DeviceId) -> Option<Arc<dyn CharDevice>> {
|
||||
DEVICE_REGISTRY.lock().get(&id.to_raw()).cloned()
|
||||
}
|
||||
|
||||
/// The maximum value of the major device ID of a char device.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/fs/char_dev.c#L104>.
|
||||
pub const MAX_MAJOR: u16 = 511;
|
||||
|
||||
/// The ranges of free char majors.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/include/linux/fs.h#L2840>.
|
||||
const DYNAMIC_MAJOR_ID_RANGES: [Range<u16>; 2] = [234..255, 384..512];
|
||||
|
||||
static MAJORS: Mutex<BTreeSet<u16>> = Mutex::new(BTreeSet::new());
|
||||
|
||||
/// Acquires a major ID.
|
||||
///
|
||||
/// The returned `MajorIdOwner` object represents the ownership to the major ID.
|
||||
/// Until the object is dropped, this major ID cannot be acquired via `acquire_major` or `allocate_major` again.
|
||||
pub fn acquire_major(major: MajorId) -> Result<MajorIdOwner> {
|
||||
if major.get() > MAX_MAJOR {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid major ID");
|
||||
}
|
||||
|
||||
if MAJORS.lock().insert(major.get()) {
|
||||
Ok(MajorIdOwner(major))
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EEXIST, "major ID already acquired")
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a major ID.
|
||||
///
|
||||
/// The returned `MajorIdOwner` object represents the ownership to the major ID.
|
||||
/// Until the object is dropped, this major ID cannot be acquired via `acquire_major` or `allocate_major` again.
|
||||
pub fn allocate_major() -> Result<MajorIdOwner> {
|
||||
let mut majors = MAJORS.lock();
|
||||
|
||||
for id in DYNAMIC_MAJOR_ID_RANGES
|
||||
.iter()
|
||||
.flat_map(|range| range.clone().rev())
|
||||
{
|
||||
if majors.insert(id) {
|
||||
return Ok(MajorIdOwner(MajorId::new(id)));
|
||||
}
|
||||
}
|
||||
|
||||
return_errno_with_message!(Errno::ENOSPC, "no more major IDs available");
|
||||
}
|
||||
|
||||
/// An owned major ID.
|
||||
///
|
||||
/// Each instances of this type will unregister the major ID when dropped.
|
||||
pub struct MajorIdOwner(MajorId);
|
||||
|
||||
impl MajorIdOwner {
|
||||
/// Returns the major ID.
|
||||
pub fn get(&self) -> MajorId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MajorIdOwner {
|
||||
fn drop(&mut self) {
|
||||
MAJORS.lock().remove(&self.0.get());
|
||||
}
|
||||
}
|
||||
|
||||
/// A device's name under devtmpfs.
|
||||
///
|
||||
/// A `DevtmpfsName` consists of two parts:
|
||||
/// 1. The device name;
|
||||
/// 2. The class name.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// If you want a device to appear as `/dev/zero`,
|
||||
/// then assign it a name of `DevtmpfsName::new("zero", None)`.
|
||||
///
|
||||
/// If you want to a device to appear as `/dev/input/event0`,
|
||||
/// then assign it a name of `DevtmpfsName::new("event0", Some("input"))`.
|
||||
pub struct DevtmpfsName<'a> {
|
||||
dev_name: &'a str,
|
||||
class_name: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> DevtmpfsName<'a> {
|
||||
pub fn new(dev_name: &'a str, class_name: Option<&'a str>) -> Self {
|
||||
Self {
|
||||
dev_name,
|
||||
class_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dev_name(&self) -> &'a str {
|
||||
self.dev_name
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> Option<&'a str> {
|
||||
self.class_name
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn init_in_first_process(fs_resolver: &FsResolver) -> Result<()> {
|
||||
for device in collect_all() {
|
||||
let name = device.devtmpfs_name().dev_name().to_string();
|
||||
let device = Arc::new(CharFile::new(device));
|
||||
add_node(device, &name, fs_resolver)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Represents a character device inode in the filesystem.
|
||||
///
|
||||
/// Only implements the `Device` trait.
|
||||
#[derive(Debug)]
|
||||
pub struct CharFile(Arc<dyn CharDevice>);
|
||||
|
||||
impl CharFile {
|
||||
pub fn new(device: Arc<dyn CharDevice>) -> Self {
|
||||
Self(device)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for CharFile {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.0.id()
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(OpenCharFile(self.0.open()?)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an opened character device file ready for I/O operations.
|
||||
///
|
||||
/// Does not implement the `Device` trait but provides full implementations
|
||||
/// for I/O related traits.
|
||||
pub struct OpenCharFile(Arc<dyn FileIo>);
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl InodeIo for OpenCharFile {
|
||||
fn read_at(
|
||||
&self,
|
||||
offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize>;
|
||||
fn write_at(
|
||||
&self,
|
||||
offset: usize,
|
||||
reader: &mut VmReader,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize>;
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl Pollable for OpenCharFile {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents;
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl FileIo for OpenCharFile {
|
||||
fn check_seekable(&self) -> Result<()>;
|
||||
fn is_offset_aware(&self) -> bool;
|
||||
fn mappable(&self) -> Result<Mappable>;
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
pub struct Full;
|
||||
|
||||
impl Device for Full {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(7))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for Full {
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for Full {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let len = writer.avail();
|
||||
writer.fill_zeros(len)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
_reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::ENOSPC, "no space left on /dev/full")
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Full {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::vec;
|
||||
|
||||
use ostd::mm::{FallibleVmWrite, VmReader, VmWriter};
|
||||
|
||||
use crate::{
|
||||
error::Errno,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::Result,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
return_errno_with_message,
|
||||
util::random,
|
||||
};
|
||||
|
||||
pub fn geturandom(writer: &mut VmWriter) -> Result<usize> {
|
||||
const IO_CAPABILITY: usize = 4096;
|
||||
|
||||
if !writer.has_avail() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut buffer = vec![0; writer.avail().min(IO_CAPABILITY)];
|
||||
let mut written_bytes = 0;
|
||||
|
||||
while writer.has_avail() {
|
||||
random::getrandom(&mut buffer[..writer.avail().min(IO_CAPABILITY)]);
|
||||
match writer.write_fallible(&mut VmReader::from(buffer.as_slice())) {
|
||||
Ok(len) => written_bytes += len,
|
||||
Err((err, 0)) if written_bytes == 0 => return Err(err.into()),
|
||||
Err((_, len)) => return Ok(written_bytes + len),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(written_bytes)
|
||||
}
|
||||
|
||||
// TODO: Support true randomness by collecting environment noise.
|
||||
pub use geturandom as getrandom;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[expect(dead_code)]
|
||||
pub(super) enum MemFile {
|
||||
Mem,
|
||||
Kmem,
|
||||
Null,
|
||||
Port,
|
||||
Zero,
|
||||
Core,
|
||||
Full,
|
||||
Random,
|
||||
Urandom,
|
||||
Aio,
|
||||
Kmsg,
|
||||
Oldmem,
|
||||
}
|
||||
|
||||
impl MemFile {
|
||||
pub(super) fn minor(&self) -> u32 {
|
||||
match self {
|
||||
MemFile::Mem => 1,
|
||||
MemFile::Kmem => 2,
|
||||
MemFile::Null => 3,
|
||||
MemFile::Port => 4,
|
||||
MemFile::Zero => 5,
|
||||
MemFile::Core => 6,
|
||||
MemFile::Full => 7,
|
||||
MemFile::Random => 8,
|
||||
MemFile::Urandom => 9,
|
||||
MemFile::Aio => 10,
|
||||
MemFile::Kmsg => 11,
|
||||
MemFile::Oldmem => 12,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn name(&self) -> &str {
|
||||
match self {
|
||||
MemFile::Mem => "mem",
|
||||
MemFile::Kmem => "kmem",
|
||||
MemFile::Null => "null",
|
||||
MemFile::Port => "port",
|
||||
MemFile::Zero => "zero",
|
||||
MemFile::Core => "core",
|
||||
MemFile::Full => "full",
|
||||
MemFile::Random => "random",
|
||||
MemFile::Urandom => "urandom",
|
||||
MemFile::Aio => "aio",
|
||||
MemFile::Kmsg => "kmsg",
|
||||
MemFile::Oldmem => "oldmem",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for MemFile {
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for MemFile {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
match self {
|
||||
MemFile::Full | MemFile::Zero => {
|
||||
let len = writer.avail();
|
||||
writer.fill_zeros(len)?;
|
||||
Ok(len)
|
||||
}
|
||||
MemFile::Null => Ok(0),
|
||||
MemFile::Random => getrandom(writer),
|
||||
MemFile::Urandom => geturandom(writer),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "read is not supported yet"),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
match self {
|
||||
MemFile::Null | MemFile::Random | MemFile::Urandom | MemFile::Zero => {
|
||||
let len = reader.remain();
|
||||
reader.skip(len);
|
||||
Ok(len)
|
||||
}
|
||||
MemFile::Full => {
|
||||
return_errno_with_message!(Errno::ENOSPC, "no space left on /dev/full")
|
||||
}
|
||||
_ => return_errno_with_message!(Errno::ENAVAIL, "write is not supported yet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for MemFile {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Memory devices.
|
||||
//!
|
||||
//! Character device with major number 1. The minor numbers are mapped as follows:
|
||||
//! - 1 = /dev/mem Physical memory access
|
||||
//! - 2 = /dev/kmem OBSOLETE - replaced by /proc/kcore
|
||||
//! - 3 = /dev/null Null device
|
||||
//! - 4 = /dev/port I/O port access
|
||||
//! - 5 = /dev/zero Null byte source
|
||||
//! - 6 = /dev/core OBSOLETE - replaced by /proc/kcore
|
||||
//! - 7 = /dev/full Returns ENOSPC on write
|
||||
//! - 8 = /dev/random Nondeterministic random number gen.
|
||||
//! - 9 = /dev/urandom Faster, less secure random number gen.
|
||||
//! - 10 = /dev/aio Asynchronous I/O notification interface
|
||||
//! - 11 = /dev/kmsg Writes to this come out as printk's, reads export the buffered printk records.
|
||||
//! - 12 = /dev/oldmem OBSOLETE - replaced by /proc/vmcore
|
||||
//!
|
||||
//! See <https://www.kernel.org/doc/Documentation/admin-guide/devices.txt>.
|
||||
|
||||
mod file;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use file::MemFile;
|
||||
pub use file::{getrandom, geturandom};
|
||||
use spin::Once;
|
||||
|
||||
use super::char::{acquire_major, register, CharDevice, MajorIdOwner};
|
||||
use crate::{device::char::DevtmpfsName, fs::inode_handle::FileIo, prelude::*};
|
||||
|
||||
/// A memory device.
|
||||
#[derive(Debug)]
|
||||
pub struct MemDevice {
|
||||
id: DeviceId,
|
||||
file: MemFile,
|
||||
}
|
||||
|
||||
impl MemDevice {
|
||||
fn new(file: MemFile) -> Self {
|
||||
let major = MEM_MAJOR.get().unwrap().get();
|
||||
let minor = MinorId::new(file.minor());
|
||||
|
||||
Self {
|
||||
id: DeviceId::new(major, minor),
|
||||
file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for MemDevice {
|
||||
fn devtmpfs_name(&self) -> DevtmpfsName {
|
||||
DevtmpfsName::new(self.file.name(), None)
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Arc<dyn FileIo>> {
|
||||
Ok(Arc::new(self.file))
|
||||
}
|
||||
}
|
||||
|
||||
static MEM_MAJOR: Once<MajorIdOwner> = Once::new();
|
||||
|
||||
pub(super) fn init_in_first_kthread() {
|
||||
MEM_MAJOR.call_once(|| acquire_major(MajorId::new(1)).unwrap());
|
||||
|
||||
register(Arc::new(MemDevice::new(MemFile::Full))).unwrap();
|
||||
register(Arc::new(MemDevice::new(MemFile::Null))).unwrap();
|
||||
register(Arc::new(MemDevice::new(MemFile::Random))).unwrap();
|
||||
register(Arc::new(MemDevice::new(MemFile::Urandom))).unwrap();
|
||||
register(Arc::new(MemDevice::new(MemFile::Zero))).unwrap();
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Misc devices.
|
||||
//!
|
||||
//! Character device with major number 10.
|
||||
|
||||
use device_id::MajorId;
|
||||
use spin::Once;
|
||||
|
||||
use super::char::{acquire_major, MajorIdOwner};
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", feature = "cvm_guest"))]
|
||||
pub mod tdxguest;
|
||||
|
||||
static MISC_MAJOR: Once<MajorIdOwner> = Once::new();
|
||||
|
||||
pub(super) fn init_in_first_kthread() {
|
||||
MISC_MAJOR.call_once(|| acquire_major(MajorId::new(10)).unwrap());
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ostd::if_tdx_enabled!({
|
||||
super::char::register(tdxguest::TdxGuest::new()).unwrap();
|
||||
});
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::sync::{Arc, Weak};
|
||||
use core::{
|
||||
mem::{offset_of, size_of},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use aster_util::{field_ptr, safe_ptr::SafePtr};
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use device_id::{DeviceId, MinorId};
|
||||
use ostd::{
|
||||
const_assert,
|
||||
mm::{
|
||||
|
|
@ -22,9 +23,9 @@ use tdx_guest::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
device::char::{CharDevice, DevtmpfsName},
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
},
|
||||
|
|
@ -32,19 +33,38 @@ use crate::{
|
|||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
pub struct TdxGuest;
|
||||
const TDX_GUEST_MINOR: u32 = 0x7b;
|
||||
|
||||
impl Device for TdxGuest {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Misc
|
||||
/// The `/dev/tdx_guest` device.
|
||||
#[derive(Debug)]
|
||||
pub struct TdxGuest {
|
||||
id: DeviceId,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl TdxGuest {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let major = super::MISC_MAJOR.get().unwrap().get();
|
||||
let minor = MinorId::new(TDX_GUEST_MINOR);
|
||||
|
||||
Arc::new_cyclic(|weak| Self {
|
||||
id: DeviceId::new(major, minor),
|
||||
weak_self: weak.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for TdxGuest {
|
||||
fn devtmpfs_name(&self) -> DevtmpfsName {
|
||||
DevtmpfsName::new("tdx_guest", None)
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(MajorId::new(0xa), MinorId::new(0x7b))
|
||||
self.id
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(Self))
|
||||
fn open(&self) -> Result<Arc<dyn FileIo>> {
|
||||
Ok(self.weak_self.upgrade().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,24 +1,18 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod char;
|
||||
mod disk;
|
||||
mod full;
|
||||
mod null;
|
||||
mod mem;
|
||||
pub mod misc;
|
||||
mod pty;
|
||||
mod random;
|
||||
mod shm;
|
||||
pub mod tty;
|
||||
mod urandom;
|
||||
mod zero;
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", feature = "cvm_guest"))]
|
||||
pub mod tdxguest;
|
||||
|
||||
use alloc::format;
|
||||
|
||||
use device_id::DeviceId;
|
||||
pub use mem::{getrandom, geturandom};
|
||||
pub use pty::{new_pty_pair, PtyMaster, PtySlave};
|
||||
pub use random::Random;
|
||||
pub use urandom::Urandom;
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
|
|
@ -32,6 +26,8 @@ use crate::{
|
|||
|
||||
pub fn init_in_first_kthread() {
|
||||
disk::init_in_first_kthread();
|
||||
mem::init_in_first_kthread();
|
||||
misc::init_in_first_kthread();
|
||||
}
|
||||
|
||||
/// Init the device node in fs, must be called after mounting rootfs.
|
||||
|
|
@ -43,12 +39,6 @@ pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
|||
let dev_path = fs_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
||||
dev_path.mount(RamFs::new(), PerMountFlags::default(), ctx)?;
|
||||
|
||||
let null = Arc::new(null::Null);
|
||||
add_node(null, "null", &fs_resolver)?;
|
||||
|
||||
let zero = Arc::new(zero::Zero);
|
||||
add_node(zero, "zero", &fs_resolver)?;
|
||||
|
||||
tty::init();
|
||||
|
||||
let tty = Arc::new(tty::TtyDevice);
|
||||
|
|
@ -61,24 +51,12 @@ pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
|||
add_node(tty.clone(), &format!("tty{}", index), &fs_resolver)?;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ostd::if_tdx_enabled!({
|
||||
add_node(Arc::new(tdxguest::TdxGuest), "tdx_guest", &fs_resolver)?;
|
||||
});
|
||||
|
||||
let random = Arc::new(random::Random);
|
||||
add_node(random, "random", &fs_resolver)?;
|
||||
|
||||
let urandom = Arc::new(urandom::Urandom);
|
||||
add_node(urandom, "urandom", &fs_resolver)?;
|
||||
|
||||
let full = Arc::new(full::Full);
|
||||
add_node(full, "full", &fs_resolver)?;
|
||||
|
||||
pty::init_in_first_process(&fs_resolver, ctx)?;
|
||||
|
||||
shm::init_in_first_process(&fs_resolver, ctx)?;
|
||||
|
||||
char::init_in_first_process(&fs_resolver)?;
|
||||
|
||||
disk::init_in_first_process(&fs_resolver)?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -93,11 +71,6 @@ pub fn get_device(devid: DeviceId) -> Result<Arc<dyn Device>> {
|
|||
let minor = devid.minor().get();
|
||||
|
||||
match (major, minor) {
|
||||
(1, 3) => Ok(Arc::new(null::Null)),
|
||||
(1, 5) => Ok(Arc::new(zero::Zero)),
|
||||
(1, 7) => Ok(Arc::new(full::Full)),
|
||||
(1, 8) => Ok(Arc::new(random::Random)),
|
||||
(1, 9) => Ok(Arc::new(urandom::Urandom)),
|
||||
(4, minor) => {
|
||||
let Some(tty) = tty::iter_n_tty().nth(minor as usize) else {
|
||||
return_errno_with_message!(Errno::EINVAL, "the TTY minor ID is invalid");
|
||||
|
|
@ -105,6 +78,11 @@ pub fn get_device(devid: DeviceId) -> Result<Arc<dyn Device>> {
|
|||
Ok(tty.clone())
|
||||
}
|
||||
(5, 0) => Ok(Arc::new(tty::TtyDevice)),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "the device ID is invalid or unsupported"),
|
||||
_ => char::lookup(devid)
|
||||
.map(|device| Arc::new(char::CharFile::new(device)) as Arc<dyn Device>)
|
||||
.ok_or(Error::with_message(
|
||||
Errno::EINVAL,
|
||||
"the device ID is invalid or unsupported",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
pub struct Null;
|
||||
|
||||
impl Device for Null {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(3))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for Null {
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for Null {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
_writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let len = reader.remain();
|
||||
reader.skip(len);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Null {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use super::Urandom;
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
pub struct Random;
|
||||
|
||||
impl Random {
|
||||
pub fn getrandom(writer: &mut VmWriter) -> Result<usize> {
|
||||
// TODO: Support true randomness by collecting environment noise.
|
||||
Urandom::getrandom(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Random {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(8))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for Random {
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for Random {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
Self::getrandom(writer)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let len = reader.remain();
|
||||
reader.skip(len);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Random {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::random::getrandom,
|
||||
};
|
||||
|
||||
pub struct Urandom;
|
||||
|
||||
impl Urandom {
|
||||
pub fn getrandom(writer: &mut VmWriter) -> Result<usize> {
|
||||
const IO_CAPABILITY: usize = 4096;
|
||||
|
||||
if !writer.has_avail() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut buffer = vec![0; writer.avail().min(IO_CAPABILITY)];
|
||||
let mut written_bytes = 0;
|
||||
|
||||
while writer.has_avail() {
|
||||
getrandom(&mut buffer[..writer.avail().min(IO_CAPABILITY)]);
|
||||
match writer.write_fallible(&mut VmReader::from(buffer.as_slice())) {
|
||||
Ok(len) => written_bytes += len,
|
||||
Err((err, 0)) if written_bytes == 0 => return Err(err.into()),
|
||||
Err((_, len)) => return Ok(written_bytes + len),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(written_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Urandom {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(9))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for Urandom {
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for Urandom {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
Self::getrandom(writer)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let len = reader.remain();
|
||||
reader.skip(len);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Urandom {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
};
|
||||
|
||||
pub struct Zero;
|
||||
|
||||
impl Device for Zero {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Char
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(5))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for Zero {
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for Zero {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let read_len = writer.fill_zeros(writer.avail())?;
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
Ok(reader.remain())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Zero {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ use ostd::{
|
|||
sync::RwMutex,
|
||||
};
|
||||
|
||||
use crate::{device::tdxguest::tdx_get_quote, fs::configfs};
|
||||
use crate::{device::misc::tdxguest::tdx_get_quote, fs::configfs};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Tsm {
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ pub fn sys_getrandom(buf: Vaddr, count: usize, flags: u32, ctx: &Context) -> Res
|
|||
let user_space = ctx.user_space();
|
||||
let mut writer = user_space.writer(buf, count)?;
|
||||
let read_len = if flags.contains(GetRandomFlags::GRND_RANDOM) {
|
||||
device::Random::getrandom(&mut writer)?
|
||||
device::getrandom(&mut writer)?
|
||||
} else {
|
||||
device::Urandom::getrandom(&mut writer)?
|
||||
device::geturandom(&mut writer)?
|
||||
};
|
||||
Ok(SyscallReturn::Return(read_len as isize))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue