Support `virtio-rng` and expose it as `/dev/hwrng`
This commit is contained in:
parent
c3dda32805
commit
3382754270
|
|
@ -0,0 +1,85 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, format, string::String, sync::Arc};
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use aster_util::mem_obj_slice::Slice;
|
||||||
|
use log::debug;
|
||||||
|
use ostd::{
|
||||||
|
arch::trap::TrapFrame,
|
||||||
|
mm::dma::{DmaStream, FromDevice},
|
||||||
|
sync::{Mutex, SpinLock},
|
||||||
|
};
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
device::{
|
||||||
|
VirtioDeviceError,
|
||||||
|
entropy::{handle_recv_irq, register_device},
|
||||||
|
},
|
||||||
|
queue::VirtQueue,
|
||||||
|
transport::VirtioTransport,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static ENTROPY_DEVICE_PREFIX: &str = "virtio_rng.";
|
||||||
|
static ENTROPY_DEVICE_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
pub static RNG_CURRENT: Once<Mutex<String>> = Once::new();
|
||||||
|
|
||||||
|
pub struct EntropyDevice {
|
||||||
|
transport: SpinLock<Box<dyn VirtioTransport>>,
|
||||||
|
pub request_queue: SpinLock<VirtQueue>,
|
||||||
|
pub receive_buffer: DmaStream<FromDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntropyDevice {
|
||||||
|
pub fn init(mut transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
|
||||||
|
let request_queue = SpinLock::new(VirtQueue::new(0, 1, transport.as_mut()).unwrap());
|
||||||
|
let receive_buffer = DmaStream::alloc_uninit(1, false).unwrap();
|
||||||
|
|
||||||
|
let device = Arc::new(EntropyDevice {
|
||||||
|
transport: SpinLock::new(transport),
|
||||||
|
request_queue,
|
||||||
|
receive_buffer,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register irq callbacks
|
||||||
|
let mut transport = device.transport.disable_irq().lock();
|
||||||
|
transport
|
||||||
|
.register_queue_callback(0, Box::new(handle_recv_irq), false)
|
||||||
|
.unwrap();
|
||||||
|
transport
|
||||||
|
.register_cfg_callback(Box::new(config_space_change))
|
||||||
|
.unwrap();
|
||||||
|
transport.finish_init();
|
||||||
|
drop(transport);
|
||||||
|
|
||||||
|
let device_id = ENTROPY_DEVICE_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
let name = format!("{ENTROPY_DEVICE_PREFIX}{device_id}");
|
||||||
|
|
||||||
|
register_device(name.clone(), device);
|
||||||
|
|
||||||
|
RNG_CURRENT.call_once(|| Mutex::new(name));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_pop(&self) -> bool {
|
||||||
|
let request_queue = self.request_queue.lock();
|
||||||
|
request_queue.can_pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate_receive_buffer(&self, receive_queue: &mut VirtQueue, to_read: usize) {
|
||||||
|
receive_queue
|
||||||
|
.add_dma_buf(&[], &[&Slice::new(&self.receive_buffer, 0..to_read)])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if receive_queue.should_notify() {
|
||||||
|
receive_queue.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_space_change(_: &TrapFrame) {
|
||||||
|
debug!("Virtio-Entropy device configuration space change");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, sync::Arc, vec::Vec};
|
||||||
|
|
||||||
|
use ostd::{
|
||||||
|
arch::trap::TrapFrame,
|
||||||
|
sync::{LocalIrqDisabled, SpinLock},
|
||||||
|
};
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
use crate::device::entropy::device::EntropyDevice;
|
||||||
|
|
||||||
|
pub mod device;
|
||||||
|
|
||||||
|
pub trait EntropyDeviceIrqHandler = Fn() + Send + Sync + 'static;
|
||||||
|
|
||||||
|
pub fn register_device(name: String, device: Arc<EntropyDevice>) {
|
||||||
|
ENTROPY_DEVICE_TABLE
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.insert(name, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_device(str: &str) -> Option<Arc<EntropyDevice>> {
|
||||||
|
let lock = ENTROPY_DEVICE_TABLE.get().unwrap().lock();
|
||||||
|
lock.get(str).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_devices() -> Vec<(String, Arc<EntropyDevice>)> {
|
||||||
|
let entropy_devs = ENTROPY_DEVICE_TABLE.get().unwrap().lock();
|
||||||
|
|
||||||
|
entropy_devs
|
||||||
|
.iter()
|
||||||
|
.map(|(name, dev)| (name.clone(), dev.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_recv_callback(callback: impl EntropyDeviceIrqHandler) {
|
||||||
|
ENTROPY_DEVICE_CALLBACK.call_once(|| Box::new(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_recv_irq(_: &TrapFrame) {
|
||||||
|
ENTROPY_DEVICE_CALLBACK.get().unwrap()()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
ENTROPY_DEVICE_TABLE.call_once(|| SpinLock::new(BTreeMap::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static ENTROPY_DEVICE_CALLBACK: Once<Box<dyn EntropyDeviceIrqHandler>> = Once::new();
|
||||||
|
|
||||||
|
pub static ENTROPY_DEVICE_TABLE: Once<
|
||||||
|
SpinLock<BTreeMap<String, Arc<EntropyDevice>>, LocalIrqDisabled>,
|
||||||
|
> = Once::new();
|
||||||
|
|
@ -6,6 +6,7 @@ use crate::queue::QueueError;
|
||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod console;
|
pub mod console;
|
||||||
|
pub mod entropy;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod network;
|
pub mod network;
|
||||||
pub mod socket;
|
pub mod socket;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use device::{
|
||||||
VirtioDeviceType,
|
VirtioDeviceType,
|
||||||
block::device::BlockDevice,
|
block::device::BlockDevice,
|
||||||
console::device::ConsoleDevice,
|
console::device::ConsoleDevice,
|
||||||
|
entropy::{self, device::EntropyDevice},
|
||||||
input::device::InputDevice,
|
input::device::InputDevice,
|
||||||
network::device::NetworkDevice,
|
network::device::NetworkDevice,
|
||||||
socket::{self, device::SocketDevice},
|
socket::{self, device::SocketDevice},
|
||||||
|
|
@ -42,6 +43,8 @@ fn virtio_component_init() -> Result<(), ComponentInitError> {
|
||||||
|
|
||||||
// Find all devices and register them to the corresponding crate
|
// Find all devices and register them to the corresponding crate
|
||||||
transport::init();
|
transport::init();
|
||||||
|
// For entropy table static init
|
||||||
|
entropy::init();
|
||||||
// For vsock table static init
|
// For vsock table static init
|
||||||
socket::init();
|
socket::init();
|
||||||
while let Some(mut transport) = pop_device_transport() {
|
while let Some(mut transport) = pop_device_transport() {
|
||||||
|
|
@ -70,9 +73,10 @@ fn virtio_component_init() -> Result<(), ComponentInitError> {
|
||||||
let device_type = transport.device_type();
|
let device_type = transport.device_type();
|
||||||
let res = match transport.device_type() {
|
let res = match transport.device_type() {
|
||||||
VirtioDeviceType::Block => BlockDevice::init(transport),
|
VirtioDeviceType::Block => BlockDevice::init(transport),
|
||||||
|
VirtioDeviceType::Console => ConsoleDevice::init(transport),
|
||||||
|
VirtioDeviceType::Entropy => EntropyDevice::init(transport),
|
||||||
VirtioDeviceType::Input => InputDevice::init(transport),
|
VirtioDeviceType::Input => InputDevice::init(transport),
|
||||||
VirtioDeviceType::Network => NetworkDevice::init(transport),
|
VirtioDeviceType::Network => NetworkDevice::init(transport),
|
||||||
VirtioDeviceType::Console => ConsoleDevice::init(transport),
|
|
||||||
VirtioDeviceType::Socket => SocketDevice::init(transport),
|
VirtioDeviceType::Socket => SocketDevice::init(transport),
|
||||||
_ => {
|
_ => {
|
||||||
warn!("[Virtio]: Found unimplemented device:{:?}", device_type);
|
warn!("[Virtio]: Found unimplemented device:{:?}", device_type);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
// 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(())
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
mod evdev;
|
mod evdev;
|
||||||
mod fb;
|
mod fb;
|
||||||
|
mod hwrng;
|
||||||
mod mem;
|
mod mem;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
mod pty;
|
mod pty;
|
||||||
|
|
@ -38,6 +39,7 @@ pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
||||||
let dev_path = path_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
let dev_path = path_resolver.lookup(&FsPath::try_from("/dev")?)?;
|
||||||
dev_path.mount(RamFs::new(), PerMountFlags::default(), ctx)?;
|
dev_path.mount(RamFs::new(), PerMountFlags::default(), ctx)?;
|
||||||
|
|
||||||
|
hwrng::init_in_first_process()?;
|
||||||
tty::init_in_first_process()?;
|
tty::init_in_first_process()?;
|
||||||
pty::init_in_first_process(&path_resolver, ctx)?;
|
pty::init_in_first_process(&path_resolver, ctx)?;
|
||||||
shm::init_in_first_process(&path_resolver, ctx)?;
|
shm::init_in_first_process(&path_resolver, ctx)?;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/fb.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../test.h"
|
||||||
|
|
||||||
|
#define HWRNG_DEVICE "/dev/hwrng"
|
||||||
|
#define HWRNG_MAJOR 10
|
||||||
|
#define HWRNG_MINOR 183
|
||||||
|
|
||||||
|
static int hwrng_fd = -1;
|
||||||
|
|
||||||
|
FN_TEST(open_hwrng)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
hwrng_fd = TEST_SUCC(open(HWRNG_DEVICE, O_RDONLY));
|
||||||
|
|
||||||
|
TEST_RES(fstat(hwrng_fd, &st),
|
||||||
|
S_ISCHR(st.st_mode) && major(st.st_rdev) == HWRNG_MAJOR &&
|
||||||
|
minor(st.st_rdev) == HWRNG_MINOR);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(read_hwrng)
|
||||||
|
{
|
||||||
|
uint8_t buf1[64];
|
||||||
|
uint8_t buf2[64];
|
||||||
|
|
||||||
|
ssize_t ret = read(hwrng_fd, buf1, sizeof(buf1));
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == ENODEV) {
|
||||||
|
fprintf(stderr, "hwrng tests skipped: %s (%s)\n",
|
||||||
|
HWRNG_DEVICE, strerror(errno));
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
fprintf(stderr,
|
||||||
|
"fatal error: read_hwrng: read('%s') failed: %s\n",
|
||||||
|
HWRNG_DEVICE, strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_RES(read(hwrng_fd, buf2, sizeof(buf2)), _ret == 64);
|
||||||
|
|
||||||
|
TEST_RES(memcmp(buf1, buf2, sizeof(buf1)), _ret != 0);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_SETUP(close_hwrng)
|
||||||
|
{
|
||||||
|
CHECK(close(hwrng_fd));
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
|
@ -118,6 +118,8 @@ QEMU_ARGS="\
|
||||||
-machine q35,kernel-irqchip=split \
|
-machine q35,kernel-irqchip=split \
|
||||||
-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,serial=vext2,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,request-merging=off,backend_defaults=off,discard=off,write-zeroes=off,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,serial=vext2,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,request-merging=off,backend_defaults=off,discard=off,write-zeroes=off,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
||||||
-device virtio-blk-pci,bus=pcie.0,addr=0x7,drive=x1,serial=vexfat,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,request-merging=off,backend_defaults=off,discard=off,write-zeroes=off,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
-device virtio-blk-pci,bus=pcie.0,addr=0x7,drive=x1,serial=vexfat,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,request-merging=off,backend_defaults=off,discard=off,write-zeroes=off,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
||||||
|
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||||
|
-device virtio-rng-pci,bus=pcie.0,addr=0x8,disable-legacy=on,disable-modern=off,rng=rng0,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
||||||
-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off$VIRTIO_NET_FEATURES$IOMMU_DEV_EXTRA \
|
-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off$VIRTIO_NET_FEATURES$IOMMU_DEV_EXTRA \
|
||||||
-device virtio-serial-pci,disable-legacy=on,disable-modern=off$IOMMU_DEV_EXTRA \
|
-device virtio-serial-pci,disable-legacy=on,disable-modern=off$IOMMU_DEV_EXTRA \
|
||||||
$CONSOLE_ARGS \
|
$CONSOLE_ARGS \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue