blob: 23877369dc6c549ed45f693a9c529e4c4747165d [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::{
auth::FsCred,
device::{simple_device_ops, DeviceMode},
fs::{
buffers::{InputBuffer, OutputBuffer},
kobject::{KObjectDeviceAttribute, KType},
sysfs::SysFsDirectory,
*,
},
logging::*,
mm::*,
task::*,
types::*,
};
use fuchsia_zircon::{self as zx, cprng_draw};
use std::sync::Arc;
#[derive(Default)]
pub struct DevNull;
pub fn new_null_file(kernel: &Arc<Kernel>, flags: OpenFlags) -> FileHandle {
Anon::new_file_extended(
kernel,
Box::new(DevNull),
flags,
FsNodeInfo::new_factory(FileMode::from_bits(0o666), FsCred::root()),
)
}
impl FileOps for DevNull {
fileops_impl_seekless!();
fn write(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn InputBuffer,
) -> Result<usize, Errno> {
// Writes to /dev/null on Linux treat the input buffer in an unconventional way. The actual
// data is not touched and if the input parameters are plausible the device claims to
// successfully write up to MAX_RW_COUNT bytes. If the input parameters are outside of the
// user accessible address space, writes will return EFAULT.
// For debugging log up to 4096 bytes from the input buffer. We don't care about errors when
// trying to read data to log. The amount of data logged is chosen arbitrarily.
let bytes_to_log = std::cmp::min(4096, data.available());
let mut log_buffer = vec![0; bytes_to_log];
let bytes_logged = match data.read(&mut log_buffer) {
Ok(bytes) => {
log_info!("write to devnull: {:?}", String::from_utf8_lossy(&log_buffer[0..bytes]));
bytes
}
Err(_) => 0,
};
Ok(bytes_logged + data.drain())
}
fn read(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
_data: &mut dyn OutputBuffer,
) -> Result<usize, Errno> {
Ok(0)
}
fn to_handle(
&self,
_file: &FileHandle,
_current_task: &CurrentTask,
) -> Result<Option<zx::Handle>, Errno> {
Ok(None)
}
}
#[derive(Default)]
struct DevZero;
impl FileOps for DevZero {
fileops_impl_seekless!();
fn mmap(
&self,
_file: &FileObject,
current_task: &CurrentTask,
addr: DesiredAddress,
vmo_offset: u64,
length: usize,
prot_flags: ProtectionFlags,
mut options: MappingOptions,
filename: NamespaceNode,
) -> Result<MappedVmo, Errno> {
// All /dev/zero mappings behave as anonymous mappings.
//
// This means that we always create a new zero-filled VMO for this mmap request.
// Memory is never shared between two mappings of /dev/zero, even if
// `MappingOptions::SHARED` is set.
//
// Similar to anonymous mappings, if this process were to request a shared mapping
// of /dev/zero and then fork, the child and the parent process would share the
// VMO created here.
let vmo = create_anonymous_mapping_vmo(length as u64)?;
options |= MappingOptions::ANONYMOUS;
let addr = current_task.mm.map(
addr,
vmo.clone(),
vmo_offset,
length,
prot_flags,
options,
// We set the filename here, even though we are creating what is
// functionally equivalent to an anonymous mapping. Doing so affects
// the output of `/proc/self/maps` and identifies this mapping as
// file-based.
MappingName::File(filename),
FileWriteGuardRef(None),
)?;
Ok(MappedVmo::new(vmo, addr))
}
fn write(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn InputBuffer,
) -> Result<usize, Errno> {
Ok(data.drain())
}
fn read(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn OutputBuffer,
) -> Result<usize, Errno> {
data.zero()
}
}
#[derive(Default)]
struct DevFull;
impl FileOps for DevFull {
fileops_impl_seekless!();
fn write(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
_data: &mut dyn InputBuffer,
) -> Result<usize, Errno> {
error!(ENOSPC)
}
fn read(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn OutputBuffer,
) -> Result<usize, Errno> {
data.write_each(&mut |bytes| {
bytes.fill(0);
Ok(bytes.len())
})
}
}
#[derive(Default)]
pub struct DevRandom;
impl FileOps for DevRandom {
fileops_impl_seekless!();
fn write(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn InputBuffer,
) -> Result<usize, Errno> {
Ok(data.drain())
}
fn read(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn OutputBuffer,
) -> Result<usize, Errno> {
data.write_each(&mut |bytes| {
cprng_draw(bytes);
Ok(bytes.len())
})
}
}
#[derive(Default)]
struct DevKmsg;
impl FileOps for DevKmsg {
fileops_impl_seekless!();
fn read(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
_data: &mut dyn OutputBuffer,
) -> Result<usize, Errno> {
Ok(0)
}
fn write(
&self,
_file: &FileObject,
_current_task: &CurrentTask,
_offset: usize,
data: &mut dyn InputBuffer,
) -> Result<usize, Errno> {
let bytes = data.read_all()?;
log!(
level = info,
tag = "kmsg",
"{}",
String::from_utf8_lossy(&bytes).trim_end_matches('\n')
);
Ok(bytes.len())
}
}
pub fn mem_device_init(kernel: &Arc<Kernel>) {
let mem_class = kernel.device_registry.virtual_bus().get_or_create_child(
b"mem",
KType::Class,
SysFsDirectory::new,
);
kernel.add_and_register_device(
KObjectDeviceAttribute::new(
Some(mem_class.clone()),
b"null",
b"null",
DeviceType::NULL,
DeviceMode::Char,
),
simple_device_ops::<DevNull>,
);
kernel.add_and_register_device(
KObjectDeviceAttribute::new(
Some(mem_class.clone()),
b"zero",
b"zero",
DeviceType::ZERO,
DeviceMode::Char,
),
simple_device_ops::<DevZero>,
);
kernel.add_and_register_device(
KObjectDeviceAttribute::new(
Some(mem_class.clone()),
b"full",
b"full",
DeviceType::FULL,
DeviceMode::Char,
),
simple_device_ops::<DevFull>,
);
kernel.add_and_register_device(
KObjectDeviceAttribute::new(
Some(mem_class.clone()),
b"random",
b"random",
DeviceType::RANDOM,
DeviceMode::Char,
),
simple_device_ops::<DevRandom>,
);
kernel.add_and_register_device(
KObjectDeviceAttribute::new(
Some(mem_class.clone()),
b"urandom",
b"urandom",
DeviceType::URANDOM,
DeviceMode::Char,
),
simple_device_ops::<DevRandom>,
);
kernel.add_and_register_device(
KObjectDeviceAttribute::new(
Some(mem_class),
b"kmsg",
b"kmsg",
DeviceType::KMSG,
DeviceMode::Char,
),
simple_device_ops::<DevKmsg>,
);
}