mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 18:03:25 +00:00
Support virtio console device
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
e9544d489f
commit
01e485b96e
@ -14,6 +14,7 @@ align_ext = { path = "../../../framework/libs/align_ext" }
|
||||
jinux-input = { path = "../input" }
|
||||
jinux-block = { path = "../block" }
|
||||
jinux-network = { path = "../network" }
|
||||
jinux-console = { path = "../console" }
|
||||
jinux-frame = { path = "../../../framework/jinux-frame" }
|
||||
jinux-util = { path = "../../libs/jinux-util" }
|
||||
jinux-rights = { path = "../../libs/jinux-rights" }
|
||||
@ -21,6 +22,7 @@ typeflags-util = { path = "../../libs/typeflags-util" }
|
||||
pod = { git = "https://github.com/jinzhao-dev/pod", rev = "d7dba56" }
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
log = "0.4"
|
||||
bit_field = "0.10.1"
|
||||
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
||||
smoltcp = { version = "0.9.1", default-features = false, features = [
|
||||
"alloc",
|
||||
|
34
services/comps/virtio/src/device/console/config.rs
Normal file
34
services/comps/virtio/src/device/console/config.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use jinux_frame::io_mem::IoMem;
|
||||
use jinux_util::safe_ptr::SafePtr;
|
||||
use pod::Pod;
|
||||
|
||||
use crate::transport::VirtioTransport;
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct ConsoleFeatures: u64{
|
||||
/// Configuration cols and rows are valid.
|
||||
const VIRTIO_CONSOLE_F_SIZE = 1 << 0;
|
||||
/// Device has support for multiple ports;
|
||||
/// max_nr_ports is valid and control virtqueues will be used.
|
||||
const VIRTIO_CONSOLE_F_MULTIPORT = 1 << 1;
|
||||
/// Device has support for emergency write.
|
||||
/// Configuration field emerg_wr is valid.
|
||||
const VIRTIO_CONSOLE_F_EMERG_WRITE = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Pod, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct VirtioConsoleConfig {
|
||||
pub cols: u16,
|
||||
pub row: u16,
|
||||
pub max_nr_ports: u32,
|
||||
pub emerg_wr: u32,
|
||||
}
|
||||
|
||||
impl VirtioConsoleConfig {
|
||||
pub(super) fn new(transport: &dyn VirtioTransport) -> SafePtr<Self, IoMem> {
|
||||
let memory = transport.device_config_memory();
|
||||
SafePtr::new(memory, 0)
|
||||
}
|
||||
}
|
147
services/comps/virtio/src/device/console/device.rs
Normal file
147
services/comps/virtio/src/device/console/device.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use core::hint::spin_loop;
|
||||
|
||||
use alloc::{boxed::Box, fmt::Debug, string::ToString, sync::Arc, vec::Vec};
|
||||
use jinux_console::{AnyConsoleDevice, ConsoleCallback};
|
||||
use jinux_frame::{config::PAGE_SIZE, io_mem::IoMem, sync::SpinLock, trap::TrapFrame};
|
||||
use jinux_util::safe_ptr::SafePtr;
|
||||
use log::debug;
|
||||
|
||||
use crate::{
|
||||
device::{console::config::ConsoleFeatures, VirtioDeviceError},
|
||||
queue::VirtQueue,
|
||||
transport::VirtioTransport,
|
||||
};
|
||||
|
||||
use super::{config::VirtioConsoleConfig, DEVICE_NAME};
|
||||
|
||||
pub struct ConsoleDevice {
|
||||
config: SafePtr<VirtioConsoleConfig, IoMem>,
|
||||
transport: Box<dyn VirtioTransport>,
|
||||
receive_queue: SpinLock<VirtQueue>,
|
||||
transmit_queue: SpinLock<VirtQueue>,
|
||||
buffer: SpinLock<Box<[u8; PAGE_SIZE]>>,
|
||||
callbacks: SpinLock<Vec<&'static ConsoleCallback>>,
|
||||
}
|
||||
|
||||
impl AnyConsoleDevice for ConsoleDevice {
|
||||
fn send(&self, value: &[u8]) {
|
||||
let mut transmit_queue = self.transmit_queue.lock_irq_disabled();
|
||||
transmit_queue.add(&[value], &[]).unwrap();
|
||||
if transmit_queue.should_notify() {
|
||||
transmit_queue.notify();
|
||||
}
|
||||
while !transmit_queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
transmit_queue.pop_used().unwrap();
|
||||
}
|
||||
|
||||
fn recv(&self, buf: &mut [u8]) -> Option<usize> {
|
||||
let mut receive_queue = self.receive_queue.lock_irq_disabled();
|
||||
if !receive_queue.can_pop() {
|
||||
return None;
|
||||
}
|
||||
let (_, len) = receive_queue.pop_used().unwrap();
|
||||
|
||||
let mut recv_buffer = self.buffer.lock();
|
||||
buf.copy_from_slice(&recv_buffer.as_ref()[..len as usize]);
|
||||
receive_queue.add(&[], &[recv_buffer.as_mut()]).unwrap();
|
||||
if receive_queue.should_notify() {
|
||||
receive_queue.notify();
|
||||
}
|
||||
Some(len as usize)
|
||||
}
|
||||
|
||||
fn register_callback(&self, callback: &'static (dyn Fn(&[u8]) + Send + Sync)) {
|
||||
self.callbacks.lock().push(callback);
|
||||
}
|
||||
|
||||
fn handle_irq(&self) {
|
||||
let mut receive_queue = self.receive_queue.lock_irq_disabled();
|
||||
if !receive_queue.can_pop() {
|
||||
return;
|
||||
}
|
||||
let (_, len) = receive_queue.pop_used().unwrap();
|
||||
let mut recv_buffer = self.buffer.lock();
|
||||
let buffer = &recv_buffer.as_ref()[..len as usize];
|
||||
let lock = self.callbacks.lock();
|
||||
for callback in lock.iter() {
|
||||
callback.call((buffer,));
|
||||
}
|
||||
receive_queue.add(&[], &[recv_buffer.as_mut()]).unwrap();
|
||||
if receive_queue.should_notify() {
|
||||
receive_queue.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ConsoleDevice {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("ConsoleDevice")
|
||||
.field("config", &self.config)
|
||||
.field("transport", &self.transport)
|
||||
.field("receive_queue", &self.receive_queue)
|
||||
.field("transmit_queue", &self.transmit_queue)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleDevice {
|
||||
pub fn negotiate_features(features: u64) -> u64 {
|
||||
let mut features = ConsoleFeatures::from_bits_truncate(features);
|
||||
// A virtio console device may have multiple ports, but we only use one port to communicate now.
|
||||
features.remove(ConsoleFeatures::VIRTIO_CONSOLE_F_MULTIPORT);
|
||||
features.bits()
|
||||
}
|
||||
|
||||
pub fn init(mut transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
|
||||
let config = VirtioConsoleConfig::new(transport.as_ref());
|
||||
const RECV0_QUEUE_INDEX: u16 = 0;
|
||||
const TRANSMIT0_QUEUE_INDEX: u16 = 1;
|
||||
let receive_queue =
|
||||
SpinLock::new(VirtQueue::new(RECV0_QUEUE_INDEX, 2, transport.as_mut()).unwrap());
|
||||
let transmit_queue =
|
||||
SpinLock::new(VirtQueue::new(TRANSMIT0_QUEUE_INDEX, 2, transport.as_mut()).unwrap());
|
||||
|
||||
let mut device = Self {
|
||||
config,
|
||||
transport,
|
||||
receive_queue,
|
||||
transmit_queue,
|
||||
buffer: SpinLock::new(Box::new([0; PAGE_SIZE])),
|
||||
callbacks: SpinLock::new(Vec::new()),
|
||||
};
|
||||
|
||||
let mut recv_lock = device.receive_queue.lock();
|
||||
recv_lock
|
||||
.add(&[], &[device.buffer.lock().as_mut()])
|
||||
.unwrap();
|
||||
if recv_lock.should_notify() {
|
||||
recv_lock.notify();
|
||||
}
|
||||
drop(recv_lock);
|
||||
device
|
||||
.transport
|
||||
.register_queue_callback(RECV0_QUEUE_INDEX, Box::new(handle_console_input), false)
|
||||
.unwrap();
|
||||
device
|
||||
.transport
|
||||
.register_cfg_callback(Box::new(config_space_change))
|
||||
.unwrap();
|
||||
device.transport.finish_init();
|
||||
|
||||
jinux_console::register_device(DEVICE_NAME.to_string(), Arc::new(device));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_console_input(_: &TrapFrame) {
|
||||
jinux_console::get_device(&DEVICE_NAME.to_string())
|
||||
.unwrap()
|
||||
.handle_irq();
|
||||
}
|
||||
|
||||
fn config_space_change(_: &TrapFrame) {
|
||||
debug!("Virtio-Console device configuration space change");
|
||||
}
|
4
services/comps/virtio/src/device/console/mod.rs
Normal file
4
services/comps/virtio/src/device/console/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod config;
|
||||
pub mod device;
|
||||
|
||||
pub static DEVICE_NAME: &str = "Virtio-Console";
|
@ -2,6 +2,7 @@ use crate::queue::QueueError;
|
||||
use int_to_c_enum::TryFromInt;
|
||||
|
||||
pub mod block;
|
||||
pub mod console;
|
||||
pub mod input;
|
||||
pub mod network;
|
||||
|
||||
|
@ -12,8 +12,8 @@ use alloc::boxed::Box;
|
||||
use bitflags::bitflags;
|
||||
use component::ComponentInitError;
|
||||
use device::{
|
||||
block::device::BlockDevice, input::device::InputDevice, network::device::NetworkDevice,
|
||||
VirtioDeviceType,
|
||||
block::device::BlockDevice, console::device::ConsoleDevice, input::device::InputDevice,
|
||||
network::device::NetworkDevice, VirtioDeviceType,
|
||||
};
|
||||
use log::{error, warn};
|
||||
use transport::{mmio::VIRTIO_MMIO_DRIVER, pci::VIRTIO_PCI_DRIVER, DeviceStatus};
|
||||
@ -49,6 +49,7 @@ fn virtio_component_init() -> Result<(), ComponentInitError> {
|
||||
VirtioDeviceType::Block => BlockDevice::init(transport),
|
||||
VirtioDeviceType::Input => InputDevice::init(transport),
|
||||
VirtioDeviceType::Network => NetworkDevice::init(transport),
|
||||
VirtioDeviceType::Console => ConsoleDevice::init(transport),
|
||||
_ => {
|
||||
warn!("[Virtio]: Found unimplemented device:{:?}", device_type);
|
||||
Ok(())
|
||||
@ -82,6 +83,7 @@ fn negotiate_features(transport: &mut Box<dyn VirtioTransport>) {
|
||||
VirtioDeviceType::Network => NetworkDevice::negotiate_features(device_specified_features),
|
||||
VirtioDeviceType::Block => BlockDevice::negotiate_features(device_specified_features),
|
||||
VirtioDeviceType::Input => InputDevice::negotiate_features(device_specified_features),
|
||||
VirtioDeviceType::Console => ConsoleDevice::negotiate_features(device_specified_features),
|
||||
_ => device_specified_features,
|
||||
};
|
||||
let mut support_feature = Feature::from_bits_truncate(features);
|
||||
|
Reference in New Issue
Block a user