270 lines
7.2 KiB
Rust
270 lines
7.2 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use alloc::{boxed::Box, string::String, sync::Arc};
|
|
|
|
use aster_virtio::device::entropy::{
|
|
all_devices,
|
|
device::{EntropyDevice, RNG_CURRENT},
|
|
get_device, register_recv_callback,
|
|
};
|
|
use device_id::{DeviceId, MajorId, MinorId};
|
|
use ostd::mm::{VmReader, VmWriter, io_util::HasVmReaderWriter};
|
|
|
|
use crate::{
|
|
device::registry::char,
|
|
events::IoEvents,
|
|
fs::{
|
|
device::{Device, DeviceType},
|
|
inode_handle::FileIo,
|
|
utils::{InodeIo, StatusFlags},
|
|
},
|
|
prelude::*,
|
|
process::signal::{PollHandle, Pollable, Pollee},
|
|
util::ring_buffer::RingBuffer,
|
|
};
|
|
|
|
static HW_RNG_HANDLE: Mutex<Option<Arc<HwRngHandle>>> = Mutex::new(None);
|
|
|
|
const HW_RNG_BUFFER_CAPACITY: usize = 4096;
|
|
|
|
#[derive(Clone)]
|
|
struct HwRngHandle {
|
|
rng: Arc<EntropyDevice>,
|
|
pollee: Pollee,
|
|
recv_state: Arc<SpinLock<HwRngRecvState>>,
|
|
}
|
|
|
|
struct HwRngRecvState {
|
|
buffer: RingBuffer<u8>,
|
|
in_flight: bool,
|
|
}
|
|
|
|
impl HwRngHandle {
|
|
pub fn new(rng: Arc<EntropyDevice>) -> Self {
|
|
Self {
|
|
rng,
|
|
pollee: Pollee::new(),
|
|
recv_state: Arc::new(SpinLock::new(HwRngRecvState {
|
|
buffer: RingBuffer::new(HW_RNG_BUFFER_CAPACITY),
|
|
in_flight: false,
|
|
})),
|
|
}
|
|
}
|
|
|
|
pub fn check_io_events(&self) -> IoEvents {
|
|
let mut events = IoEvents::empty();
|
|
|
|
if !self.recv_state.lock().buffer.is_empty() {
|
|
events |= IoEvents::IN;
|
|
}
|
|
|
|
events
|
|
}
|
|
|
|
fn activate_receive_buffer(&self) {
|
|
let should_activate = {
|
|
let mut state = self.recv_state.disable_irq().lock();
|
|
if state.in_flight || state.buffer.is_full() {
|
|
return;
|
|
}
|
|
state.in_flight = true;
|
|
true
|
|
};
|
|
|
|
if should_activate {
|
|
let mut request_queue = self.rng.request_queue.disable_irq().lock();
|
|
self.rng
|
|
.activate_receive_buffer(&mut request_queue, PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
fn handle_recv_irq(&self) {
|
|
let mut request_queue = self.rng.request_queue.disable_irq().lock();
|
|
let Ok((_, used_len)) = request_queue.pop_used() else {
|
|
return;
|
|
};
|
|
drop(request_queue);
|
|
|
|
let used_len = used_len as usize;
|
|
self.rng
|
|
.receive_buffer
|
|
.sync_from_device(0..used_len)
|
|
.unwrap();
|
|
|
|
let (wrote, should_activate) = {
|
|
let mut state = self.recv_state.disable_irq().lock();
|
|
let free_len = state.buffer.free_len();
|
|
let read_len = used_len.min(free_len);
|
|
|
|
let mut wrote = 0;
|
|
if read_len > 0 {
|
|
let mut reader = self.rng.receive_buffer.reader().unwrap();
|
|
reader.limit(read_len);
|
|
|
|
let mut tmp = vec![0u8; read_len];
|
|
let mut writer = VmWriter::from(tmp.as_mut_slice());
|
|
wrote = reader.read(&mut writer);
|
|
state.buffer.push_slice(&tmp[..wrote]).unwrap();
|
|
}
|
|
state.in_flight = false;
|
|
|
|
let should_activate = if state.buffer.is_full() {
|
|
false
|
|
} else {
|
|
state.in_flight = true;
|
|
true
|
|
};
|
|
|
|
(wrote, should_activate)
|
|
};
|
|
|
|
if wrote > 0 {
|
|
self.pollee.notify(IoEvents::IN);
|
|
}
|
|
|
|
if should_activate {
|
|
let mut request_queue = self.rng.request_queue.disable_irq().lock();
|
|
self.rng
|
|
.activate_receive_buffer(&mut request_queue, PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
fn try_read(&self, writer: &mut VmWriter) -> Result<usize> {
|
|
let mut state = self.recv_state.disable_irq().lock();
|
|
if state.buffer.is_empty() {
|
|
return_errno_with_message!(Errno::EAGAIN, "entropy buffer is not ready");
|
|
}
|
|
|
|
state.buffer.read_fallible(writer)
|
|
}
|
|
}
|
|
|
|
struct HwRngDevice;
|
|
|
|
impl Device for HwRngDevice {
|
|
fn type_(&self) -> DeviceType {
|
|
DeviceType::Char
|
|
}
|
|
|
|
fn id(&self) -> DeviceId {
|
|
// Same Value with Linux: major 10, minor 183
|
|
device_id::DeviceId::new(MajorId::new(10), MinorId::new(183))
|
|
}
|
|
|
|
fn devtmpfs_path(&self) -> Option<String> {
|
|
Some("hwrng".into())
|
|
}
|
|
|
|
fn open(&self) -> Result<Box<dyn FileIo>> {
|
|
if RNG_CURRENT.get().is_none() {
|
|
return_errno_with_message!(Errno::ENODEV, "No hardware RNG device found");
|
|
}
|
|
|
|
let mut handle_lock = HW_RNG_HANDLE.lock();
|
|
|
|
if handle_lock.is_none() {
|
|
let name_lock = RNG_CURRENT.get().unwrap();
|
|
|
|
let mut device = get_device(&name_lock.lock());
|
|
|
|
if device.is_none() {
|
|
let all = all_devices();
|
|
if let Some((fallback_name, fallback_device)) = all.first() {
|
|
*name_lock.lock() = fallback_name.clone();
|
|
device = Some(fallback_device.clone());
|
|
}
|
|
}
|
|
|
|
let device = device.ok_or_else(|| {
|
|
Error::with_message(Errno::ENODEV, "No hardware RNG device found")
|
|
})?;
|
|
|
|
*handle_lock = Some(Arc::new(HwRngHandle::new(device)));
|
|
}
|
|
|
|
let hwrng_handle = handle_lock.as_ref().unwrap();
|
|
Ok(Box::new((**hwrng_handle).clone()))
|
|
}
|
|
}
|
|
|
|
impl Pollable for HwRngHandle {
|
|
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
|
self.pollee
|
|
.poll_with(mask, poller, || self.check_io_events())
|
|
}
|
|
}
|
|
|
|
impl InodeIo for HwRngHandle {
|
|
fn read_at(
|
|
&self,
|
|
_offset: usize,
|
|
writer: &mut VmWriter,
|
|
status_flags: StatusFlags,
|
|
) -> Result<usize> {
|
|
let len = writer.avail();
|
|
let mut written_bytes = 0;
|
|
let is_nonblocking = status_flags.contains(StatusFlags::O_NONBLOCK);
|
|
|
|
while written_bytes < len {
|
|
self.activate_receive_buffer();
|
|
|
|
let read_once = if is_nonblocking {
|
|
self.try_read(writer)
|
|
} else {
|
|
self.wait_events(IoEvents::IN, None, || self.try_read(writer))
|
|
};
|
|
|
|
match read_once {
|
|
Ok(0) => break,
|
|
Ok(copied) => {
|
|
written_bytes += copied;
|
|
self.pollee.invalidate();
|
|
}
|
|
Err(err) if is_nonblocking && err.error() == Errno::EAGAIN => {
|
|
if written_bytes == 0 {
|
|
return Err(err);
|
|
}
|
|
break;
|
|
}
|
|
Err(err) => return Err(err),
|
|
}
|
|
}
|
|
|
|
Ok(written_bytes)
|
|
}
|
|
|
|
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 HwRngHandle {
|
|
fn check_seekable(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
fn is_offset_aware(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub(super) fn init_in_first_process() -> Result<()> {
|
|
register_recv_callback(|| {
|
|
let device_lock = HW_RNG_HANDLE.lock();
|
|
if let Some(hwrng_handle) = &*device_lock {
|
|
hwrng_handle.handle_recv_irq();
|
|
}
|
|
});
|
|
|
|
char::register(Arc::new(HwRngDevice))?;
|
|
|
|
Ok(())
|
|
}
|