Component initialization mechanism complete

This commit is contained in:
Yuke Peng 2023-02-27 16:18:04 +08:00 committed by Tate, Hongliang Tian
parent 4f3e359892
commit e4323f808c
56 changed files with 1473 additions and 416 deletions

View File

@ -7,6 +7,7 @@ edition = "2021"
bootloader = {version="0.10.12"}
jinux-frame = {path = "framework/jinux-frame"}
jinux-std = {path = "services/libs/jinux-std"}
component = {path = "services/comp-sys/component"}
[workspace]
@ -15,8 +16,10 @@ members = [
"framework/jinux-frame",
"framework/pod",
"framework/pod-derive",
"services/comps/jinux-pci",
"services/comps/jinux-virtio",
"services/comps/pci",
"services/comps/virtio",
"services/comps/input",
"services/comps/block",
"services/libs/jinux-std",
"services/libs/jinux-rights-proc",
"services/libs/typeflags",

View File

@ -3,6 +3,8 @@
std = { name = "jinux-std" }
pci = { name = "jinux-pci" }
virtio = { name = "jinux-virtio"}
input = { name = "jinux-input"}
block = { name = "jinux-block"}
main = { name = "jinux" }
[whitelist]

View File

@ -100,7 +100,24 @@ pub fn init(boot_info: &'static mut BootInfo) {
}
let value = x86_64_util::cpuid(1);
}
invoke_c_init_funcs();
}
fn invoke_c_init_funcs() {
extern "C" {
fn sinit_array();
fn einit_array();
}
let call_len = (einit_array as u64 - sinit_array as u64) / 8;
for i in 0..call_len {
unsafe {
let address = (sinit_array as u64 + 8 * i) as *const u64;
let function = address as *const fn();
(*function)();
}
}
}
fn general_handler(trap_frame: &TrapFrame) {
// info!("general handler");
// println!("{:#x?}", trap_frame);

View File

@ -9,6 +9,17 @@ SECTIONS {
*(.rodata .rodata.*)
}
.got ALIGN(4K):{
*(.got .got.*)
}
. = ALIGN(4K);
sinit_array = .;
.init_array : {
*(.init_array .init_array.*)
}
einit_array = .;
.text ALIGN(4K): {
*(.text .text.*)
}

View File

@ -46,7 +46,9 @@ fn main() -> anyhow::Result<()> {
println!("Created disk image at `{}`", bios.display());
return Ok(());
}
#[cfg(windows)]
let mut run_cmd = Command::new("qemu-system-x86_64.exe");
#[cfg(not(windows))]
let mut run_cmd = Command::new("qemu-system-x86_64");
run_cmd
.arg("-drive")
@ -80,6 +82,9 @@ fn main() -> anyhow::Result<()> {
fn create_fs_image(path: &Path) -> anyhow::Result<String> {
let mut fs_img_path = path.parent().unwrap().to_str().unwrap().to_string();
#[cfg(windows)]
fs_img_path.push_str("\\fs.img");
#[cfg(not(windows))]
fs_img_path.push_str("/fs.img");
let path = Path::new(fs_img_path.as_str());
if path.exists() {

View File

@ -0,0 +1,19 @@
[package]
name = "component-macro"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "1.0.104"
itertools = "0.10.5"
toml = "0.7.2"
[dependencies.json]
version = "0.12.4"

View File

@ -0,0 +1,33 @@
use proc_macro2::{TokenStream, TokenTree};
use quote::{ToTokens, TokenStreamExt};
use syn::parse::Parse;
/// The content inside typeflag macro
pub struct ComponentInitFunction {
function: Vec<TokenTree>,
pub function_name: TokenTree,
}
impl Parse for ComponentInitFunction {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut vec: Vec<TokenTree> = Vec::new();
vec.push(input.parse().unwrap());
let function_name: TokenTree = input.parse().unwrap();
vec.push(function_name.clone());
while !input.is_empty() {
vec.push(input.parse().unwrap())
}
Ok(ComponentInitFunction {
function: vec,
function_name,
})
}
}
impl ToTokens for ComponentInitFunction {
fn to_tokens(&self, tokens: &mut TokenStream) {
for token in &self.function {
tokens.append(token.clone());
}
}
}

View File

@ -0,0 +1,83 @@
//This crate defines the component system related macros.
//!
#![feature(proc_macro_diagnostic)]
#![allow(dead_code)]
#![forbid(unsafe_code)]
mod init_comp;
mod priority;
use init_comp::ComponentInitFunction;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
pub(crate) const COMPONENT_FILE_NAME: &str = "Components.toml";
/// Register a function to be called when the component system is initialized. The function should not public.
///
/// Example:
/// ```rust
/// #[init_component]
/// fn init() -> Result<(), component::ComponentInitError> {
/// Ok(())
/// }
///
/// ```
///
/// It will expand to
/// ```rust
/// fn init() -> Result<(), component::ComponentInitError> {
/// Ok(())
/// }
///
/// const fn file() -> &'static str{
/// file!()
/// }
///
/// component::submit!(component::ComponentRegistry::new(&init,file()));
/// ```
/// The priority will calculate automatically
///
#[proc_macro_attribute]
pub fn init_component(_: TokenStream, input: TokenStream) -> proc_macro::TokenStream {
let function = parse_macro_input!(input as ComponentInitFunction);
let function_name = &function.function_name;
quote! {
#function
const fn file() -> &'static str{
file!()
}
component::submit!(component::ComponentRegistry::new(&#function_name,file()));
}
.into()
}
/// Automatically generate all component information required by the component system.
///
/// It is often used with `component::init`.
///
/// Example:
///
/// ```rust
/// component::init(component::component_generate!());
/// ```
///
#[proc_macro]
pub fn generate_information(_: TokenStream) -> proc_macro::TokenStream {
let out = priority::component_generate();
let path = priority::get_component_toml_path();
quote! {
{
include_str!(#path);
extern crate alloc;
alloc::vec![
#(component::ComponentInfo::new #out),*
]
}
}
.into()
}

View File

@ -0,0 +1,215 @@
use std::{collections::HashMap, fs::File, io::Read, ops::Add, process::Command, str::FromStr};
use json::JsonValue;
use proc_macro2::{Group, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use crate::COMPONENT_FILE_NAME;
#[derive(Debug)]
pub struct ComponentInfo {
name: String,
path: String,
priority: u16,
}
impl ToTokens for ComponentInfo {
fn to_tokens(&self, tokens: &mut TokenStream) {
let token = TokenStream::from_str(
format!("\"{}\",\"{}\",{}", self.name, self.path, self.priority).as_str(),
)
.unwrap();
tokens.append(Group::new(proc_macro2::Delimiter::Parenthesis, token));
}
}
/// Automatic generate all the component information
pub fn component_generate() -> Vec<ComponentInfo> {
// extract components information
let mut metadata = metadata();
let mut component_packages = vec![];
let workspace_root = metadata["workspace_root"].as_str().unwrap();
let workspace_root = String::from_str(workspace_root).unwrap().replace("\\", "/");
let comps_name = get_components_name(&workspace_root, &metadata["packages"]);
for package in metadata["packages"].members_mut() {
let name = package["name"].as_str().unwrap();
if comps_name.contains(&name.to_string()) {
// remove useless depend
let mut depends = JsonValue::Array(Vec::new());
loop {
let depend = package["dependencies"].pop();
if depend == JsonValue::Null {
break;
}
if comps_name.contains(&depend["name"].as_str().unwrap().to_string()) {
depends.push(depend).unwrap();
}
}
package["dependencies"] = depends;
component_packages.push(package);
}
}
// calculate priority
let mut mapping: HashMap<String, u16> = HashMap::new();
let mut component_packages_map: HashMap<String, &mut JsonValue> = HashMap::new();
for i in component_packages.iter_mut() {
component_packages_map.insert(i["name"].as_str().unwrap().to_string(), i);
}
for (name, package) in component_packages_map.iter() {
if mapping.contains_key(package["name"].as_str().unwrap()) {
continue;
}
calculate_priority(&mut mapping, &component_packages_map, name.clone());
}
drop(component_packages_map);
// priority calculation complete
let mut components_info = Vec::new();
for package in component_packages {
let temp_id = package["id"].as_str().unwrap();
// extract path, let's take `(path+file:///path/to/comps/pci)` as an example
let path = {
// use the last element, `pci)`
let mut paths: Vec<&str> = temp_id.split(&workspace_root).collect();
// remove the last character
let mut path1 = paths.pop().unwrap().to_string();
path1.pop();
if path1.starts_with("/") {
path1.remove(0);
}
path1
};
let component_info = ComponentInfo {
name: package["name"].as_str().unwrap().to_string(),
path: path.to_owned(),
priority: *mapping
.get(&package["name"].as_str().unwrap().to_string())
.unwrap(),
};
components_info.push(component_info)
}
components_info
}
/// Get the path to the Components.toml file
pub fn get_component_toml_path() -> TokenStream {
let metadata = metadata();
let workspace_root = metadata["workspace_root"].as_str().unwrap();
let mut workspace_root = String::from_str(workspace_root)
.unwrap()
.replace("\\", "/")
.add("/")
.add(COMPONENT_FILE_NAME)
.add("\"");
workspace_root.insert(0, '\"');
TokenStream::from_str(workspace_root.as_str()).unwrap()
}
fn is_component(package: &JsonValue) -> bool {
for depend in package["dependencies"].members() {
if depend["name"].as_str().unwrap() == "component" {
return true;
}
}
false
}
/// Get all the components name, this function will also check if the Components.toml contain all the components.
fn get_components_name(workspace_root: &String, packages: &JsonValue) -> Vec<String> {
let file_components_name = read_component_file(workspace_root);
let mut comps_name = Vec::new();
for package in packages.members() {
if is_component(package) {
if !file_components_name.contains(&package["name"].as_str().unwrap().to_string()) {
// if the package is in the workspace_root
if package["id"].as_str().unwrap().contains(workspace_root) {
panic!(
"Package {} in the workspace that not written in the {} file",
package["name"].as_str().unwrap(),
COMPONENT_FILE_NAME
);
}
}
comps_name.push(package["name"].as_str().unwrap().to_string());
}
}
comps_name
}
/// read component file, return all the components name
fn read_component_file(workspace_root: &String) -> Vec<String> {
let component_toml: toml::Value = {
let mut component_file_path = workspace_root.clone();
component_file_path.push_str("/");
component_file_path.push_str(COMPONENT_FILE_NAME);
let mut file = File::open(component_file_path)
.expect("Components.toml file not found, please check if the file exists");
let mut str_val = String::new();
file.read_to_string(&mut str_val).unwrap();
toml::from_str(&str_val).unwrap()
};
for (name, value) in component_toml.as_table().unwrap() {
if name.as_str() == "components" {
return value
.as_table()
.unwrap()
.values()
.map(|value| {
value
.as_table()
.unwrap()
.values()
.map(|str_val| str_val.as_str().unwrap().to_string())
.collect()
})
.collect();
}
}
panic!("Componets.toml file not valid")
}
/// calculate the priority of one node
fn calculate_priority(
prioritys: &mut HashMap<String, u16>,
package_mapping: &HashMap<String, &mut JsonValue>,
node_name: String,
) -> u16 {
if prioritys.contains_key(&node_name) {
return prioritys.get(&node_name).unwrap().clone();
}
let package = &package_mapping[&node_name];
let mut lowest_priority: u16 = 0;
for depends in package["dependencies"].members() {
lowest_priority = lowest_priority.max(
calculate_priority(
prioritys,
package_mapping,
depends["name"].as_str().unwrap().to_string(),
) + 1,
);
}
prioritys.insert(node_name.to_string(), lowest_priority);
lowest_priority
}
fn metadata() -> json::JsonValue {
let mut cmd = Command::new(env!("CARGO"));
cmd.arg("metadata");
cmd.arg("--format-version").arg("1");
let output = cmd.output().unwrap();
if !output.status.success() {
panic!("cannot get metadata");
}
let output = String::from_utf8(output.stdout).unwrap();
let parsed = json::parse(&output).unwrap();
parsed
}

View File

@ -0,0 +1,14 @@
[package]
name = "component"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
inventory = {git="https://github.com/sdww0/inventory"}
log = "0.4"
component-macro = {path = "../component-macro"}
[build-dependencies]
json= "0.12.4"

View File

@ -0,0 +1,57 @@
# Component
## Overview
This crate is used for the initialization of the component system, which provides a priority initialization scheme based on the inventory crate.
## Usage
### Register component
Registering a crate as component by marking a function in the lib.rs with `#[init_component]` macro. The specific definition of the function can refer to the comments in the macro.
### Component initialization
Component system need to be initialized by calling `componet::init` function and it needs information about all components. Usually it is used with the `component::generate_information` macro.
## Example
```rust
// comp1/lib.rs
use std::sync::atomic::AtomicU16;
use std::sync::atomic::Ordering::Relaxed;
use component::init_component;
pub static INIT_COUNT : AtomicU16 = AtomicU16::new(0);
#[init_component]
fn comp1_init() -> Result<(), component::ComponentInitError> {
assert_eq!(INIT_COUNT.load(Relaxed),0);
INIT_COUNT.fetch_add(1,Relaxed);
Ok(())
}
// src/main.rs
use std::sync::atomic::Ordering::Relaxed;
use component::init_component;
use comp1::INIT_COUNT;
#[init_component]
fn init() -> Result<(), component::ComponentInitError> {
assert_eq!(INIT_COUNT.load(Relaxed),1);
INIT_COUNT.fetch_add(1,Relaxed);
Ok(())
}
fn main(){
component::init(component::generate_information!()).unwrap();
assert_eq!(INIT_COUNT.load(Relaxed),2);
}
```
## Notes
- Currently, initialization requires the presence of a `Components.toml` file, which stores some information about components and access control. The [tests](tests/kernel/Components.toml) provides a sample file of it. If the components declared inside `Components.toml` is inconsistent with the component found by `generate_information` macro (i.e. A crate depends on the component library but is not declared in `Components.toml`), then a compilation error will occur.
- The `generate_information` macro will generate the information of all components. But ultimately which functions are called still depends on which `#[init_component]` macros are extended. If you want to test a component. Then, other components with a lower priority than it or other unused high-priority components will not be initialized at runtime.

View File

@ -0,0 +1,178 @@
//! Component system
//!
#![no_std]
#![forbid(unsafe_code)]
#![feature(fn_traits)]
#![feature(once_cell)]
extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use alloc::{borrow::ToOwned, fmt::Debug};
use log::{debug, error, info};
pub use component_macro::*;
pub use inventory::submit;
#[derive(Debug)]
pub enum ComponentInitError {
UninitializedDependencies(String),
Unknown,
}
pub struct ComponentRegistry {
function: &'static (dyn Fn() -> Result<(), ComponentInitError> + Sync),
path: &'static str,
}
impl ComponentRegistry {
pub const fn new(
function: &'static (dyn Fn() -> Result<(), ComponentInitError> + Sync),
path: &'static str,
) -> Self {
Self { function, path }
}
}
inventory::collect!(ComponentRegistry);
impl Debug for ComponentRegistry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ComponentRegistry")
.field("path", &self.path)
.finish()
}
}
pub struct ComponentInfo {
name: String,
path: String,
priority: u32,
function: Option<&'static (dyn Fn() -> Result<(), ComponentInitError> + Sync)>,
}
impl ComponentInfo {
pub fn new(name: &str, path: &str, priority: u32) -> Self {
Self {
name: name.to_string(),
path: path.to_string(),
priority,
function: None,
}
}
}
impl PartialEq for ComponentInfo {
fn eq(&self, other: &Self) -> bool {
self.priority == other.priority
}
}
impl PartialOrd for ComponentInfo {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.priority.partial_cmp(&other.priority)
}
}
impl Eq for ComponentInfo {}
impl Ord for ComponentInfo {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.priority.cmp(&other.priority)
}
}
impl Debug for ComponentInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ComponentInfo")
.field("name", &self.name)
.field("path", &self.path)
.field("priority", &self.priority)
.finish()
}
}
#[derive(Debug)]
pub enum ComponentSystemInitError {
FileNotValid,
NotIncludeAllComponent(String),
}
/// Component system initialization. It will collect invoke all functions that are marked by init_component based on dependencies between crates.
///
/// The collection of ComponentInfo usually generate by `component_generate` macro.
///
/// ```rust
/// component::init(component::component_generate!());
/// ```
///
pub fn init(components: Vec<ComponentInfo>) -> Result<(), ComponentSystemInitError> {
let components_info = parse_input(components);
match_and_call(components_info)?;
Ok(())
}
fn parse_input(components: Vec<ComponentInfo>) -> BTreeMap<String, ComponentInfo> {
debug!("All component:{components:?}");
let mut out = BTreeMap::new();
for component in components {
out.insert(component.path.clone(), component);
}
out
}
/// Match the ComponetInfo with ComponentRegistry. The key is the relative path of one component
fn match_and_call(
mut components: BTreeMap<String, ComponentInfo>,
) -> Result<(), ComponentSystemInitError> {
let mut infos = Vec::new();
for registry in inventory::iter::<ComponentRegistry> {
// relative/path/to/comps/pci/src/lib.rs
let mut str: String = registry.path.to_owned();
str = str.replace("\\", "/");
// relative/path/to/comps/pci
// There are two cases, one in the test folder and one in the src folder.
// There may be multiple directories within the folder.
// There we assume it will not have such directories: 'comp1/src/comp2/src/lib.rs' so that we can split by tests or src string
if str.contains("src/") {
str = str
.trim_end_matches(str.get(str.find("src/").unwrap()..str.len()).unwrap())
.to_string();
} else if str.contains("tests/") {
str = str
.trim_end_matches(str.get(str.find("tests/").unwrap()..str.len()).unwrap())
.to_string();
} else {
panic!("The path of {} cannot recognized by component system", str);
}
str = str.trim_end_matches("/").to_owned();
let mut info = components
.remove(&str)
.ok_or(ComponentSystemInitError::NotIncludeAllComponent(str))?;
info.function.replace(registry.function);
infos.push(info);
}
debug!("Remain componets:{components:?}");
if components.len() != 0 {
info!("Exists components that are not initialized");
}
infos.sort();
info!("Components initializing...");
for i in infos {
info!("Component initializing:{:?}", i);
if let Err(res) = i.function.unwrap().call(()) {
error!("Component initalize error:{:?}", res);
} else {
info!("Component initalize complete");
}
}
info!("All components initalization completed");
Ok(())
}

View File

@ -0,0 +1,17 @@
[package]
name = "kernel"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
component = {path="../../../component"}
foo = {path="foo"}
bar = {path="bar"}
simple_logger = "4.0.0"
log = "0.4"
[workspace]
foo = {path="foo"}
bar = {path="bar"}

View File

@ -0,0 +1,5 @@
# template
[components]
kernel = {name = "kernel"}
foo = {name = "foo"}
bar = {name = "bar"}

View File

@ -0,0 +1,10 @@
[package]
name = "bar"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
component = {path="../../../../component"}

View File

@ -0,0 +1,13 @@
use std::sync::atomic::AtomicU16;
use std::sync::atomic::Ordering::Relaxed;
use component::init_component;
pub static INIT_COUNT: AtomicU16 = AtomicU16::new(0);
#[init_component]
fn bar_init() -> Result<(), component::ComponentInitError> {
assert_eq!(INIT_COUNT.load(Relaxed), 0);
INIT_COUNT.fetch_add(1, Relaxed);
Ok(())
}

View File

@ -0,0 +1,15 @@
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
component = {path="../../../../component"}
bar = {path = "../bar"}
log = "0.4"
[dev-dependencies]
simple_logger = "4.0.0"

View File

@ -0,0 +1,15 @@
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Once;
use bar::INIT_COUNT;
use component::init_component;
pub static FOO_VALUE: Once = Once::new();
#[init_component]
fn foo_init() -> Result<(), component::ComponentInitError> {
assert_eq!(INIT_COUNT.load(Relaxed), 1);
INIT_COUNT.fetch_add(1, Relaxed);
FOO_VALUE.call_once(|| {});
Ok(())
}

View File

@ -0,0 +1,9 @@
use bar::INIT_COUNT;
use std::sync::atomic::Ordering::Relaxed;
#[test]
fn test() {
simple_logger::init_with_level(log::Level::Debug).unwrap();
component::init(component::generate_information!()).unwrap();
assert_eq!(INIT_COUNT.load(Relaxed), 1);
}

View File

@ -0,0 +1,19 @@
use std::sync::atomic::Ordering::Relaxed;
use bar::INIT_COUNT;
use component::init_component;
use foo::FOO_VALUE;
#[init_component]
fn kernel_init() -> Result<(), component::ComponentInitError> {
assert_eq!(INIT_COUNT.load(Relaxed), 2);
assert!(FOO_VALUE.is_completed());
INIT_COUNT.fetch_add(1, Relaxed);
Ok(())
}
fn main() {
simple_logger::init_with_level(log::Level::Info).unwrap();
component::init(component::generate_information!()).unwrap();
assert_eq!(INIT_COUNT.load(Relaxed), 3);
}

View File

@ -0,0 +1,17 @@
use bar::INIT_COUNT;
use component::init_component;
use std::sync::atomic::Ordering::Relaxed;
#[init_component]
fn kernel_init() -> Result<(), component::ComponentInitError> {
assert_eq!(INIT_COUNT.load(Relaxed), 1);
INIT_COUNT.fetch_add(1, Relaxed);
Ok(())
}
#[test]
fn test() {
simple_logger::init_with_level(log::Level::Debug).unwrap();
component::init(component::generate_information!()).unwrap();
assert_eq!(INIT_COUNT.load(Relaxed), 2);
}

View File

@ -0,0 +1,24 @@
[package]
name = "jinux-block"
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-pci = {path="../pci"}
jinux-virtio = {path="../virtio"}
jinux-util = {path="../../libs/jinux-util"}
pod = {path = "../../../framework/pod"}
pod-derive = {path = "../../../framework/pod-derive"}
component = {path="../../comp-sys/component"}
log = "0.4"
[features]
[dependencies.lazy_static]
version = "1.0"
features = ["spin_no_std"]

View File

@ -0,0 +1,71 @@
//! The block device of jinux
#![no_std]
#![forbid(unsafe_code)]
#![feature(fn_traits)]
mod virtio;
extern crate alloc;
use core::any::Any;
use alloc::string::ToString;
use alloc::sync::Arc;
use component::init_component;
use component::ComponentInitError;
use jinux_virtio::VirtioDeviceType;
use spin::Once;
use virtio::VirtioBlockDevice;
pub const BLK_SIZE: usize = 512;
pub trait BlockDevice: Send + Sync + Any {
fn init(&self) {}
fn read_block(&self, block_id: usize, buf: &mut [u8]);
fn write_block(&self, block_id: usize, buf: &[u8]);
fn handle_irq(&self);
}
pub static BLK_COMPONENT: Once<BLKComponent> = Once::new();
#[init_component]
fn blk_component_init() -> Result<(), ComponentInitError> {
let a = BLKComponent::init()?;
BLK_COMPONENT.call_once(|| a);
Ok(())
}
pub struct BLKComponent {
/// Input device map, key is the irq number, value is the Input device
blk_device: Arc<dyn BlockDevice>,
}
impl BLKComponent {
pub fn init() -> Result<Self, ComponentInitError> {
let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap();
let devices = virtio.get_device(VirtioDeviceType::Block);
for device in devices {
let v_device = VirtioBlockDevice::new(device);
return Ok(Self {
blk_device: Arc::new(v_device),
});
}
Err(ComponentInitError::UninitializedDependencies(
"Virtio".to_string(),
))
}
pub const fn name() -> &'static str {
"Block device"
}
// 0~65535
pub const fn priority() -> u16 {
8192
}
}
impl BLKComponent {
pub fn get_device(self: &Self) -> Arc<dyn BlockDevice> {
self.blk_device.clone()
}
}

View File

@ -0,0 +1,54 @@
//! Block device based on Virtio
use jinux_frame::TrapFrame;
use jinux_pci::msix::MSIX;
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::{device::block::device::BLKDevice, PCIVirtioDevice, VitrioPciCommonCfg};
use log::debug;
use spin::Mutex;
use crate::{BlockDevice, BLK_COMPONENT};
pub struct VirtioBlockDevice {
blk_device: Mutex<BLKDevice>,
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
msix: MSIX,
}
impl BlockDevice for VirtioBlockDevice {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
self.blk_device.lock().read_block(block_id, buf);
}
/// it is blocking now
fn write_block(&self, block_id: usize, buf: &[u8]) {
self.blk_device.lock().write_block(block_id, buf);
}
fn handle_irq(&self) {
debug!("block device handle irq");
}
}
impl VirtioBlockDevice {
pub(crate) fn new(mut virtio_device: PCIVirtioDevice) -> Self {
fn handle_block_device(_: &TrapFrame) {
BLK_COMPONENT.get().unwrap().blk_device.handle_irq()
}
fn config_space_change(_: &TrapFrame) {
debug!("block device config space change");
}
virtio_device.register_interrupt_functions(&config_space_change, &handle_block_device);
let blk_device = Mutex::new(match virtio_device.device {
jinux_virtio::device::VirtioDevice::Block(blk) => blk,
_ => {
panic!("Error when creating new block device, the input device is other type of virtio device");
}
});
Self {
blk_device,
common_cfg: virtio_device.common_cfg,
msix: virtio_device.msix,
}
}
}

View File

@ -0,0 +1,25 @@
[package]
name = "jinux-input"
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-pci = {path="../pci"}
jinux-virtio = {path="../virtio"}
jinux-util = {path="../../libs/jinux-util"}
pod = {path = "../../../framework/pod"}
pod-derive = {path = "../../../framework/pod-derive"}
component = {path="../../comp-sys/component"}
virtio-input-decoder = "0.1.4"
log = "0.4"
[features]
[dependencies.lazy_static]
version = "1.0"
features = ["spin_no_std"]

View File

@ -0,0 +1,90 @@
//! The input device of jinux
#![no_std]
#![forbid(unsafe_code)]
#![feature(fn_traits)]
mod virtio;
extern crate alloc;
use core::any::Any;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use component::init_component;
use component::ComponentInitError;
use jinux_virtio::VirtioDeviceType;
use spin::{Mutex, Once};
use virtio::VirtioInputDevice;
use virtio_input_decoder::DecodeType;
pub trait INPUTDevice: Send + Sync + Any {
fn handle_irq(&self) -> Option<()>;
fn register_callbacks(&self, function: &'static (dyn Fn(DecodeType) + Send + Sync));
fn name(&self) -> &String;
}
pub static INPUT_COMPONENT: Once<INPUTComponent> = Once::new();
#[init_component]
fn input_component_init() -> Result<(), ComponentInitError> {
let a = INPUTComponent::init()?;
INPUT_COMPONENT.call_once(|| a);
Ok(())
}
pub struct INPUTComponent {
/// Input device map, key is the irq number, value is the Input device
input_device_map: Mutex<BTreeMap<u8, Arc<dyn INPUTDevice>>>,
}
impl INPUTComponent {
pub fn init() -> Result<Self, ComponentInitError> {
let mut input_device_map: BTreeMap<u8, Arc<dyn INPUTDevice>> = BTreeMap::new();
let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap();
let devices = virtio.get_device(VirtioDeviceType::Input);
for device in devices {
let (v_device, irq_num) = VirtioInputDevice::new(device);
input_device_map.insert(irq_num, Arc::new(v_device));
}
Ok(Self {
input_device_map: Mutex::new(input_device_map),
})
}
pub const fn name() -> &'static str {
"Input Device"
}
// 0~65535
pub const fn priority() -> u16 {
8192
}
}
impl INPUTComponent {
fn call(self: &Self, irq_number: u8) -> Result<(), InputDeviceHandleError> {
// FIXME: use Result instead
let binding = self.input_device_map.lock();
let device = binding
.get(&irq_number)
.ok_or(InputDeviceHandleError::DeviceNotExists)?;
device.handle_irq();
Ok(())
}
pub fn get_input_device(self: &Self) -> Vec<Arc<dyn INPUTDevice>> {
self.input_device_map
.lock()
.iter()
.map(|(_, device)| device.clone())
.collect::<Vec<Arc<dyn INPUTDevice>>>()
}
}
#[derive(Debug)]
enum InputDeviceHandleError {
DeviceNotExists,
Unknown,
}

View File

@ -0,0 +1,122 @@
//! Input device based on Virtio
use alloc::{string::String, sync::Arc, vec::Vec};
use jinux_frame::{offset_of, TrapFrame};
use jinux_pci::msix::MSIX;
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::device::input::device::InputProp;
use jinux_virtio::VitrioPciCommonCfg;
use jinux_virtio::{
device::input::{device::InputDevice, InputConfigSelect},
PCIVirtioDevice,
};
use log::{debug, info};
use spin::Mutex;
use virtio_input_decoder::{DecodeType, Decoder};
use crate::INPUTDevice;
pub struct VirtioInputDevice {
input_device: InputDevice,
common_cfg: InFramePtr<VitrioPciCommonCfg>,
msix: Mutex<MSIX>,
name: String,
callbacks: Mutex<Vec<Arc<dyn Fn(DecodeType) + Send + Sync + 'static>>>,
}
impl VirtioInputDevice {
/// Create a new Virtio Input Device, return value contains the irq number it will use
pub(crate) fn new(virtio_device: PCIVirtioDevice) -> (Self, u8) {
let input_device = match virtio_device.device {
jinux_virtio::device::VirtioDevice::Input(dev) => dev,
_ => {
panic!("Error when creating new input device, the input device is other type of virtio device");
}
};
let mut raw_name: [u8; 128] = [0; 128];
input_device.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name);
let name = String::from_utf8(raw_name.to_vec()).unwrap();
info!("input device name:{}", name);
let mut prop: [u8; 128] = [0; 128];
input_device.query_config_select(InputConfigSelect::PropBits, 0, &mut prop);
let input_prop = InputProp::from_bits(prop[0]).unwrap();
debug!("input device prop:{:?}", input_prop);
fn handle_input(frame: &TrapFrame) {
debug!("in handle input");
let input_component = crate::INPUT_COMPONENT.get().unwrap();
input_component.call(frame.id as u8);
}
fn config_space_change(_: &TrapFrame) {
debug!("input device config space change");
}
let common_cfg = virtio_device.common_cfg;
let mut msix = virtio_device.msix;
let config_msix_vector =
common_cfg.read_at(offset_of!(VitrioPciCommonCfg, config_msix_vector)) as usize;
let mut event_irq_number = 0;
for i in 0..msix.table_size as usize {
let msix = msix.table.get_mut(i).unwrap();
if !msix.irq_handle.is_empty() {
panic!("msix already have irq functions");
}
if config_msix_vector == i {
msix.irq_handle.on_active(config_space_change);
} else {
event_irq_number = msix.irq_handle.num();
msix.irq_handle.on_active(handle_input);
}
}
(
Self {
input_device,
common_cfg,
msix: Mutex::new(msix),
name,
callbacks: Mutex::new(Vec::new()),
},
event_irq_number,
)
}
}
impl INPUTDevice for VirtioInputDevice {
fn handle_irq(&self) -> Option<()> {
let input = &self.input_device;
// one interrupt may contains serval input, so it should loop
loop {
let event = input.pop_pending_event()?;
let dtype = match Decoder::decode(
event.event_type as usize,
event.code as usize,
event.value as usize,
) {
Ok(dtype) => dtype,
Err(_) => return Some(()),
};
let lock = self.callbacks.lock();
for callback in lock.iter() {
callback.call((dtype,));
}
match dtype {
virtio_input_decoder::DecodeType::Key(key, r#type) => {
info!("{:?} {:?}", key, r#type);
}
virtio_input_decoder::DecodeType::Mouse(mouse) => info!("{:?}", mouse),
}
}
}
fn register_callbacks(&self, function: &'static (dyn Fn(DecodeType) + Send + Sync)) {
self.callbacks.lock().push(Arc::new(function))
}
fn name(&self) -> &String {
&self.name
}
}

View File

@ -1,64 +0,0 @@
//! The pci of jinux
#![no_std]
#![forbid(unsafe_code)]
#![allow(dead_code)]
pub mod capability;
pub mod msix;
pub mod util;
extern crate alloc;
extern crate pod_derive;
use alloc::{sync::Arc, vec::Vec};
use lazy_static::lazy_static;
use log::info;
use spin::mutex::Mutex;
use util::CSpaceAccessMethod;
pub use crate::util::PCIDevice;
pub const PCI_COMMAND: u16 = 0x04;
pub const PCI_BAR: u16 = 0x10;
pub const PCI_CAP_PTR: u16 = 0x34;
pub const PCI_INTERRUPT_LINE: u16 = 0x3c;
pub const PCI_INTERRUPT_PIN: u16 = 0x3d;
pub const PCI_MSIX_CTRL_CAP: u16 = 0x00;
pub const PCI_MSIX_TABLE: u16 = 0x04;
pub const PCI_MSIX_PBA: u16 = 0x08;
pub const PCI_CAP_ID_MSI: u8 = 0x05;
lazy_static! {
static ref PCI_DEVICES: Mutex<Vec<Arc<PCIDevice>>> = Mutex::new(Vec::new());
}
pub fn init() {
if device_amount() > 0 {
panic!("initialize pci device twice time")
}
let mut devices = PCI_DEVICES.lock();
for dev in util::scan_bus(CSpaceAccessMethod::IO) {
info!(
"pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) command: {:?} status: {:?} irq: {}:{:?}",
dev.loc.bus,
dev.loc.device,
dev.loc.function,
dev.id.vendor_id,
dev.id.device_id,
dev.id.class,
dev.id.subclass,
dev.command,
dev.status,
dev.pic_interrupt_line,
dev.interrupt_pin
);
devices.push(Arc::new(dev));
}
}
pub fn get_pci_devices(index: usize) -> Option<Arc<PCIDevice>> {
PCI_DEVICES.lock().get(index).cloned()
}
pub fn device_amount() -> usize {
PCI_DEVICES.lock().len()
}

View File

@ -12,7 +12,8 @@ jinux-frame = {path = "../../../framework/jinux-frame"}
jinux-util = {path="../../libs/jinux-util"}
pod = {path = "../../../framework/pod"}
pod-derive = {path = "../../../framework/pod-derive"}
log= "0.4"
component = {path="../../comp-sys/component"}
log = "0.4"
[dependencies.lazy_static]
version = "1.0"

View File

@ -0,0 +1,86 @@
//! The pci of jinux
#![no_std]
#![forbid(unsafe_code)]
#![allow(dead_code)]
pub mod capability;
pub mod msix;
pub mod util;
extern crate alloc;
use component::init_component;
use component::ComponentInitError;
extern crate pod_derive;
use alloc::{sync::Arc, vec::Vec};
use spin::{mutex::Mutex, Once};
use util::CSpaceAccessMethod;
pub use crate::util::PCIDevice;
pub const PCI_COMMAND: u16 = 0x04;
pub const PCI_BAR: u16 = 0x10;
pub const PCI_CAP_PTR: u16 = 0x34;
pub const PCI_INTERRUPT_LINE: u16 = 0x3c;
pub const PCI_INTERRUPT_PIN: u16 = 0x3d;
pub const PCI_MSIX_CTRL_CAP: u16 = 0x00;
pub const PCI_MSIX_TABLE: u16 = 0x04;
pub const PCI_MSIX_PBA: u16 = 0x08;
pub const PCI_CAP_ID_MSI: u8 = 0x05;
pub static PCI_COMPONENT: Once<PCIComponent> = Once::new();
#[init_component]
fn pci_component_init() -> Result<(), ComponentInitError> {
let a = PCIComponent::init()?;
PCI_COMPONENT.call_once(|| a);
Ok(())
}
pub struct PCIComponent {
pci_device: Mutex<Vec<Arc<PCIDevice>>>,
}
impl PCIComponent {
pub fn init() -> Result<Self, ComponentInitError> {
let mut devices = Vec::new();
for dev in util::scan_bus(CSpaceAccessMethod::IO) {
log::info!(
"pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) command: {:?} status: {:?} irq: {}:{:?}",
dev.loc.bus,
dev.loc.device,
dev.loc.function,
dev.id.vendor_id,
dev.id.device_id,
dev.id.class,
dev.id.subclass,
dev.command,
dev.status,
dev.pic_interrupt_line,
dev.interrupt_pin
);
devices.push(Arc::new(dev));
}
Ok(Self {
pci_device: Mutex::new(devices),
})
}
pub const fn name() -> &'static str {
"PCI"
}
// 0~65535
pub const fn priority() -> u16 {
0
}
}
impl PCIComponent {
pub fn get_pci_devices(self: &Self, index: usize) -> Option<Arc<PCIDevice>> {
self.pci_device.lock().get(index).cloned()
}
pub fn device_amount(self: &Self) -> usize {
self.pci_device.lock().len()
}
}

View File

@ -9,12 +9,13 @@ edition = "2021"
bitflags = "1.3"
spin = "0.9.4"
jinux-frame = {path = "../../../framework/jinux-frame"}
jinux-pci = {path="../jinux-pci"}
jinux-pci = {path="../pci"}
jinux-util = {path="../../libs/jinux-util"}
pod = {path = "../../../framework/pod"}
pod-derive = {path = "../../../framework/pod-derive"}
log= "0.4"
component = {path="../../comp-sys/component"}
log = "0.4"
[features]

View File

@ -9,7 +9,7 @@ use pod::Pod;
use crate::{
device::block::{BlkReq, BlkResp, ReqType, RespStatus, BLK_SIZE},
device::VirtioDeviceError,
queue::VirtQueue,
queue::{QueueError, VirtQueue},
VitrioPciCommonCfg,
};
@ -65,14 +65,17 @@ impl BLKDevice {
sector: block_id as u64,
};
let mut resp = BlkResp::default();
self.queue
let token = self
.queue
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
.expect("add queue failed");
self.queue.notify();
while !self.queue.can_pop() {
spin_loop();
}
self.queue.pop_used().expect("pop used failed");
self.queue
.pop_used_with_token(token)
.expect("pop used failed");
match resp.status {
RespStatus::Ok => {}
_ => panic!("io error in block device"),
@ -87,15 +90,17 @@ impl BLKDevice {
sector: block_id as u64,
};
let mut resp = BlkResp::default();
self.queue
let token = self
.queue
.add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()])
.expect("add queue failed");
self.queue.notify();
while !self.queue.can_pop() {
spin_loop();
}
self.queue.pop_used().expect("pop used failed");
self.queue
.pop_used_with_token(token)
.expect("pop used failed");
let st = resp.status;
match st {
RespStatus::Ok => {}
@ -103,16 +108,57 @@ impl BLKDevice {
};
}
pub fn read_block_nb(&mut self, block_id: usize, buf: &mut [u8], resp: &mut BlkResp) {
pub fn pop_used(&mut self) -> Result<(u16, u32), QueueError> {
self.queue.pop_used()
}
pub fn pop_used_with_token(&mut self, token: u16) -> Result<u32, QueueError> {
self.queue.pop_used_with_token(token)
}
/// read data from block device, this function is non-blocking
/// return value is token
pub fn read_block_non_blocking(
&mut self,
block_id: usize,
buf: &mut [u8],
req: &mut BlkReq,
resp: &mut BlkResp,
) -> u16 {
assert_eq!(buf.len(), BLK_SIZE);
let req = BlkReq {
*req = BlkReq {
type_: ReqType::In,
reserved: 0,
sector: block_id as u64,
};
self.queue
let token = self
.queue
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
.unwrap();
self.queue.notify();
token
}
/// write data to block device, this function is non-blocking
/// return value is token
pub fn write_block_non_blocking(
&mut self,
block_id: usize,
buf: &[u8],
req: &mut BlkReq,
resp: &mut BlkResp,
) -> u16 {
assert_eq!(buf.len(), BLK_SIZE);
*req = BlkReq {
type_: ReqType::Out,
reserved: 0,
sector: block_id as u64,
};
let token = self
.queue
.add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()])
.expect("add queue failed");
self.queue.notify();
token
}
}

View File

@ -152,6 +152,7 @@ impl VirtioDevice {
VirtioDeviceType::Input => InputDevice::negotiate_features(device_specified_features),
VirtioDeviceType::Crypto => todo!(),
VirtioDeviceType::Socket => todo!(),
VirtioDeviceType::Unknown => todo!(),
};
let support_feature = Feature::from_bits_truncate(features);
// support_feature.remove(Feature::RING_EVENT_IDX);

View File

@ -5,14 +5,19 @@
extern crate alloc;
use alloc::{sync::Arc, vec::Vec};
use component::init_component;
use core::str::FromStr;
use alloc::{collections::VecDeque, string::String, sync::Arc, vec::Vec};
use bitflags::bitflags;
use component::ComponentInitError;
use device::VirtioDevice;
use jinux_frame::{offset_of, TrapFrame};
use jinux_pci::util::{PCIDevice, BAR};
use jinux_util::frame_ptr::InFramePtr;
use log::{debug, info};
use pod_derive::Pod;
use spin::{Mutex, Once};
use crate::device::VirtioInfo;
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, msix::MSIX};
@ -20,9 +25,72 @@ use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, msix::MSIX};
extern crate pod_derive;
pub mod device;
pub mod queue;
pub static VIRTIO_COMPONENT: Once<VIRTIOComponent> = Once::new();
#[init_component]
fn virtio_component_init() -> Result<(), ComponentInitError> {
let a = VIRTIOComponent::init()?;
VIRTIO_COMPONENT.call_once(|| a);
Ok(())
}
pub struct VIRTIOComponent {
virtio_devices: Mutex<VecDeque<PCIVirtioDevice>>,
}
impl VIRTIOComponent {
pub fn init() -> Result<Self, ComponentInitError> {
let pci_devices =
jinux_pci::PCI_COMPONENT
.get()
.ok_or(ComponentInitError::UninitializedDependencies(
String::from_str("PCI").unwrap(),
))?;
let mut virtio_devices = VecDeque::new();
for index in 0..pci_devices.device_amount() {
let pci_device = pci_devices.get_pci_devices(index).unwrap();
if pci_device.id.vendor_id == 0x1af4 {
virtio_devices.push_back(PCIVirtioDevice::new(pci_device));
}
}
Ok(Self {
virtio_devices: Mutex::new(virtio_devices),
})
}
pub const fn name() -> &'static str {
"Virtio"
}
// 0~65535
pub const fn priority() -> u16 {
256
}
}
impl VIRTIOComponent {
pub fn pop(self: &Self) -> Option<PCIVirtioDevice> {
self.virtio_devices.lock().pop_front()
}
pub fn get_device(self: &Self, device_type: VirtioDeviceType) -> Vec<PCIVirtioDevice> {
let mut devices = Vec::new();
let mut lock = self.virtio_devices.lock();
let len = lock.len();
for i in 0..len {
let device = lock.pop_front().unwrap();
let d_type = VirtioDeviceType::from_virtio_device(&device.device);
if d_type == device_type {
devices.push(device);
} else {
lock.push_back(device);
}
}
devices
}
}
bitflags! {
/// The device status field.
pub struct DeviceStatus: u8 {
@ -118,7 +186,7 @@ impl VitrioPciCommonCfg {
}
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum VirtioDeviceType {
Network,
Block,
@ -130,7 +198,27 @@ pub enum VirtioDeviceType {
Input,
Crypto,
Socket,
Unknown,
}
impl VirtioDeviceType {
pub fn from_virtio_device(device: &VirtioDevice) -> Self {
match device {
VirtioDevice::Network => VirtioDeviceType::Network,
VirtioDevice::Block(_) => VirtioDeviceType::Block,
VirtioDevice::Console => VirtioDeviceType::Console,
VirtioDevice::Entropy => VirtioDeviceType::Entropy,
VirtioDevice::TraditionalMemoryBalloon => VirtioDeviceType::TraditionalMemoryBalloon,
VirtioDevice::ScsiHost => VirtioDeviceType::ScsiHost,
VirtioDevice::GPU => VirtioDeviceType::GPU,
VirtioDevice::Input(_) => VirtioDeviceType::Input,
VirtioDevice::Crypto => VirtioDeviceType::Crypto,
VirtioDevice::Socket => VirtioDeviceType::Socket,
VirtioDevice::Unknown => VirtioDeviceType::Unknown,
}
}
}
pub struct PCIVirtioDevice {
/// common config of one device
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
@ -250,17 +338,28 @@ impl PCIVirtioDevice {
}
}
/// register all the interrupt functions except the config change, this function should call only once
pub fn register_interrupt_functions<F>(&mut self, function: &'static F)
where
/// register all the interrupt functions, this function should call only once
pub fn register_interrupt_functions<F, T>(
&mut self,
config_change_function: &'static F,
other_function: &'static T,
) where
F: Fn(&TrapFrame) + Send + Sync + 'static,
T: Fn(&TrapFrame) + Send + Sync + 'static,
{
let config_msix_vector =
self.common_cfg
.read_at(offset_of!(VitrioPciCommonCfg, config_msix_vector)) as usize;
for i in 0..self.msix.table_size as usize {
let msix = self.msix.table.get_mut(i).unwrap();
if !msix.irq_handle.is_empty() {
panic!("function `register_queue_interrupt_functions` called more than one time");
}
msix.irq_handle.on_active(function);
if config_msix_vector == i {
msix.irq_handle.on_active(config_change_function);
} else {
msix.irq_handle.on_active(other_function);
}
}
}
}

View File

@ -9,12 +9,14 @@ use jinux_frame::{
vm::{VmAllocOptions, VmFrame, VmFrameVec},
};
use jinux_util::frame_ptr::InFramePtr;
use log::debug;
#[derive(Debug)]
pub enum QueueError {
InvalidArgs,
BufferTooSmall,
NotReady,
AlreadyUsed,
WrongToken,
}
/// The mechanism for bulk data transport on virtio devices.
@ -94,6 +96,7 @@ impl VirtQueue {
offset_of!(VitrioPciCommonCfg, queue_desc),
frame.paddr() as u64,
);
debug!("queue_desc vm frame:{:x?}", frame);
frame_vec.push(frame);
}
@ -105,6 +108,7 @@ impl VirtQueue {
offset_of!(VitrioPciCommonCfg, queue_driver),
frame.paddr() as u64,
);
debug!("queue_driver vm frame:{:x?}", frame);
frame_vec.push(frame);
}
@ -116,6 +120,7 @@ impl VirtQueue {
offset_of!(VitrioPciCommonCfg, queue_device),
frame.paddr() as u64,
);
debug!("queue_device vm frame:{:x?}", frame);
frame_vec.push(frame);
}
@ -278,6 +283,35 @@ impl VirtQueue {
Ok((index, len))
}
/// If the given token is next on the device used queue, pops it and returns the total buffer
/// length which was used (written) by the device.
///
/// Ref: linux virtio_ring.c virtqueue_get_buf_ctx
pub fn pop_used_with_token(&mut self, token: u16) -> Result<u32, QueueError> {
if !self.can_pop() {
return Err(QueueError::NotReady);
}
// read barrier
fence(Ordering::SeqCst);
let last_used_slot = self.last_used_idx & (self.queue_size - 1);
let index = self.used.read_at(
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8) as *const u32,
) as u16;
let len = self.used.read_at(
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8 + 4) as *const u32,
);
if index != token {
return Err(QueueError::WrongToken);
}
self.recycle_descriptors(index);
self.last_used_idx = self.last_used_idx.wrapping_add(1);
Ok(len)
}
/// Return size of the queue.
pub fn size(&self) -> u16 {
self.queue_size
@ -308,10 +342,16 @@ impl Descriptor {
}
fn set_buf(inframe_ptr: &InFramePtr<Descriptor>, buf: &[u8]) {
inframe_ptr.write_at(
offset_of!(Descriptor, addr),
jinux_frame::translate_not_offset_virtual_address(buf.as_ptr() as usize) as u64,
);
let va = buf.as_ptr() as usize;
let pa = if va >= jinux_frame::config::PHYS_OFFSET && va <= jinux_frame::config::KERNEL_OFFSET {
// can use offset
jinux_frame::virt_to_phys(va)
} else {
jinux_frame::translate_not_offset_virtual_address(buf.as_ptr() as usize)
};
debug!("set buf write virt address:{:x}", va);
debug!("set buf write phys address:{:x}", pa);
inframe_ptr.write_at(offset_of!(Descriptor, addr), pa as u64);
inframe_ptr.write_at(offset_of!(Descriptor, len), buf.len() as u32);
}
bitflags! {

View File

@ -9,8 +9,8 @@ edition = "2021"
jinux-frame = {path = "../../../framework/jinux-frame"}
pod = {path = "../../../framework/pod"}
pod-derive = {path = "../../../framework/pod-derive"}
jinux-pci = {path="../../comps/jinux-pci"}
jinux-virtio = {path="../../comps/jinux-virtio"}
jinux-input = {path="../../comps/input"}
jinux-block = {path="../../comps/block"}
controlled = { path = "../../comp-sys/controlled" }
typeflags = {path="../typeflags"}
typeflags-util = {path="../typeflags-util"}

View File

@ -1,7 +1,30 @@
pub mod pci;
pub mod tty;
use jinux_input::INPUT_COMPONENT;
use log::info;
pub fn init() {
pci::init();
// print all the input device to make sure input crate will compile
for comp in INPUT_COMPONENT.get().unwrap().get_input_device() {
info!("input device name:{}", comp.name());
}
tty::init();
}
#[allow(unused)]
fn block_device_test() {
let block_device = jinux_block::BLK_COMPONENT.get().unwrap().get_device();
let mut write_buffer = [0u8; 512];
let mut read_buffer = [0u8; 512];
info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize);
info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize);
for i in 0..512 {
for byte in write_buffer.iter_mut() {
*byte = i as u8;
}
block_device.write_block(i as usize, &write_buffer);
block_device.read_block(i as usize, &mut read_buffer);
assert_eq!(write_buffer, read_buffer);
}
info!("block device test passed!");
}

View File

@ -1,21 +0,0 @@
use log::info;
pub mod virtio;
pub fn init() {
jinux_pci::init();
for index in 0..jinux_pci::device_amount() {
let pci_device = jinux_pci::get_pci_devices(index)
.expect("initialize pci device failed: pci device is None");
if pci_device.id.vendor_id == 0x1af4 {
if pci_device.id.device_id == 0x1001 || pci_device.id.device_id == 0x1042 {
info!("found virtio block device");
virtio::block::init(pci_device);
} else if pci_device.id.device_id == 0x1011 || pci_device.id.device_id == 0x1052 {
info!("found virtio input device");
virtio::input::init(pci_device);
}
}
}
info!("pci initialization complete");
}

View File

@ -1,106 +0,0 @@
use crate::process::Process;
use alloc::sync::Arc;
use jinux_pci::msix::MSIX;
use jinux_pci::PCIDevice;
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::device::block::device::BLKDevice;
use jinux_virtio::device::block::BlkResp;
use jinux_virtio::PCIVirtioDevice;
use jinux_virtio::VitrioPciCommonCfg;
use lazy_static::lazy_static;
use log::info;
use spin::mutex::Mutex;
use super::BlockDevice;
pub const BLK_SIZE: usize = 512;
use jinux_frame::TrapFrame;
pub struct VirtioBlockDevice {
blk_device: Mutex<BLKDevice>,
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
msix: MSIX,
}
lazy_static! {
// TODO: use dyn BlockDevice instead
pub static ref BLOCK_DEVICE: Arc<Mutex<Option<VirtioBlockDevice>>> = Arc::new(Mutex::new(None)) ;
}
impl VirtioBlockDevice {
pub fn read_block_nb(&self, block_id: usize, buf: &mut [u8], res: &mut BlkResp) {
self.blk_device.lock().read_block_nb(block_id, buf, res);
}
}
impl BlockDevice for VirtioBlockDevice {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
self.blk_device.lock().read_block(block_id, buf);
}
/// it is blocking now
fn write_block(&self, block_id: usize, buf: &[u8]) {
self.blk_device.lock().write_block(block_id, buf);
}
fn handle_irq(&self) {
info!("handle irq in block device!");
}
}
impl VirtioBlockDevice {
fn new(mut virtio_device: PCIVirtioDevice) -> Self {
fn handle_block_device(frame: &TrapFrame) {
info!("pci block device queue interrupt");
BLOCK_DEVICE.lock().as_ref().unwrap().handle_irq();
}
virtio_device.register_interrupt_functions(&handle_block_device);
let blk_device = Mutex::new(match virtio_device.device {
jinux_virtio::device::VirtioDevice::Block(blk) => blk,
_ => {
panic!("Error when creating new block device, the input device is other type of virtio device");
}
});
Self {
blk_device,
common_cfg: virtio_device.common_cfg,
msix: virtio_device.msix,
}
}
}
pub fn init(pci_device: Arc<PCIDevice>) {
let virtio_device = PCIVirtioDevice::new(pci_device);
let mut a = BLOCK_DEVICE.lock();
a.replace(VirtioBlockDevice::new(virtio_device));
let dev = a.as_ref().unwrap();
drop(a);
}
fn inner_block_device_test() {
let block_device = BLOCK_DEVICE.clone();
let mut write_buffer = [0u8; 512];
let mut read_buffer = [0u8; 512];
info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize);
info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize);
for i in 0..512 {
for byte in write_buffer.iter_mut() {
*byte = i as u8;
}
info!("write block");
block_device
.lock()
.as_ref()
.unwrap()
.write_block(i as usize, &write_buffer);
info!("read block");
block_device
.lock()
.as_ref()
.unwrap()
.read_block(i as usize, &mut read_buffer);
assert_eq!(write_buffer, read_buffer);
}
info!("block device test passed!");
}
#[allow(unused)]
pub fn block_device_test() {
let _ = Process::spawn_kernel_process(|| {
inner_block_device_test();
});
}

View File

@ -1,182 +0,0 @@
use core::any::Any;
use core::sync::atomic::AtomicBool;
use alloc::collections::BTreeMap;
use alloc::{string::String, sync::Arc, vec::Vec};
use jinux_frame::{offset_of, TrapFrame};
use jinux_pci::{msix::MSIX, PCIDevice};
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::device::input::device::InputProp;
use jinux_virtio::VitrioPciCommonCfg;
use jinux_virtio::{
device::input::{device::InputDevice, InputConfigSelect},
PCIVirtioDevice,
};
use lazy_static::lazy_static;
use log::{debug, info};
use spin::Mutex;
use virtio_input_decoder::{DecodeType, Decoder};
pub trait INPUTDevice: Send + Sync + Any {
fn handle_irq(&self) -> Option<()>;
}
lazy_static! {
pub static ref KEYBOARD_EVENT: Mutex<Vec<DecodeType>> = Mutex::new(Vec::new());
pub static ref MOUSE_EVENT: Mutex<Vec<DecodeType>> = Mutex::new(Vec::new());
static ref KEYBOARD_CALLBACKS: Mutex<Vec<Arc<dyn Fn() + Send + Sync + 'static>>> =
Mutex::new(Vec::new());
static ref INPUT_DEVICE_LIST: Mutex<Vec<Arc<VirtioInputDevice>>> =
Mutex::new(Vec::with_capacity(2));
static ref INPUT_DEVICE_IRQ_HASH: Mutex<BTreeMap<u16, usize>> = Mutex::new(BTreeMap::new());
}
pub struct VirtioInputDevice {
input_device: InputDevice,
common_cfg: InFramePtr<VitrioPciCommonCfg>,
msix: Mutex<MSIX>,
is_keyboard: AtomicBool,
}
impl VirtioInputDevice {
fn new(virtio_device: PCIVirtioDevice, id: usize) -> Self {
let input_device = match virtio_device.device {
jinux_virtio::device::VirtioDevice::Input(dev) => dev,
_ => {
panic!("Error when creating new input device, the input device is other type of virtio device");
}
};
Self {
input_device,
common_cfg: virtio_device.common_cfg,
msix: Mutex::new(virtio_device.msix),
is_keyboard: AtomicBool::new(false),
}
}
fn register_interrupts(&self, id: usize) {
fn handle_input(frame: &TrapFrame) {
info!("in handle input");
let id = *INPUT_DEVICE_IRQ_HASH
.lock()
.get(&(frame.id as u16))
.expect("wrong irq number in input device trap handler");
INPUT_DEVICE_LIST
.lock()
.get(id)
.as_ref()
.unwrap()
.handle_irq();
}
fn config_space_change(frame: &TrapFrame) {}
let config_msix_vector =
self.common_cfg
.read_at(offset_of!(VitrioPciCommonCfg, config_msix_vector)) as usize;
let mut device_hash_lock = INPUT_DEVICE_IRQ_HASH.lock();
let mut msix = self.msix.lock();
for i in 0..msix.table_size as usize {
if i == config_msix_vector {
continue;
}
device_hash_lock.insert(msix.table.get(i).unwrap().irq_handle.num() as u16, id);
}
drop(device_hash_lock);
for i in 0..msix.table_size as usize {
let msix = msix.table.get_mut(i).unwrap();
if !msix.irq_handle.is_empty() {
panic!("function `register_queue_interrupt_functions` called more than one time");
}
if config_msix_vector == i {
msix.irq_handle.on_active(config_space_change);
} else {
msix.irq_handle.on_active(handle_input);
}
}
}
fn print_device_information(&self) {
let mut raw_name: [u8; 128] = [0; 128];
self.input_device
.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name);
let name = String::from_utf8(raw_name.to_vec()).unwrap();
info!("input device name:{}", name);
let mut prop: [u8; 128] = [0; 128];
self.input_device
.query_config_select(InputConfigSelect::PropBits, 0, &mut prop);
let input_prop = InputProp::from_bits(prop[0]).unwrap();
debug!("input device prop:{:?}", input_prop);
// if name.contains("Keyboard"){
// let mut raw_ev : [u8;128] = [0;128];
// let size = self.input_device.query_config_select(InputConfigSelect::EvBits, KEY, &mut raw_ev);
// info!("size:{}, raw_ev :{:x?}",size, raw_ev);
// }else{
// let mut raw_ev : [u8;128] = [0;128];
// let size = self.input_device.query_config_select(InputConfigSelect::EvBits, REL, &mut raw_ev);
// info!("size:{}, raw_ev :{:x?}",size, raw_ev);
// }
self.is_keyboard.store(
name.contains("Keyboard"),
core::sync::atomic::Ordering::Relaxed,
);
}
#[inline]
pub fn is_keyboard(&self) -> bool {
self.is_keyboard.load(core::sync::atomic::Ordering::Relaxed)
}
}
impl INPUTDevice for VirtioInputDevice {
fn handle_irq(&self) -> Option<()> {
let input = &self.input_device;
// one interrupt may contains serval input, so it should loop
loop {
let event = input.pop_pending_event()?;
let dtype = match Decoder::decode(
event.event_type as usize,
event.code as usize,
event.value as usize,
) {
Ok(dtype) => dtype,
Err(_) => return Some(()),
};
if self.is_keyboard() {
let mut lock = KEYBOARD_EVENT.lock();
lock.push(dtype);
drop(lock);
let lock = KEYBOARD_CALLBACKS.lock();
for callback in lock.iter() {
callback.call(());
}
} else {
let mut lock = MOUSE_EVENT.lock();
lock.push(dtype);
}
match dtype {
virtio_input_decoder::DecodeType::Key(key, r#type) => {
info!("{:?} {:?}", key, r#type);
}
virtio_input_decoder::DecodeType::Mouse(mouse) => info!("{:?}", mouse),
}
}
}
}
pub fn init(pci_device: Arc<PCIDevice>) {
let mut lock = INPUT_DEVICE_LIST.lock();
let id = lock.len();
let dev = Arc::new(VirtioInputDevice::new(PCIVirtioDevice::new(pci_device), id));
lock.push(dev.clone());
dev.register_interrupts(id);
drop(lock);
dev.print_device_information();
}
pub fn register_keyboard_callback(callback: Arc<dyn Fn() + 'static + Send + Sync>) {
KEYBOARD_CALLBACKS.lock().push(callback);
}

View File

@ -1,10 +0,0 @@
use core::any::Any;
pub mod block;
pub mod input;
pub trait BlockDevice: Send + Sync + Any {
fn read_block(&self, block_id: usize, buf: &mut [u8]);
fn write_block(&self, block_id: usize, buf: &[u8]);
fn handle_irq(&self);
}

View File

@ -90,7 +90,7 @@ impl<'a> Iterator for FutexIter<'a> {
return None;
}
while self.entry_ptr != &self.robust_list.list as *const _ as _ {
while self.entry_ptr != &self.robust_list.list as *const _ as usize {
if self.count == ROBUST_LIST_LIMIT {
break;
}

View File

@ -17,7 +17,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
test_main();
jinux_frame::init(boot_info);
println!("[kernel] finish init jinux_frame");
component::init(component::generate_information!()).unwrap();
jinux_std::init();
jinux_std::run_first_process();
}