Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
9823d1f
[add] add `identity_may` feature
Alignof Feb 7, 2025
95fc335
[add] add `load_guest_elf` in `identity_map` feature
Alignof Feb 7, 2025
c5a58f8
[update] modify to call only if `identity_map` feature disabled
Alignof Feb 7, 2025
6f1b8a1
[add] add `PciDevice::memmap`
Alignof Feb 7, 2025
57882d2
[update] map storage devece memory if `identity_map` feature is enabled
Alignof Feb 7, 2025
b177eaa
[add] add memory mapping for identity map
Alignof Feb 7, 2025
9cc8d97
[add] add `map_guest_dtb` for `identity_map` feature
Alignof Feb 7, 2025
da480c6
[fix] fix loading initrd when `identity_map` feature is enabled
Alignof Feb 7, 2025
a80a644
[update] update memory.x for identity map
Alignof Feb 9, 2025
15c4a5c
[refactor] fix cargo clippy warnings
Alignof Jul 3, 2025
e49ff30
Merge pull request #84 from Alignof/feature/identity_map
Alignof Jul 3, 2025
993fcaa
[add] add workspace member `extension_manager`
Alignof Feb 12, 2025
cd9ae28
[add] init `extension_manager`
Alignof Feb 12, 2025
3120d20
[add] add lib.rs and move `EmulateExtension` trait to it
Alignof Feb 25, 2025
caa0765
[wip] tmp commit
Alignof Jun 4, 2025
48cc5b8
[!][update] move modules root to lib.rs
Alignof Jun 4, 2025
236d5dc
[refactor] apply `cargo clippy --fix`
Alignof Jun 5, 2025
9635506
[refactor] allow missing docs in h_extension::csrs
Alignof Jun 7, 2025
6f83c03
[!][fix] remove unnecessary `unsafe` keyword
Alignof Jun 8, 2025
dc24837
[refactor] add missing doc comments
Alignof Jun 8, 2025
2a6ae41
[update] update comments in extension_manager/build.rs
Alignof Jun 9, 2025
77b93c9
[add] add `emulate_extension::initialize`
Alignof Jun 9, 2025
3bc655e
[!][add] add `hikami_core` crate and move modules to it
Alignof Jun 9, 2025
7ea65ec
[wip][fix] use crate_name instead of module name in `extension_manage…
Alignof Jun 12, 2025
5212f6b
[update] remove `emulate_extension::initialize`
Alignof Jun 12, 2025
f9e267c
[add] add `import_global_variables`
Alignof Jun 12, 2025
73ad689
[!][update] move `hikami_core::trap` module to `hikami`
Alignof Jun 12, 2025
1f001e6
[update] make `enable_extension` enable as default
Alignof Jun 12, 2025
59b8f8e
[fix] fix extension macros
Alignof Jun 15, 2025
921ac51
[update] use `extension_manager::import_global_variables`
Alignof Jun 19, 2025
8712448
[add] add `handle_illegal_inst`
Alignof Jul 1, 2025
38170d6
[add] add `generate_instruction_arms`
Alignof Jul 1, 2025
0a7e6eb
[add] add `generate_csr_arms`
Alignof Jul 1, 2025
ca85ea8
[add] add `is_csr_defined` to `EmulateExtension`
Alignof Jul 2, 2025
26d6106
[add] add `handle_virtual_inst` and `generate_csr_field_arms`
Alignof Jul 2, 2025
a6a60de
[add] add `is_csr_field_defined` to `EmulateExtension`
Alignof Jul 2, 2025
9de1cbf
[update] change arguments of `EmulateExtension::csr_field`
Alignof Jul 2, 2025
972498c
[refactor] apply cargo fix and cargo fmt
Alignof Jul 3, 2025
75583de
[!][fix] add features to child crate and automatically enable if root…
Alignof Jul 4, 2025
d5ad3d6
[!][fix] remove raw variable `ZICFISS_DATA` from code
Alignof Jul 4, 2025
dab0bba
[update] add example emulation crate `hikami_zbb`
Alignof Jul 6, 2025
65fdc0a
[update] add rerun condition to extension_manager/build.rs to watch C…
Alignof Jul 6, 2025
790ae33
[update] add a description of extension manager
Alignof Jul 6, 2025
1ac18d2
[!][update] move `GUEST_KERNEL`, `GUEST_INITRD` and `GUEST_DTB` to hi…
Alignof Jul 6, 2025
5186188
[update] add patch section to Cargo.toml to avoid crate confliction
Alignof Jul 6, 2025
bf034e2
[fix] fix source of `hikami_zbb` crate
Alignof Jul 6, 2025
ffcbd51
Merge pull request #86 from Alignof/fix/remove_binary_info_from_hikam…
Alignof Jul 6, 2025
c268f96
Merge pull request #85 from Alignof/feature/hikami_core
Alignof Jul 6, 2025
93d1cbe
[update] update root crate edition
Alignof Jul 6, 2025
cf800e8
[refactor] fix `clippy::uninlined_format_args` in extension_manager/b…
Alignof Jul 6, 2025
5058e18
[update] update rust channel (nightly -> 1.88.0)
Alignof Jul 8, 2025
6d2f2ce
[refactor] apply `cargo clippy --fix --bin "hikami"`
Alignof Jul 8, 2025
e0141a1
[refactor] apply cargo fmt
Alignof Jul 8, 2025
2fe29bd
Merge pull request #87 from Alignof/feature/edition_2024
Alignof Jul 8, 2025
2242421
[!][fix] fix `Devices::create_device_map`
Alignof Jul 9, 2025
fd18819
[!][update] remove `memory_maps` argument from `PciDevice::new`
Alignof Jul 9, 2025
21a39df
[rebase] 1st
Alignof Jul 9, 2025
4b7cd64
[update] update default drive in device tree
Alignof Jul 9, 2025
40baf07
[refactor] add comments to `MmioDevice` implementation for `Pci`
Alignof Jul 9, 2025
169b6c6
[fix] fix an argument of `Guest::map_guest_dtb` in `identity_map` fea…
Alignof Jul 9, 2025
9091262
Merge pull request #88 from Alignof/fix/sata_emulation
Alignof Jul 9, 2025
43976fc
[add] add QEMU option description to disable zbb emulation in QEMU
Alignof Jul 9, 2025
c824f6f
[update] hikami's version (1.2.0 -> 2.0.0)
Alignof Jul 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[target.riscv64imac-unknown-none-elf]
# if you wanna test zbb emulation, you can:
# -cpu rv64,smstateen=true -> -cpu rv64,smstateen=true,zbb=false
runner = """
qemu-system-riscv64
-cpu rv64,smstateen=true
Expand Down
40 changes: 33 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "hikami"
version = "1.2.0"
edition = "2021"
version = "2.0.0"
edition = "2024"

[lints.clippy]
pedantic = "warn"
Expand All @@ -17,17 +17,43 @@ missing_crate_level_docs = "warn"
panic = 'abort'
codegen-units = 1

[features]
# debug log
debug_log = []
[workspace]
members = [ "extension_manager" , "hikami_core"]

[dependencies]
[workspace.dependencies]
elf = { version = "0.7.2", default-features = false }
fdt = "0.1.5"
linked_list_allocator = "0.10.5"
raki = "1.3.1"
raki = { git = "https://github.com/Alignof/raki", branch = "feature/Zbb"}
riscv = "0.11.1"
rustsbi = "0.4.0"
sbi-rt = "0.0.3"
sbi-spec = { version = "0.0.8", features = [ "legacy" ] }
spin = "0.9.8"

[features]
# debug log
debug_log = [ "hikami_core/debug_log" ]
# identity memory map
identity_map = [ "hikami_core/identity_map" ]
# default on
default = [ "enable_extension" ]
# for `extension_manager`
enable_extension = [ "hikami_zbb" ]

[dependencies]
hikami_core = { path = "hikami_core" }
hikami_zbb = { git = "https://github.com/Alignof/hikami_zbb", branch = "develop", optional = true }
extension_manager = { path = "extension_manager" }

elf = { workspace = true }
fdt = { workspace = true }
raki = { workspace = true }
riscv = { workspace = true }
rustsbi = { workspace = true }
sbi-rt = { workspace = true }
sbi-spec = { workspace = true }
linked_list_allocator = { workspace = true }

[patch.'https://github.com/Alignof/hikami']
hikami_core = { path = "hikami_core" }
44 changes: 40 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# hikami
[![Rust](https://github.com/Alignof/hikami/actions/workflows/rust.yml/badge.svg)](https://github.com/Alignof/hikami/actions/workflows/rust.yml)
[![Rust](https://github.com/Alignof/hikami/actions/workflows/rust.yml/badge.svg)](https://github.com/Alignof/hikami/actions/workflows/rust.yml)\
A lightweight Type-1 hypervisor for RISC-V H-extension, featuring **RISC-V extension emulation**.

This project aims not only to realize a lightweight hypervisor that can be used on RISC-V H extensions, but also to easily reproduce and manage the "extension" on the hypervisor.
Poster in RISC-V Days Tokyo 2024 Summer: [PDF](https://riscv.or.jp/wp-content/uploads/RV-Days_Tokyo_2024_Summer_paper_9.pdf)
This project aims not only to realize a lightweight hypervisor that can be used on RISC-V H extensions, but also to easily reproduce and manage the "extension" on the hypervisor.\
Poster in RISC-V Days Tokyo 2024 Summer: [PDF](https://riscv.or.jp/wp-content/uploads/RV-Days_Tokyo_2024_Summer_paper_9.pdf)\
Paper in ComSys2024(ja): [link](https://ipsj.ixsq.nii.ac.jp/records/241051)

## Related projects
Expand All @@ -16,6 +16,42 @@ Paper in ComSys2024(ja): [link](https://ipsj.ixsq.nii.ac.jp/records/241051)
$ cargo doc --open
```

## Extension Management
hikami utilizes a procedural macro crate called `extension_manager` to dynamically incorporate RISC-V extension emulations.
This allows for adding or removing extension supports without modifying the core hypervisor code.

To enable an extension, simply add the extension crate name to the `enable_extension` feature list in the root `Cargo.toml` file.
For example, to enable the `Zbb` extension, you would add `hikami_zbb` as follows:

```toml
# hikami/Cargo.toml
[dependencies]
hikami_zbb = { git = "https://github.com/Alignof/hikami_zbb", optional = true }
[features]
enable_extension = [ "hikami_zbb" ]
```

See also: [https://github.com/Alignof/hikami_zbb](https://github.com/Alignof/hikami_zbb)

and add `zbb=false` option to qemu args in `.cargo/config.toml`.
```toml
[target.riscv64imac-unknown-none-elf]
runner = """
qemu-system-riscv64
-cpu rv64,smstateen=true,zbb=false
-machine virt
-bios default
-nographic
-m 2G
-drive file=rootfs.ext2,format=raw,id=hd0,if=none
-device ich9-ahci,id=ahci -device ide-hd,drive=hd0,bus=ahci.0
-kernel
"""
```

During the build process, `extension_manager` automatically detects these crates and expands the necessary code to initialize the extension and dispatch instruction handling.
This approach simplifies the management of multiple extensions and enhances the modularity of the hypervisor.

## Getting Started
### Setup
```sh
Expand Down Expand Up @@ -92,5 +128,5 @@ Coming soon...
- [hypocaust-2](https://github.com/KuangjuX/hypocaust-2)

## Acknowledgement
Exploratory IT Human Resources Project (MITOU Program) of Information-technology Promotion Agency, Japan (IPA) in the fiscal year 2024.
Exploratory IT Human Resources Project (MITOU Program) of Information-technology Promotion Agency, Japan (IPA) in the fiscal year 2024.\
[https://www.ipa.go.jp/jinzai/mitou/it/2024/gaiyou-tn-3.html](https://www.ipa.go.jp/jinzai/mitou/it/2024/gaiyou-tn-3.html)
15 changes: 15 additions & 0 deletions extension_manager/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "extension_manager"
version = "0.1.0"
edition = "2024"

[lib]
proc-macro = true

[build-dependencies]
serde_json = "1.0.138"

[dependencies]
quote = "1.0.38"
syn = "2.0.98"
proc-macro2 = "1.0.93"
62 changes: 62 additions & 0 deletions extension_manager/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;

fn main() {
println!("cargo:rerun-if-changed=Cargo.toml");

// exec `cargo metadata` and get a json.
let output = Command::new("cargo")
.args(["metadata", "--format-version=1", "--no-deps"])
.output()
.expect("Failed to execute cargo metadata");

let metadata = String::from_utf8(output.stdout).expect("Invalid UTF-8");

// parse the json.
let json: serde_json::Value = serde_json::from_str(&metadata).expect("Failed to parse JSON");

// retrieve `workspace_root`.
let workspace_root = json.get("workspace_root").and_then(|v| v.as_str()).unwrap();
let root_package_name = Path::new(workspace_root)
.file_name()
.and_then(|s| s.to_str())
.unwrap();

// extract `features` in `macro_crate`.
let root_crate = json
.get("packages")
.and_then(|v| v.as_array())
.and_then(|packages| {
packages
.iter()
.find(|pkg| pkg.get("name").and_then(|n| n.as_str()) == Some(root_package_name))
})
.expect("Failed to find root_crate package");

dbg!(&root_crate);

// collect crates reguardless of `enable_extension`.
let feature_dependencies: HashSet<String> = root_crate
.get("features")
.and_then(|v| v.as_object())
.and_then(|features| features.get("enable_extension")) // crates reguardless of `enable_extension`
.and_then(|v| v.as_array())
.map(|deps| {
deps.iter()
.filter_map(|dep| dep.as_str())
.map(|dep| dep.replace('-', "_")) // replace `-` with `_`
.collect()
})
.unwrap_or_default();
let crate_names = feature_dependencies.into_iter().collect::<Vec<_>>();

// output crates list to `OUT_DIR/dependencies.rs`
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = format!("{out_dir}/dependencies.rs");
let content = format!("static CRATES: &[&str] = &{crate_names:?};");

fs::write(out_path, content).expect("Failed to write dependencies.rs");
}
186 changes: 186 additions & 0 deletions extension_manager/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//! Hikami extension manager.
//!
//! It load extension emulation module as crate and expand function calls from macro.

extern crate proc_macro;
extern crate proc_macro2;

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::Ident;

include!(concat!(env!("OUT_DIR"), "/dependencies.rs"));

/// Handle all virtual instruction exception.
#[proc_macro]
pub fn handle_virtual_inst(_input: TokenStream) -> TokenStream {
let csr_field_arms = generate_csr_field_arms();
let expanded = quote! {
match fault_inst.opc {
OpcodeKind::Zicsr(_) => {
let csr_num = fault_inst.rs2.unwrap() as u16;
#(#csr_field_arms)*
}
_ => unreachable!(),
}
};

TokenStream::from(expanded)
}

/// Expand all `EmulateExtension::csr_field`.
fn generate_csr_field_arms() -> impl Iterator<Item = proc_macro2::TokenStream> {
CRATES.iter().map(|crate_name| {
let ext_name = crate_name
.strip_prefix("hikami_")
.expect("Crate name should start with 'hikami_'");
let global_var_name = format!("{}_DATA", ext_name.to_uppercase());
let global_var_ident = Ident::new(&global_var_name, Span::call_site());

quote! {
if unsafe { #global_var_ident.lock().get().unwrap().is_csr_field_defined(csr_num) } {
// update emulated CSR field.
unsafe { #global_var_ident.lock() }.get_mut().unwrap().csr_field(
&fault_inst,
);

let mut context = unsafe { HYPERVISOR_DATA.lock().get().unwrap().guest().context };
context.update_sepc_by_inst(&fault_inst);

return;
}
}
})
}

/// Handle all illegal instruction exception.
#[proc_macro]
pub fn handle_illegal_inst(_input: TokenStream) -> TokenStream {
let inst_arms = generate_instruction_arms();
let csr_arms = generate_csr_arms();
let expanded = quote! {
match fault_inst.opc {
#(#inst_arms)*
OpcodeKind::Zicsr(_) => {
let csr_num = fault_inst.rs2.unwrap() as u16;
#(#csr_arms)*
unimplemented!("unsupported CSRs: {csr_num:#x}");
}
_ => hs_forward_exception(),
}

let mut context = unsafe { HYPERVISOR_DATA.lock().get().unwrap().guest().context };
context.update_sepc_by_inst(&fault_inst);
};

TokenStream::from(expanded)
}

/// Expand all `EmulateExtension::csr`.
fn generate_csr_arms() -> impl Iterator<Item = proc_macro2::TokenStream> {
CRATES.iter().map(|crate_name| {
let ext_name = crate_name
.strip_prefix("hikami_")
.expect("Crate name should start with 'hikami_'");
let global_var_name = format!("{}_DATA", ext_name.to_uppercase());
let global_var_ident = Ident::new(&global_var_name, Span::call_site());

quote! {
if unsafe { #global_var_ident.lock().get().unwrap().is_csr_defined(csr_num) } {
unsafe { #global_var_ident.lock() }
.get_mut()
.unwrap()
.csr(&fault_inst);

let mut context = unsafe { HYPERVISOR_DATA.lock().get().unwrap().guest().context };
context.update_sepc_by_inst(&fault_inst);

return;
}
}
})
}

/// Genrate all `EmulateExtension::instruction` arm from extension crate lists.
fn generate_instruction_arms() -> impl Iterator<Item = proc_macro2::TokenStream> {
CRATES.iter().map(|crate_name| {
let ext_name = crate_name
.strip_prefix("hikami_")
.expect("Crate name should start with 'hikami_'");
let global_var_name = format!("{}_DATA", ext_name.to_uppercase());

let mut variant_name_chars = ext_name.chars();
let variant_name = match variant_name_chars.next() {
None => String::new(),
Some(c) => c.to_uppercase().collect::<String>() + variant_name_chars.as_str(),
};
let variant_ident = Ident::new(&variant_name, Span::call_site());
let global_var_ident = Ident::new(&global_var_name, Span::call_site());

quote! {
OpcodeKind::#variant_ident(_) => unsafe { #global_var_ident.lock() }
.get_mut()
.unwrap()
.instruction(&fault_inst),
}
})
}

/// Import all global varables.
#[proc_macro]
pub fn import_global_variables(_input: TokenStream) -> TokenStream {
let calls = CRATES.iter().map(|crate_name| {
let ext_name = crate_name
.strip_prefix("hikami_")
.expect("Crate name should start with 'hikami_'");
let global_var_name = format!("{}_DATA", ext_name.to_uppercase());

let crate_ident = Ident::new(crate_name, Span::call_site());
let global_var_ident = Ident::new(&global_var_name, Span::call_site());

quote! {
use #crate_ident::#global_var_ident;
}
});

let expanded = quote! {
#(#calls)*
};

TokenStream::from(expanded)
}

/// Initialize all global variables.
#[proc_macro]
pub fn initialize(_input: TokenStream) -> TokenStream {
let calls = CRATES.iter().map(|crate_name| {
let ext_name = crate_name
.strip_prefix("hikami_")
.expect("Crate name should start with 'hikami_'");
// crate name format: hikami_extension-name
let mut struct_name_chars = ext_name.chars();
let struct_name = match struct_name_chars.next() {
None => String::new(),
Some(c) => c.to_uppercase().collect::<String>() + struct_name_chars.as_str(),
};
let global_var_name = format!("{}_DATA", ext_name.to_uppercase());

let crate_ident = Ident::new(crate_name, Span::call_site());
let struct_ident = Ident::new(&struct_name, Span::call_site());
let global_var_ident = Ident::new(&global_var_name, Span::call_site());

quote! {
use #crate_ident::{#struct_ident, #global_var_ident};
unsafe {
#global_var_ident.lock().get_or_init(#struct_ident::new);
}
}
});

let expanded = quote! {
#(#calls)*
};

TokenStream::from(expanded)
}
Loading
Loading