mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Component initialization mechanism complete
This commit is contained in:
parent
4f3e359892
commit
e4323f808c
@ -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",
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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.*)
|
||||
}
|
||||
|
@ -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() {
|
||||
|
19
src/services/comp-sys/component-macro/Cargo.toml
Normal file
19
src/services/comp-sys/component-macro/Cargo.toml
Normal 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"
|
33
src/services/comp-sys/component-macro/src/init_comp.rs
Normal file
33
src/services/comp-sys/component-macro/src/init_comp.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
83
src/services/comp-sys/component-macro/src/lib.rs
Normal file
83
src/services/comp-sys/component-macro/src/lib.rs
Normal 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()
|
||||
}
|
215
src/services/comp-sys/component-macro/src/priority.rs
Normal file
215
src/services/comp-sys/component-macro/src/priority.rs
Normal 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
|
||||
}
|
14
src/services/comp-sys/component/Cargo.toml
Normal file
14
src/services/comp-sys/component/Cargo.toml
Normal 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"
|
57
src/services/comp-sys/component/README.md
Normal file
57
src/services/comp-sys/component/README.md
Normal 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.
|
178
src/services/comp-sys/component/src/lib.rs
Normal file
178
src/services/comp-sys/component/src/lib.rs
Normal 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(())
|
||||
}
|
17
src/services/comp-sys/component/tests/kernel/Cargo.toml
Normal file
17
src/services/comp-sys/component/tests/kernel/Cargo.toml
Normal 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"}
|
@ -0,0 +1,5 @@
|
||||
# template
|
||||
[components]
|
||||
kernel = {name = "kernel"}
|
||||
foo = {name = "foo"}
|
||||
bar = {name = "bar"}
|
10
src/services/comp-sys/component/tests/kernel/bar/Cargo.toml
Normal file
10
src/services/comp-sys/component/tests/kernel/bar/Cargo.toml
Normal 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"}
|
||||
|
13
src/services/comp-sys/component/tests/kernel/bar/src/lib.rs
Normal file
13
src/services/comp-sys/component/tests/kernel/bar/src/lib.rs
Normal 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(())
|
||||
}
|
15
src/services/comp-sys/component/tests/kernel/foo/Cargo.toml
Normal file
15
src/services/comp-sys/component/tests/kernel/foo/Cargo.toml
Normal 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"
|
||||
|
15
src/services/comp-sys/component/tests/kernel/foo/src/lib.rs
Normal file
15
src/services/comp-sys/component/tests/kernel/foo/src/lib.rs
Normal 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(())
|
||||
}
|
@ -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);
|
||||
}
|
19
src/services/comp-sys/component/tests/kernel/src/main.rs
Normal file
19
src/services/comp-sys/component/tests/kernel/src/main.rs
Normal 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);
|
||||
}
|
17
src/services/comp-sys/component/tests/kernel/tests/test.rs
Normal file
17
src/services/comp-sys/component/tests/kernel/tests/test.rs
Normal 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);
|
||||
}
|
24
src/services/comps/block/Cargo.toml
Normal file
24
src/services/comps/block/Cargo.toml
Normal 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"]
|
71
src/services/comps/block/src/lib.rs
Normal file
71
src/services/comps/block/src/lib.rs
Normal 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()
|
||||
}
|
||||
}
|
54
src/services/comps/block/src/virtio.rs
Normal file
54
src/services/comps/block/src/virtio.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
25
src/services/comps/input/Cargo.toml
Normal file
25
src/services/comps/input/Cargo.toml
Normal 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"]
|
90
src/services/comps/input/src/lib.rs
Normal file
90
src/services/comps/input/src/lib.rs
Normal 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,
|
||||
}
|
122
src/services/comps/input/src/virtio.rs
Normal file
122
src/services/comps/input/src/virtio.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
@ -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"
|
86
src/services/comps/pci/src/lib.rs
Normal file
86
src/services/comps/pci/src/lib.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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]
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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! {
|
@ -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"}
|
||||
|
@ -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!");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
@ -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();
|
||||
});
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user