Expand description
axvisor_api is the bottom-most crate of the AxVisor Hypervisor project. It
provides a standardized set of APIs for the components of the Hypervisor and
grants them access to OS-level and Hypervisor-level functionalities, like
memory allocation, address conversion, time and timer management, cross-vcpu
operations, and so on.
axvisor_api is designed for two main purposes:
-
Replace generic-based API-injection mechanism.
Generic-based API-injection mechanism, for example:
pub trait VCpuHal { /* ... */ } pub struct VCpu<H: VCpuHal> { /* ... */ }has been widely used previously here and in other related projects. It’s definitely great, with zero overhead, good readability, and no link-time magic. However, it turns out that passing generic parameters down through multiple layers of modules and components is quite inconvenient, and it’s always a pain to decide how to categorize the APIs into different traits properly.
Finally, the author decided to just use a big monolithic trait for every component, and use
crate_interfaceto eliminate the generic parameter. (Although there are multiple traits in this crate, technically in the current implementation the link-time symbol space is used as a big monolithic trait.) Theoretically, this may slightly increase the difficulty of re-using a single component in a new software project, but the author believes that it will reduce the overall complexity when not one but a few related components are re-used together. -
Enable portability of the Hypervisor across different unikernels.
Technically, the whole AxVisor Hypervisor can be ported to different unikernels by implementing the APIs defined in this crate (although not tested yet). If such porting is successful, this crate can also be used as a tested hardware-and-unikernel abstraction layer for other hypervisor projects.
This crate also provides a standard way to define and implement APIs, with
the api_def and api_impl procedural macros. They are built on top of
the crate_interface crate, which provides the low-level functionalities
of defining and implementing crate-level interfaces.
§How to define and implement APIs
§Define APIs
To define APIs, you can use the api_def attribute on a trait defining the
API, with each API function defined as a regular function in the trait. It’s
recommended to pack the trait definition and related definitions (like type
aliases) into a module for better organization.
mod example {
/// Example API definition
#[api_def]
pub trait ExampleIf {
/// An example API function
fn example_func(arg: usize) -> usize;
/// Another example API function
fn another_func();
}
}
fn use_example_api() {
let result = example::example_func(42);
example::another_func();
}api_def will generate a caller function for each API function defined in
the trait, at the same level as the trait definition. The generated callers
can be used to invoke the API functions, as demonstrated above.
§Implement APIs
Defined APIs should be implemented somewhere, unless they are not used
anywhere. To implement APIs, the implementer should define an empty struct
and implement the API trait for the struct, with the api_impl attribute on
the impl block. For example,
mod example {
/// Example API definition
#[api_def]
pub trait ExampleIf {
/// An example API function
fn example_func(arg: usize) -> usize;
/// Another example API function
fn another_func();
}
}
struct ExampleImpl;
#[api_impl]
impl example::ExampleIf for ExampleImpl {
fn example_func(arg: usize) -> usize {
arg + 1
}
fn another_func() {
println!("Another function called");
}
}
fn main() {
let result = example::example_func(42);
assert_eq!(result, 43);
example::another_func(); // prints "Another function called"
}Modules§
- arch
- Architecture-specific APIs.
- host
- Host system related APIs.
- memory
- Memory allocation and address translation APIs.
- time
- Time and timer APIs.
- vmm
- Virtual machine management APIs.