mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +00:00
Support virtio console device
This commit is contained in:
parent
e9544d489f
commit
01e485b96e
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -628,6 +628,18 @@ dependencies = [
|
||||
"spin 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinux-console"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"component",
|
||||
"jinux-frame",
|
||||
"jinux-util",
|
||||
"log",
|
||||
"spin 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinux-frame"
|
||||
version = "0.1.0"
|
||||
@ -757,6 +769,7 @@ dependencies = [
|
||||
"int-to-c-enum",
|
||||
"intrusive-collections",
|
||||
"jinux-block",
|
||||
"jinux-console",
|
||||
"jinux-frame",
|
||||
"jinux-input",
|
||||
"jinux-network",
|
||||
@ -812,11 +825,13 @@ name = "jinux-virtio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"align_ext",
|
||||
"bit_field",
|
||||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"component",
|
||||
"int-to-c-enum",
|
||||
"jinux-block",
|
||||
"jinux-console",
|
||||
"jinux-frame",
|
||||
"jinux-input",
|
||||
"jinux-network",
|
||||
|
@ -39,6 +39,7 @@ members = [
|
||||
"framework/libs/ktest",
|
||||
"framework/libs/tdx-guest",
|
||||
"services/comps/block",
|
||||
"services/comps/console",
|
||||
"services/comps/framebuffer",
|
||||
"services/comps/input",
|
||||
"services/comps/network",
|
||||
|
@ -4,6 +4,7 @@ std = { name = "jinux-std" }
|
||||
virtio = { name = "jinux-virtio" }
|
||||
input = { name = "jinux-input" }
|
||||
block = { name = "jinux-block" }
|
||||
console = { name = "jinux-console" }
|
||||
time = { name = "jinux-time" }
|
||||
framebuffer = { name = "jinux-framebuffer" }
|
||||
network = { name = "jinux-network" }
|
||||
|
@ -19,6 +19,10 @@ pub const DEVICE_ARGS: &[&str] = &[
|
||||
"virtio-keyboard-device",
|
||||
"-device",
|
||||
"virtio-net-device,netdev=net01",
|
||||
"-device",
|
||||
"virtio-serial-device",
|
||||
"-device",
|
||||
"virtconsole,chardev=mux",
|
||||
];
|
||||
|
||||
pub fn create_bootdev_image(path: PathBuf) -> PathBuf {
|
||||
|
@ -43,6 +43,10 @@ pub const NOIOMMU_DEVICE_ARGS: &[&str] = &[
|
||||
"virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
|
||||
"-device",
|
||||
"virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off",
|
||||
"-device",
|
||||
"virtio-serial-pci,disable-legacy=on,disable-modern=off",
|
||||
"-device",
|
||||
"virtconsole,chardev=mux",
|
||||
];
|
||||
|
||||
pub const IOMMU_DEVICE_ARGS: &[&str] = &[
|
||||
@ -53,6 +57,10 @@ pub const IOMMU_DEVICE_ARGS: &[&str] = &[
|
||||
"-device",
|
||||
"virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
|
||||
"-device",
|
||||
"virtio-serial-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
|
||||
"-device",
|
||||
"virtconsole,chardev=mux",
|
||||
"-device",
|
||||
"intel-iommu,intremap=on,device-iotlb=on",
|
||||
"-device",
|
||||
"ioh3420,id=pcie.0,chassis=1",
|
||||
|
@ -93,10 +93,12 @@ pub const COMMON_ARGS: &[&str] = &[
|
||||
"-m",
|
||||
"2G",
|
||||
"-nographic", // TODO: figure out why grub can't shown up without it
|
||||
"-monitor",
|
||||
"vc",
|
||||
"-serial",
|
||||
"mon:stdio",
|
||||
"chardev:mux",
|
||||
"-monitor",
|
||||
"chardev:mux",
|
||||
"-chardev",
|
||||
"stdio,id=mux,mux=on,signal=off,logfile=qemu.log",
|
||||
"-display",
|
||||
"none",
|
||||
"-device",
|
||||
|
16
services/comps/console/Cargo.toml
Normal file
16
services/comps/console/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "jinux-console"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.3"
|
||||
spin = "0.9.4"
|
||||
jinux-frame = { path = "../../../framework/jinux-frame" }
|
||||
jinux-util = { path = "../../libs/jinux-util" }
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
log = "0.4"
|
||||
|
||||
[features]
|
71
services/comps/console/src/lib.rs
Normal file
71
services/comps/console/src/lib.rs
Normal file
@ -0,0 +1,71 @@
|
||||
//! The console device of jinux
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
#![feature(fn_traits)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{collections::BTreeMap, fmt::Debug, string::String, sync::Arc, vec::Vec};
|
||||
use core::any::Any;
|
||||
|
||||
use component::{init_component, ComponentInitError};
|
||||
use jinux_frame::sync::SpinLock;
|
||||
use spin::Once;
|
||||
|
||||
pub type ConsoleCallback = dyn Fn(&[u8]) + Send + Sync;
|
||||
|
||||
pub trait AnyConsoleDevice: Send + Sync + Any + Debug {
|
||||
fn send(&self, buf: &[u8]);
|
||||
fn recv(&self, buf: &mut [u8]) -> Option<usize>;
|
||||
fn register_callback(&self, callback: &'static ConsoleCallback);
|
||||
fn handle_irq(&self);
|
||||
}
|
||||
|
||||
pub fn register_device(name: String, device: Arc<dyn AnyConsoleDevice>) {
|
||||
COMPONENT
|
||||
.get()
|
||||
.unwrap()
|
||||
.console_table
|
||||
.lock()
|
||||
.insert(name, device);
|
||||
}
|
||||
|
||||
pub fn get_device(str: &str) -> Option<Arc<dyn AnyConsoleDevice>> {
|
||||
COMPONENT
|
||||
.get()
|
||||
.unwrap()
|
||||
.console_table
|
||||
.lock()
|
||||
.get(str)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn all_devices() -> Vec<(String, Arc<dyn AnyConsoleDevice>)> {
|
||||
let consoles = COMPONENT.get().unwrap().console_table.lock();
|
||||
consoles
|
||||
.iter()
|
||||
.map(|(name, device)| (name.clone(), device.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
static COMPONENT: Once<Component> = Once::new();
|
||||
|
||||
#[init_component]
|
||||
fn component_init() -> Result<(), ComponentInitError> {
|
||||
let a = Component::init()?;
|
||||
COMPONENT.call_once(|| a);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Component {
|
||||
console_table: SpinLock<BTreeMap<String, Arc<dyn AnyConsoleDevice>>>,
|
||||
}
|
||||
|
||||
impl Component {
|
||||
pub fn init() -> Result<Self, ComponentInitError> {
|
||||
Ok(Self {
|
||||
console_table: SpinLock::new(BTreeMap::new()),
|
||||
})
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -12,6 +12,7 @@ pod = { git = "https://github.com/jinzhao-dev/pod", rev = "d7dba56" }
|
||||
jinux-input = { path = "../../comps/input" }
|
||||
jinux-block = { path = "../../comps/block" }
|
||||
jinux-network = { path = "../../comps/network" }
|
||||
jinux-console = { path = "../../comps/console" }
|
||||
jinux-time = { path = "../../comps/time" }
|
||||
jinux-virtio = { path = "../../comps/virtio" }
|
||||
jinux-rights = { path = "../jinux-rights" }
|
||||
|
@ -9,7 +9,9 @@ use crate::{
|
||||
pub static TTY_DRIVER: Once<Arc<TtyDriver>> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
register_console_input_callback(&serial_input_callback);
|
||||
for (_, device) in jinux_console::all_devices() {
|
||||
device.register_callback(&console_input_callback)
|
||||
}
|
||||
let tty_driver = Arc::new(TtyDriver::new());
|
||||
// FIXME: install n_tty into tty_driver?
|
||||
let n_tty = get_n_tty();
|
||||
@ -66,6 +68,13 @@ impl TtyDriver {
|
||||
}
|
||||
}
|
||||
|
||||
fn console_input_callback(items: &[u8]) {
|
||||
let tty_driver = get_tty_driver();
|
||||
for item in items {
|
||||
tty_driver.receive_char(*item);
|
||||
}
|
||||
}
|
||||
|
||||
fn serial_input_callback(item: u8) {
|
||||
let tty_driver = get_tty_driver();
|
||||
tty_driver.receive_char(item);
|
||||
|
Loading…
x
Reference in New Issue
Block a user