GithubHelp home page GithubHelp logo

kvm-bindings's Introduction

Crates.io

kvm-bindings

Rust FFI bindings to KVM, generated using bindgen. It currently has support for the following target architectures:

  • x86_64
  • arm64

The bindings exported by this crate are statically generated using header files associated with a specific kernel version, and are not automatically synced with the kernel version running on a particular host. The user must ensure that specific structures, members, or constants are supported and valid for the kernel version they are using. For example, the immediate_exit field from the kvm_run structure is only meaningful if the KVM_CAP_IMMEDIATE_EXIT capability is available. Using invalid fields or features may lead to undefined behaviour.

Usage

First, add the following to your Cargo.toml:

kvm-bindings = "0.3"

Next, add this to your crate root:

extern crate kvm_bindings;

This crate also offers safe wrappers over FAM structs - FFI structs that have a Flexible Array Member in their definition. These safe wrappers can be used if the fam-wrappers feature is enabled for this crate. Example:

kvm-bindings = { version = "0.3", features = ["fam-wrappers"]}

Dependencies

The crate has an optional dependency to vmm-sys-util when enabling the fam-wrappers feature.

It also has an optional dependency on serde when enabling the serde feature, to allow serialization of bindings. Serialization of bindings happens as opaque binary blobs via zerocopy. Due to the kernel's ABI compatibility, this means that bindings serialized in version x of kvm-bindings can be deserialized in version y of the crate, even if the bindings have had been regenerated in the meantime.

Regenerating Bindings

Please see CONTRIBUTING.md for details on how to generate the bindings or add support for new architectures.

kvm-bindings's People

Contributors

00xc avatar acatangiu avatar alexandruag avatar andreeaflorescu avatar catdum avatar chenhengqi avatar dependabot-preview[bot] avatar dependabot[bot] avatar dianpopa avatar jonathanwoollett-light avatar lauralt avatar likebreath avatar matiasvara avatar rbradford avatar roypat avatar studychao avatar timeprinciple avatar wllenyj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kvm-bindings's Issues

Make KVM versions mutually exclusive

Context

The Linux version is selected by using cargo features. If the client doesn't want to use the default bindings (at the moment generated from kernel version 4.20) other versions can be selected by using rust features.
For example, to select the kvm bindings generated from the linux version 4.14, you would need to write the following line in Cargo.toml:

kvm_wrapper = { version = "0.1.0", features = ["kvm_v4_14_0"]}

Problem

The problem is that nothing stops you from writing the following in your Cargo.toml file:

kvm_wrapper = { version = "0.1.0", features = ["kvm_v4_14_0", "kvm_v4_20_0"]}

If the user selects more than one version though cargo will try to export bindings for all the existing versions which results in unused warnings:

...
warning: constant item is never used: `kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS`                                                                                                                                                                                                  
    --> src/x86/bindings_v4_20_0.rs:9589:1                                                                                                                                                                                                                                         
     |                                                                                                                                                                                                                                                                             
9589 | pub const kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS: kvm_device_type = 8;                                                                                                                                                                                                   
     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                   
warning: constant item is never used: `kvm_device_type_KVM_DEV_TYPE_MAX`                                                                                                                                                                                                           
    --> src/x86/bindings_v4_20_0.rs:9590:1                                                                                                                                                                                                                                         
     |                                                                                                                                                                                                                                                                             
9590 | pub const kvm_device_type_KVM_DEV_TYPE_MAX: kvm_device_type = 9;                                                                                                                                                                                                            
     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                                                                                            
...

Nice to Have

It would be nice to make features mutually exclusive, but this is not currently possible out of the box. See cargo issue rust-lang/cargo#2980.

We should anyway find a hack to fix this.

update to the latest version of vmm-sys-util

The latest version fixes some unsound code in FAM struct:

  • correctly implements PartialEq and Clone such that all fields are taken into consideration (previously only the length and entries were cloned/compared)
  • validate that from_entries cannot allocate more elements that what were specified as max_len.

Experimental riscv64 support

Following on from a Slack conversation. Here is the outline for enabling riscv64 support here:

  • Update KVM bindings for aarch64 / x86-64 to latest released kernel
  • Generate riscv64 bindings from same kernel
  • Add basic cross compilation testing - probably using GitHub actions as I don't think rust-vmm-ci is cross aware.

FamStruct safe wrappers don't implement clone

The FamStructWrapper safe wrapper implements Clone if the object it wraps also implements Clone.

To enable Clone on the safe wrappers CpuId, Msrs and MsrList we need to implement Clone for kvm_cpuid2, kvm_msrs and respectively kvm_msr_list.

Update the bindings to Linux 5.13

I was wondering what's the correct process to update the bindings to 5.13. There are some new KVM ioctls and capabilities which I would like to leverage, but they've been added after 4.20.
/cc @andreeaflorescu

Renovating `CpuId`

At the moment the current implementation of CpuId and the FamStructWrapper and FamStruct underlying it, are not as simple as they could be. For interop with kvm_bindings I wrote a custom structure which is both safe and has an identical memory layout to kvm_cpuid2, a zero-cost wrapper.

This structure could directly replace CpuId or possibly a more generic variant could be used in place of FamStructWrapper and FamStruct.

It would be good to get some feedback and discussion relating to what areas a change like this may influence.

use serde::{Deserialize, Serialize};

/// A rusty mimic of
/// [`kvm_cpuid`](https://elixir.bootlin.com/linux/v5.10.129/source/arch/x86/include/uapi/asm/kvm.h#L226)
/// .
///
/// [`RawCpuid`] has an identical memory layout to
/// [`kvm_cpuid`](https://elixir.bootlin.com/linux/v5.10.129/source/arch/x86/include/uapi/asm/kvm.h#L226)
/// .
///
/// This allows [`RawCpuid`] to function as a simpler replacement for [`kvm_bindings::CpuId`]. In
/// the future it may replace [`kvm_bindings::CpuId`] fully.
///
/// For implementation details see <https://doc.rust-lang.org/nomicon/vec/vec.html>.
#[derive(Debug)]
#[repr(C)]
pub struct RawCpuid {
    /// Number of entries.
    nent: u32,
    /// Padding.
    padding: Padding<{ size_of::<u32>() }>,
    /// Pointer to entries.
    entries: NonNull<RawCpuidEntry>,
    _marker: PhantomData<RawCpuidEntry>,
}
// This implementation could be significantly more efficient.
impl Clone for RawCpuid {
    fn clone(&self) -> Self {
        let mut new_raw_cpuid = Self::new();
        new_raw_cpuid.resize(self.nent as usize);
        for i in 0..self.nent as usize {
            new_raw_cpuid[i] = self[i].clone();
        }
        new_raw_cpuid
    }
}
impl serde::Serialize for RawCpuid {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        use serde::ser::SerializeSeq;
        let mut seq = serializer.serialize_seq(Some(self.nent as usize))?;
        for i in 0..self.nent as usize {
            seq.serialize_element(&self[i])?;
        }
        seq.end()
    }
}
struct RawCpuidVisitor;
impl<'de> serde::de::Visitor<'de> for RawCpuidVisitor {
    type Value = RawCpuid;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("Expected sequence of RawCpuidEntry")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::SeqAccess<'de>,
    {
        let mut entries = Vec::new();
        while let Some(next) = seq.next_element::<RawCpuidEntry>()? {
            entries.push(next);
        }
        Ok(Self::Value::from(entries))
    }
}
impl<'de> serde::Deserialize<'de> for RawCpuid {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_seq(RawCpuidVisitor)
    }
}
impl PartialEq for RawCpuid {
    fn eq(&self, other: &Self) -> bool {
        if self.nent == other.nent {
            for i in 0..self.nent as usize {
                if self[i] != other[i] {
                    return false;
                }
            }
            true
        } else {
            false
        }
    }
}
impl Eq for RawCpuid {}
unsafe impl Send for RawCpuid {}
unsafe impl Sync for RawCpuid {}
impl RawCpuid {
    /// Alias for [`RawCpuid::default()`].
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }
    /// Returns number of elements.
    #[must_use]
    pub fn nent(&self) -> u32 {
        self.nent
    }
    /// Returns an entry for a given lead (function) and sub-leaf (index).
    ///
    /// Returning `None` if it is not present.
    #[must_use]
    pub fn get(&self, leaf: u32, sub_leaf: u32) -> Option<&RawCpuidEntry> {
        // TODO Would using binary search here for leaf offer much speedup?
        self.iter()
            .find(|entry| entry.function == leaf && entry.index == sub_leaf)
    }
    /// Resizes allocated memory
    #[allow(clippy::cast_ptr_alignment)]
    fn resize(&mut self, n: usize) {
        // alloc
        if self.nent == 0 && n > 0 {
            let new_layout = Layout::array::<RawCpuidEntry>(n).unwrap();

            // Ensure that the new allocation doesn't exceed `isize::MAX` bytes.
            assert!(
                isize::try_from(new_layout.size()).is_ok(),
                "Allocation too large"
            );

            let new_ptr = unsafe { std::alloc::alloc(new_layout) };
            self.entries = match NonNull::new(new_ptr.cast::<RawCpuidEntry>()) {
                Some(p) => p,
                None => std::alloc::handle_alloc_error(new_layout),
            };
        }
        // realloc
        else if self.nent > 0 && n > 0 {
            let new_layout = Layout::array::<RawCpuidEntry>(n).unwrap();

            // Ensure that the new allocation doesn't exceed `isize::MAX` bytes.
            assert!(
                isize::try_from(new_layout.size()).is_ok(),
                "Allocation too large"
            );

            let old_layout =
                Layout::array::<RawCpuidEntry>(usize::try_from(self.nent).unwrap()).unwrap();
            let old_ptr = self.entries.as_ptr().cast::<u8>();
            let new_ptr = unsafe { std::alloc::realloc(old_ptr, old_layout, new_layout.size()) };

            self.entries = match NonNull::new(new_ptr.cast::<RawCpuidEntry>()) {
                Some(p) => p,
                None => std::alloc::handle_alloc_error(new_layout),
            };
        }
        // dealloc
        else if self.nent > 0 && n == 0 {
            let old_layout =
                Layout::array::<RawCpuidEntry>(usize::try_from(self.nent).unwrap()).unwrap();
            let old_ptr = self.entries.as_ptr().cast::<u8>();
            unsafe { std::alloc::dealloc(old_ptr, old_layout) };
            self.entries = NonNull::dangling();
        }
        self.nent = u32::try_from(n).unwrap();
    }

    /// Pushes entry onto end.
    ///
    /// # Panics
    ///
    /// On allocation failure.
    pub fn push(&mut self, entry: RawCpuidEntry) {
        self.resize(usize::try_from(self.nent).unwrap() + 1);
        unsafe {
            std::ptr::write(
                self.entries
                    .as_ptr()
                    .add(usize::try_from(self.nent).unwrap()),
                entry,
            )
        }
    }
    /// Pops entry from end.
    ///
    /// # Panics
    ///
    /// On allocation failure.
    pub fn pop(&mut self) -> Option<RawCpuidEntry> {
        if self.nent > 0 {
            let u_nent = usize::try_from(self.nent).unwrap();
            let rtn = unsafe { Some(std::ptr::read(self.entries.as_ptr().add(u_nent))) };
            self.resize(u_nent - 1);
            rtn
        } else {
            None
        }
    }
}
impl Default for RawCpuid {
    fn default() -> Self {
        Self {
            nent: 0,
            padding: Padding::default(),
            entries: NonNull::dangling(),
            _marker: PhantomData,
        }
    }
}

// We implement custom drop which drops all entries using `self.nent`
impl Drop for RawCpuid {
    fn drop(&mut self) {
        if self.nent != 0 {
            unsafe {
                std::alloc::dealloc(
                    self.entries.as_ptr().cast::<u8>(),
                    Layout::array::<RawCpuidEntry>(usize::try_from(self.nent).unwrap()).unwrap(),
                );
            }
        }
    }
}
impl Deref for RawCpuid {
    type Target = [RawCpuidEntry];
    fn deref(&self) -> &Self::Target {
        unsafe {
            std::slice::from_raw_parts(self.entries.as_ptr(), usize::try_from(self.nent).unwrap())
        }
    }
}
impl DerefMut for RawCpuid {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            std::slice::from_raw_parts_mut(
                self.entries.as_ptr(),
                usize::try_from(self.nent).unwrap(),
            )
        }
    }
}
impl From<kvm_bindings::CpuId> for RawCpuid {
    fn from(value: kvm_bindings::CpuId) -> Self {
        // As cannot acquire ownership of the underlying slice, we clone it.
        let cloned = value.as_slice().to_vec();
        let (ptr, len, _cap) = vec_into_raw_parts(cloned);
        Self {
            nent: u32::try_from(len).unwrap(),
            padding: Padding::default(),
            entries: NonNull::new(ptr.cast::<RawCpuidEntry>()).unwrap(),
            _marker: PhantomData,
        }
    }
}
impl From<Vec<RawCpuidEntry>> for RawCpuid {
    fn from(vec: Vec<RawCpuidEntry>) -> Self {
        let (ptr, len, _cap) = vec_into_raw_parts(vec);
        Self {
            nent: u32::try_from(len).unwrap(),
            padding: Padding::default(),
            entries: NonNull::new(ptr.cast::<RawCpuidEntry>()).unwrap(),
            _marker: PhantomData,
        }
    }
}
impl From<RawCpuid> for kvm_bindings::CpuId {
    fn from(this: RawCpuid) -> Self {
        let cpuid_slice = unsafe {
            std::slice::from_raw_parts(this.entries.as_ptr(), usize::try_from(this.nent).unwrap())
        };
        // println!("cpuid_slice: {:?}",cpuid_slice);
        #[allow(clippy::transmute_ptr_to_ptr)]
        let kvm_bindings_slice = unsafe { std::mem::transmute(cpuid_slice) };
        kvm_bindings::CpuId::from_entries(kvm_bindings_slice).unwrap()
    }
}

/// Mimic of the currently unstable
/// [`Vec::into_raw_parts`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts)
/// .
fn vec_into_raw_parts<T>(v: Vec<T>) -> (*mut T, usize, usize) {
    let mut me = std::mem::ManuallyDrop::new(v);
    (me.as_mut_ptr(), me.len(), me.capacity())
}
/// A structure for owning unused memory for padding.
///
/// A wrapper around an uninitialized `N` element array of `u8`s (`MaybeUninit<[u8;N]>` constructed
/// with `Self(MaybeUninit::uninit())`).
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Padding<const N: usize>(MaybeUninit<[u8; N]>);
impl<const N: usize> Default for Padding<N> {
    fn default() -> Self {
        Self(MaybeUninit::uninit())
    }
}
impl<const N: usize> serde::Serialize for Padding<N> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_unit_struct("Padding")
    }
}
impl<'de, const N: usize> serde::Deserialize<'de> for Padding<N> {
    fn deserialize<D>(_deserializer: D) -> Result<Padding<N>, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Ok(Padding(MaybeUninit::uninit()))
    }
}
impl<const N: usize> PartialEq for Padding<N> {
    fn eq(&self, _other: &Self) -> bool {
        true
    }
}
impl<const N: usize> Eq for Padding<N> {}

/// CPUID entry (a mimic of <https://elixir.bootlin.com/linux/v5.10.129/source/arch/x86/include/uapi/asm/kvm.h#L232>).
#[derive(Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize)]
#[repr(C)]
pub struct RawCpuidEntry {
    /// CPUID function (leaf).
    pub function: u32,
    /// CPUID index (subleaf).
    pub index: u32,
    /// TODO
    pub flags: u32,
    /// EAX register.
    pub eax: u32,
    /// EBX register.
    pub ebx: u32,
    /// ECX register.
    pub ecx: u32,
    /// EDX register.
    pub edx: u32,
    /// CPUID entry padding.
    pub padding: Padding<{ size_of::<[u32; 3]>() }>,
}
impl From<RawCpuidEntry> for (u32, u32, u32, u32) {
    fn from(this: RawCpuidEntry) -> Self {
        (this.eax, this.ebx, this.ecx, this.edx)
    }
}
impl fmt::LowerHex for RawCpuidEntry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("RawCpuidEntry")
            .field("function", &format!("{:x}", self.function))
            .field("index", &format!("{:x}", self.index))
            .field("eax", &format!("{:x}", self.eax))
            .field("ebx", &format!("{:x}", self.ebx))
            .field("ecx", &format!("{:x}", self.ecx))
            .field("edx", &format!("{:x}", self.edx))
            .finish()
    }
}

As currently used FamStruct is not a zero-cost abstraction (in this case the size of CpuId is larger than neccessary). This change would make it zero-cost.
I believe this is a plain improvement, simplifying the code, improving readability (in large part due to the simplification) and improving performance (decreasing memory usage), side affects of this change are currently not well known however and require furthers discussion.
I would advocate for the deprecation of FamStruct moving forward.
There are implementations we could use which would not make this a breaking change. I think this change could be well implemented without breaking anything (although to be sure would require implementing it).

Switch to rust-vmm-ci

Also add tests for plausible combination of features.

Right now the fam feature is not tested.

CI: Add build test for fam-wrapper feature

Once the PR #16 is merged, we will have tests for fam-wrappers only with the kernel bindings from v4.20.0.

We need to also add the following tests:

  • cargo test using the features fam-wrappers and the kernel bindings from 4.14
  • cargo build with fam-wrappers and the kernel bindings 4.14

Build error with rustc 1.30.0

With rustc 1.30.0 (the default on Ubuntu 18.04), the build fails with:

โžœ  kvm-bindings git:(master) cargo build --verbose
   Compiling kvm-bindings v0.1.0 (/home/local/ANT/jsteckli/src/kvm-bindings)
     Running `rustc --edition=2018 --crate-name kvm_bindings src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=cbcea0b2daf0e5b3 -C extra-filename=-cbcea0b2daf0e5b3 --out-dir /home/local/ANT/jsteckli/src/kvm-bindings/target/debug/deps -C incremental=/home/local/ANT/jsteckli/src/kvm-bindings/target/debug/incremental -L dependency=/home/local/ANT/jsteckli/src/kvm-bindings/target/debug/deps`
error: Edition 2018 is unstable and only available for nightly builds of rustc.                          
                                                                                                         
error: Could not compile `kvm-bindings`.                                                                 

Caused by:
  process didn't exit successfully: `rustc --edition=2018 --crate-name kvm_bindings src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=cbcea0b2daf0e5b3 -C extra-filename=-cbcea0b2daf0e5b3 --out-dir /home/local/ANT/jsteckli/src/kvm-bindings/target/debug/deps -C incremental=/home/local/ANT/jsteckli/src/kvm-bindings/target/debug/incremental -L dependency=/home/local/ANT/jsteckli/src/kvm-bindings/target/debug/deps` (exit code: 1)

Default implementations for default, debug not available

We are using FamWrapper for kvm_cpuid2, kvm_msrs, and kvm_msr_list. These structures have #[derive(Default, Debug)] macros which are pretty useful. We should make sure that their wrappers also have the derives as it makes the code nicer and easier to debug.

vmm-sys-utils dependency problem

The vmm-sys-utils used by kvm-bindings is vmm-sys-util = { version = ">=0.8.0", optional = true } and yesterday vmm-sys-utils upgraded to v0.11.0 from v0.10.0 causing the vmm-sys-util used by kvm-binding upgraded to v0.11.0 without user notice.

And Dragonball VMM use kvm-bindings with other crates which specify the vmm-sys-util version to 0.10.0 so that yesterday's upgrade of vmm-sys-utils caused the compile error of Dragonball.

Could we also specify the version for vmm-sys-utils in kvm-bindings(i.e. use caret dependencies )?

Add CODEOWNERS file

Please add a CODEOWNERS file with the details of the maintainers. Please use the following format as outlined in: https://help.github.com/en/articles/about-code-owners

Most projects can simply follow the wildcard syntax. e.g.

* @owner1 @owner2

Not only does this provide a way to see who is responsible or this repository they will also automatically be notified of incoming PR reviews.

Update CODEOWNERS file

The codeowners file is invalid. It currently lists only one maintainer, which is the gatekeeper pull requests assigner. This was a bot that we've used in the past but is no longer valid. We should add some maintainers to this repository as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.