mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 13:11:31 +00:00
hypha: move libraries to libs/
This commit is contained in:
20
libs/loopdev/Cargo.toml
Normal file
20
libs/loopdev/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
# This package is from https://github.com/stratis-storage/loopdev-3
|
||||
# Mycelium maintains an in-tree version because the goals of hypha mean that
|
||||
# there is as little binding generation as possible, especially bindings which
|
||||
# prevent development from macOS, like the original library.
|
||||
[package]
|
||||
name = "loopdev"
|
||||
version.workspace = true
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "loopdev"
|
||||
|
||||
[dependencies]
|
||||
errno = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
|
||||
[dependencies.nix]
|
||||
workspace = true
|
||||
features = ["ioctl"]
|
21
libs/loopdev/LICENSE
Normal file
21
libs/loopdev/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Anne Mulhern
|
||||
Copyright (c) 2016 Michael Daffin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
147
libs/loopdev/src/bindings.rs
Normal file
147
libs/loopdev/src/bindings.rs
Normal file
@ -0,0 +1,147 @@
|
||||
/* originally generated by rust-bindgen */
|
||||
/* modified to remove unused content by Mycelium */
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
pub const __BITS_PER_LONG: u32 = 64;
|
||||
pub const __FD_SETSIZE: u32 = 1024;
|
||||
pub const LOOP_SET_FD: u32 = 19456;
|
||||
pub const LOOP_CLR_FD: u32 = 19457;
|
||||
pub const LOOP_SET_STATUS64: u32 = 19460;
|
||||
pub const LOOP_SET_CAPACITY: u32 = 19463;
|
||||
pub const LOOP_CTL_ADD: u32 = 19584;
|
||||
pub const LOOP_CTL_GET_FREE: u32 = 19586;
|
||||
pub const LO_FLAGS_READ_ONLY: _bindgen_ty_1 = 1;
|
||||
pub const LO_FLAGS_AUTOCLEAR: _bindgen_ty_1 = 4;
|
||||
pub const LO_FLAGS_PARTSCAN: _bindgen_ty_1 = 8;
|
||||
pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
|
||||
pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
|
||||
pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
|
||||
pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
|
||||
pub type __kernel_long_t = ::std::os::raw::c_long;
|
||||
pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
|
||||
pub type __kernel_ino_t = __kernel_ulong_t;
|
||||
pub type __kernel_mode_t = ::std::os::raw::c_uint;
|
||||
pub type __kernel_pid_t = ::std::os::raw::c_int;
|
||||
pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
|
||||
pub type __kernel_uid_t = ::std::os::raw::c_uint;
|
||||
pub type __kernel_gid_t = ::std::os::raw::c_uint;
|
||||
pub type __kernel_suseconds_t = __kernel_long_t;
|
||||
pub type __kernel_daddr_t = ::std::os::raw::c_int;
|
||||
pub type __kernel_uid32_t = ::std::os::raw::c_uint;
|
||||
pub type __kernel_gid32_t = ::std::os::raw::c_uint;
|
||||
pub type __kernel_size_t = __kernel_ulong_t;
|
||||
pub type __kernel_ssize_t = __kernel_long_t;
|
||||
pub type __kernel_ptrdiff_t = __kernel_long_t;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct __kernel_fsid_t {
|
||||
pub val: [::std::os::raw::c_int; 2usize],
|
||||
}
|
||||
pub type __kernel_off_t = __kernel_long_t;
|
||||
pub type __kernel_loff_t = ::std::os::raw::c_longlong;
|
||||
pub type __kernel_old_time_t = __kernel_long_t;
|
||||
pub type __kernel_time_t = __kernel_long_t;
|
||||
pub type __kernel_time64_t = ::std::os::raw::c_longlong;
|
||||
pub type __kernel_clock_t = __kernel_long_t;
|
||||
pub type __kernel_timer_t = ::std::os::raw::c_int;
|
||||
pub type __kernel_clockid_t = ::std::os::raw::c_int;
|
||||
pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
|
||||
pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
|
||||
pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
|
||||
pub type __s8 = ::std::os::raw::c_schar;
|
||||
pub type __u8 = ::std::os::raw::c_uchar;
|
||||
pub type __s16 = ::std::os::raw::c_short;
|
||||
pub type __u16 = ::std::os::raw::c_ushort;
|
||||
pub type __s32 = ::std::os::raw::c_int;
|
||||
pub type __u32 = ::std::os::raw::c_uint;
|
||||
pub type __s64 = ::std::os::raw::c_longlong;
|
||||
pub type __u64 = ::std::os::raw::c_ulonglong;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct __kernel_fd_set {
|
||||
pub fds_bits: [::std::os::raw::c_ulong; 16usize],
|
||||
}
|
||||
|
||||
pub type __kernel_sighandler_t = Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
|
||||
pub type __kernel_key_t = ::std::os::raw::c_int;
|
||||
pub type __kernel_mqd_t = ::std::os::raw::c_int;
|
||||
pub type __le16 = __u16;
|
||||
pub type __be16 = __u16;
|
||||
pub type __le32 = __u32;
|
||||
pub type __be32 = __u32;
|
||||
pub type __le64 = __u64;
|
||||
pub type __be64 = __u64;
|
||||
pub type __sum16 = __u16;
|
||||
pub type __wsum = __u32;
|
||||
pub type __poll_t = ::std::os::raw::c_uint;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct loop_info {
|
||||
pub lo_number: ::std::os::raw::c_int,
|
||||
pub lo_device: __kernel_old_dev_t,
|
||||
pub lo_inode: ::std::os::raw::c_ulong,
|
||||
pub lo_rdevice: __kernel_old_dev_t,
|
||||
pub lo_offset: ::std::os::raw::c_int,
|
||||
pub lo_encrypt_type: ::std::os::raw::c_int,
|
||||
pub lo_encrypt_key_size: ::std::os::raw::c_int,
|
||||
pub lo_flags: ::std::os::raw::c_int,
|
||||
pub lo_name: [::std::os::raw::c_char; 64usize],
|
||||
pub lo_encrypt_key: [::std::os::raw::c_uchar; 32usize],
|
||||
pub lo_init: [::std::os::raw::c_ulong; 2usize],
|
||||
pub reserved: [::std::os::raw::c_char; 4usize],
|
||||
}
|
||||
|
||||
impl Default for loop_info {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct loop_info64 {
|
||||
pub lo_device: __u64,
|
||||
pub lo_inode: __u64,
|
||||
pub lo_rdevice: __u64,
|
||||
pub lo_offset: __u64,
|
||||
pub lo_sizelimit: __u64,
|
||||
pub lo_number: __u32,
|
||||
pub lo_encrypt_type: __u32,
|
||||
pub lo_encrypt_key_size: __u32,
|
||||
pub lo_flags: __u32,
|
||||
pub lo_file_name: [__u8; 64usize],
|
||||
pub lo_crypt_name: [__u8; 64usize],
|
||||
pub lo_encrypt_key: [__u8; 32usize],
|
||||
pub lo_init: [__u64; 2usize],
|
||||
}
|
||||
|
||||
impl Default for loop_info64 {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct loop_config {
|
||||
pub fd: __u32,
|
||||
pub block_size: __u32,
|
||||
pub info: loop_info64,
|
||||
pub __reserved: [__u64; 8usize],
|
||||
}
|
||||
|
||||
impl Default for loop_config {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
475
libs/loopdev/src/lib.rs
Normal file
475
libs/loopdev/src/lib.rs
Normal file
@ -0,0 +1,475 @@
|
||||
// Taken from https://github.com/stratis-storage/loopdev-3/blob/master/src/lib.rs
|
||||
// Licensed under MIT.
|
||||
|
||||
//! Setup and control loop devices.
|
||||
//!
|
||||
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Default options:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use loopdev::LoopControl;
|
||||
//! let lc = LoopControl::open().unwrap();
|
||||
//! let ld = lc.next_free().unwrap();
|
||||
//!
|
||||
//! println!("{}", ld.path().unwrap().display());
|
||||
//!
|
||||
//! ld.attach_file("disk.img").unwrap();
|
||||
//! // ...
|
||||
//! ld.detach().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Custom options:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use loopdev::LoopControl;
|
||||
//! # let lc = LoopControl::open().unwrap();
|
||||
//! # let ld = lc.next_free().unwrap();
|
||||
//! #
|
||||
//! ld.with()
|
||||
//! .part_scan(true)
|
||||
//! .offset(512 * 1024 * 1024) // 512 MiB
|
||||
//! .size_limit(1024 * 1024 * 1024) // 1GiB
|
||||
//! .attach("disk.img").unwrap();
|
||||
//! // ...
|
||||
//! ld.detach().unwrap();
|
||||
//! ```
|
||||
mod bindings;
|
||||
mod linux;
|
||||
|
||||
use crate::bindings::{
|
||||
loop_info64, LOOP_CLR_FD, LOOP_CTL_ADD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD,
|
||||
LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR, LO_FLAGS_PARTSCAN, LO_FLAGS_READ_ONLY,
|
||||
};
|
||||
use libc::ioctl;
|
||||
use std::ffi::{c_int, c_ulong};
|
||||
use std::{
|
||||
default::Default,
|
||||
fs::{File, OpenOptions},
|
||||
io,
|
||||
os::unix::prelude::*,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
|
||||
type IoctlRequest = c_ulong;
|
||||
#[cfg(any(target_os = "android", target_env = "musl"))]
|
||||
type IoctlRequest = c_int;
|
||||
|
||||
const LOOP_CONTROL: &str = "/dev/loop-control";
|
||||
#[cfg(not(target_os = "android"))]
|
||||
const LOOP_PREFIX: &str = "/dev/loop";
|
||||
#[cfg(target_os = "android")]
|
||||
const LOOP_PREFIX: &str = "/dev/block/loop";
|
||||
|
||||
/// Interface to the loop control device: `/dev/loop-control`.
|
||||
#[derive(Debug)]
|
||||
pub struct LoopControl {
|
||||
dev_file: File,
|
||||
}
|
||||
|
||||
impl LoopControl {
|
||||
/// Opens the loop control device.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons when opening
|
||||
/// the loop control file `/dev/loop-control`. See
|
||||
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
|
||||
/// for further details.
|
||||
pub fn open() -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
dev_file: OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(LOOP_CONTROL)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Finds and opens the next available loop device.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopControl;
|
||||
/// let lc = LoopControl::open().unwrap();
|
||||
/// let ld = lc.next_free().unwrap();
|
||||
/// println!("{}", ld.path().unwrap().display());
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons when opening
|
||||
/// the loop device file `/dev/loopX`. See
|
||||
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
|
||||
/// for further details.
|
||||
pub fn next_free(&self) -> io::Result<LoopDevice> {
|
||||
let dev_num = ioctl_to_error(unsafe {
|
||||
ioctl(
|
||||
self.dev_file.as_raw_fd() as c_int,
|
||||
LOOP_CTL_GET_FREE as IoctlRequest,
|
||||
)
|
||||
})?;
|
||||
LoopDevice::open(format!("{}{}", LOOP_PREFIX, dev_num))
|
||||
}
|
||||
|
||||
/// Add and opens a new loop device.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopControl;
|
||||
/// let lc = LoopControl::open().unwrap();
|
||||
/// let ld = lc.add(1).unwrap();
|
||||
/// println!("{}", ld.path().unwrap().display());
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This funcitons will return an error when a loop device with the passed
|
||||
/// number exists or opening the newly created device fails.
|
||||
pub fn add(&self, n: u32) -> io::Result<LoopDevice> {
|
||||
let dev_num = ioctl_to_error(unsafe {
|
||||
ioctl(
|
||||
self.dev_file.as_raw_fd() as c_int,
|
||||
LOOP_CTL_ADD as IoctlRequest,
|
||||
n as c_int,
|
||||
)
|
||||
})?;
|
||||
LoopDevice::open(format!("{}{}", LOOP_PREFIX, dev_num))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for LoopControl {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.dev_file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for LoopControl {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.dev_file.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to a loop device ie `/dev/loop0`.
|
||||
#[derive(Debug)]
|
||||
pub struct LoopDevice {
|
||||
device: File,
|
||||
}
|
||||
|
||||
impl AsRawFd for LoopDevice {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.device.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for LoopDevice {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.device.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl LoopDevice {
|
||||
/// Opens a loop device.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons when opening
|
||||
/// the given loop device file. See
|
||||
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
|
||||
/// for further details.
|
||||
pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
|
||||
// TODO create dev if it does not exist and begins with LOOP_PREFIX
|
||||
Ok(Self {
|
||||
device: OpenOptions::new().read(true).write(true).open(dev)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Attach the loop device to a file with given options.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Attach the device to a file.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopDevice;
|
||||
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
|
||||
/// ld.with().part_scan(true).attach("disk.img").unwrap();
|
||||
/// # ld.detach().unwrap();
|
||||
/// ```
|
||||
pub fn with(&self) -> AttachOptions<'_> {
|
||||
AttachOptions {
|
||||
device: self,
|
||||
info: loop_info64::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attach the loop device to a file that maps to the whole file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Attach the device to a file.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopDevice;
|
||||
/// let ld = LoopDevice::open("/dev/loop0").unwrap();
|
||||
/// ld.attach_file("disk.img").unwrap();
|
||||
/// # ld.detach().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons. Either when
|
||||
/// opening the backing file (see
|
||||
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
|
||||
/// for further details) or when calling the ioctl to attach the backing
|
||||
/// file to the device.
|
||||
pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
|
||||
let info = loop_info64 {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Self::attach_with_loop_info(self, backing_file, info)
|
||||
}
|
||||
|
||||
/// Attach the loop device to a file with `loop_info64`.
|
||||
fn attach_with_loop_info(
|
||||
&self, // TODO should be mut? - but changing it is a breaking change
|
||||
backing_file: impl AsRef<Path>,
|
||||
info: loop_info64,
|
||||
) -> io::Result<()> {
|
||||
let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;
|
||||
let bf = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(write_access)
|
||||
.open(backing_file)?;
|
||||
self.attach_fd_with_loop_info(bf, info)
|
||||
}
|
||||
|
||||
/// Attach the loop device to a fd with `loop_info`.
|
||||
fn attach_fd_with_loop_info(&self, bf: impl AsRawFd, info: loop_info64) -> io::Result<()> {
|
||||
// Attach the file
|
||||
ioctl_to_error(unsafe {
|
||||
ioctl(
|
||||
self.device.as_raw_fd() as c_int,
|
||||
LOOP_SET_FD as IoctlRequest,
|
||||
bf.as_raw_fd() as c_int,
|
||||
)
|
||||
})?;
|
||||
|
||||
let result = unsafe {
|
||||
ioctl(
|
||||
self.device.as_raw_fd() as c_int,
|
||||
LOOP_SET_STATUS64 as IoctlRequest,
|
||||
&info,
|
||||
)
|
||||
};
|
||||
match ioctl_to_error(result) {
|
||||
Err(err) => {
|
||||
// Ignore the error to preserve the original error
|
||||
let _detach_err = self.detach();
|
||||
Err(err)
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the path of the loop device.
|
||||
pub fn path(&self) -> Option<PathBuf> {
|
||||
let mut p = PathBuf::from("/proc/self/fd");
|
||||
p.push(self.device.as_raw_fd().to_string());
|
||||
std::fs::read_link(&p).ok()
|
||||
}
|
||||
|
||||
/// Get the device major number
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function needs to stat the backing file and can fail if there is
|
||||
/// an IO error.
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
pub fn major(&self) -> io::Result<u32> {
|
||||
self.device
|
||||
.metadata()
|
||||
.map(|m| linux::major(m.rdev()))
|
||||
.map(|m| m as u32)
|
||||
}
|
||||
|
||||
/// Get the device major number
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function needs to stat the backing file and can fail if there is
|
||||
/// an IO error.
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
pub fn minor(&self) -> io::Result<u32> {
|
||||
self.device
|
||||
.metadata()
|
||||
.map(|m| linux::minor(m.rdev()))
|
||||
.map(|m| m as u32)
|
||||
}
|
||||
|
||||
/// Detach a loop device from its backing file.
|
||||
///
|
||||
/// Note that the device won't fully detach until a short delay after the underling device file
|
||||
/// gets closed. This happens when `LoopDev` goes out of scope so you should ensure the `LoopDev`
|
||||
/// lives for a short a time as possible.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopDevice;
|
||||
/// let ld = LoopDevice::open("/dev/loop0").unwrap();
|
||||
/// # ld.attach_file("disk.img").unwrap();
|
||||
/// ld.detach().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons when calling the
|
||||
/// ioctl to detach the backing file from the device.
|
||||
pub fn detach(&self) -> io::Result<()> {
|
||||
ioctl_to_error(unsafe {
|
||||
ioctl(
|
||||
self.device.as_raw_fd() as c_int,
|
||||
LOOP_CLR_FD as IoctlRequest,
|
||||
0,
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resize a live loop device. If the size of the backing file changes this can be called to
|
||||
/// inform the loop driver about the new size.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons when calling the
|
||||
/// ioctl to set the capacity of the device.
|
||||
pub fn set_capacity(&self) -> io::Result<()> {
|
||||
ioctl_to_error(unsafe {
|
||||
ioctl(
|
||||
self.device.as_raw_fd() as c_int,
|
||||
LOOP_SET_CAPACITY as IoctlRequest,
|
||||
0,
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to set options when attaching a device. Created with [`LoopDevice::with`()].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Enable partition scanning on attach:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopDevice;
|
||||
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
|
||||
/// ld.with()
|
||||
/// .part_scan(true)
|
||||
/// .attach("disk.img")
|
||||
/// .unwrap();
|
||||
/// # ld.detach().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// A 1MiB slice of the file located at 1KiB into the file.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use loopdev::LoopDevice;
|
||||
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
|
||||
/// ld.with()
|
||||
/// .offset(1024*1024)
|
||||
/// .size_limit(1024*1024*1024)
|
||||
/// .attach("disk.img")
|
||||
/// .unwrap();
|
||||
/// # ld.detach().unwrap();
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub struct AttachOptions<'d> {
|
||||
device: &'d LoopDevice,
|
||||
info: loop_info64,
|
||||
}
|
||||
|
||||
impl AttachOptions<'_> {
|
||||
/// Offset in bytes from the start of the backing file the data will start at.
|
||||
pub fn offset(mut self, offset: u64) -> Self {
|
||||
self.info.lo_offset = offset;
|
||||
self
|
||||
}
|
||||
|
||||
/// Maximum size of the data in bytes.
|
||||
pub fn size_limit(mut self, size_limit: u64) -> Self {
|
||||
self.info.lo_sizelimit = size_limit;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set read only flag
|
||||
pub fn read_only(mut self, read_only: bool) -> Self {
|
||||
if read_only {
|
||||
self.info.lo_flags |= LO_FLAGS_READ_ONLY;
|
||||
} else {
|
||||
self.info.lo_flags &= !LO_FLAGS_READ_ONLY;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Set autoclear flag
|
||||
pub fn autoclear(mut self, autoclear: bool) -> Self {
|
||||
if autoclear {
|
||||
self.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
|
||||
} else {
|
||||
self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Force the kernel to scan the partition table on a newly created loop device. Note that the
|
||||
/// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
|
||||
pub fn part_scan(mut self, enable: bool) -> Self {
|
||||
if enable {
|
||||
self.info.lo_flags |= LO_FLAGS_PARTSCAN;
|
||||
} else {
|
||||
self.info.lo_flags &= !LO_FLAGS_PARTSCAN;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach the loop device to a file with the set options.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons. Either when
|
||||
/// opening the backing file (see
|
||||
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
|
||||
/// for further details) or when calling the ioctl to attach the backing
|
||||
/// file to the device.
|
||||
pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
|
||||
self.device.attach_with_loop_info(backing_file, self.info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attach the loop device to an fd
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error for various reasons when calling the
|
||||
/// ioctl to attach the backing file to the device.
|
||||
pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> {
|
||||
self.device
|
||||
.attach_fd_with_loop_info(backing_file_fd, self.info)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn ioctl_to_error(ret: i32) -> io::Result<i32> {
|
||||
if ret < 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
15
libs/loopdev/src/linux.rs
Normal file
15
libs/loopdev/src/linux.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use std::ffi::c_uint;
|
||||
|
||||
pub fn major(dev: u64) -> c_uint {
|
||||
let mut major = 0;
|
||||
major |= (dev & 0x00000000000fff00) >> 8;
|
||||
major |= (dev & 0xfffff00000000000) >> 32;
|
||||
major as c_uint
|
||||
}
|
||||
|
||||
pub fn minor(dev: u64) -> c_uint {
|
||||
let mut minor = 0;
|
||||
minor |= dev & 0x00000000000000ff;
|
||||
minor |= (dev & 0x00000ffffff00000) >> 12;
|
||||
minor as c_uint
|
||||
}
|
37
libs/xen/xencall/Cargo.toml
Normal file
37
libs/xen/xencall/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "xencall"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
||||
[dependencies.nix]
|
||||
workspace = true
|
||||
features = ["ioctl"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { workspace = true }
|
||||
|
||||
[[example]]
|
||||
name = "xencall-domain-info"
|
||||
path = "examples/domain_info.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xencall-domain-create"
|
||||
path = "examples/domain_create.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xencall-version-capabilities"
|
||||
path = "examples/version_capabilities.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xencall-vcpu-context"
|
||||
path = "examples/vcpu_context.rs"
|
12
libs/xen/xencall/examples/domain_create.rs
Normal file
12
libs/xen/xencall/examples/domain_create.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::sys::CreateDomain;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open()?;
|
||||
let domid = call.create_domain(CreateDomain::default())?;
|
||||
println!("created domain {}", domid);
|
||||
Ok(())
|
||||
}
|
11
libs/xen/xencall/examples/domain_info.rs
Normal file
11
libs/xen/xencall/examples/domain_info.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open()?;
|
||||
let info = call.get_domain_info(1)?;
|
||||
println!("{:?}", info);
|
||||
Ok(())
|
||||
}
|
11
libs/xen/xencall/examples/vcpu_context.rs
Normal file
11
libs/xen/xencall/examples/vcpu_context.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open()?;
|
||||
let context = call.get_vcpu_context(224, 0)?;
|
||||
println!("{:?}", context);
|
||||
Ok(())
|
||||
}
|
11
libs/xen/xencall/examples/version_capabilities.rs
Normal file
11
libs/xen/xencall/examples/version_capabilities.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use xencall::error::Result;
|
||||
use xencall::XenCall;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let call = XenCall::open()?;
|
||||
let info = call.get_version_capabilities()?;
|
||||
println!("{:?}", info);
|
||||
Ok(())
|
||||
}
|
13
libs/xen/xencall/src/error.rs
Normal file
13
libs/xen/xencall/src/error.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use std::io;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("kernel error")]
|
||||
Kernel(#[from] nix::errno::Errno),
|
||||
#[error("io issue encountered")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("populate physmap failed")]
|
||||
PopulatePhysmapFailed,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
624
libs/xen/xencall/src/lib.rs
Normal file
624
libs/xen/xencall/src/lib.rs
Normal file
@ -0,0 +1,624 @@
|
||||
pub mod error;
|
||||
pub mod sys;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sys::{
|
||||
AddressSize, ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, DomCtlVcpuContext,
|
||||
EvtChnAllocUnbound, GetDomainInfo, GetPageFrameInfo3, Hypercall, HypercallInit, MaxMem,
|
||||
MaxVcpus, MemoryMap, MemoryReservation, MmapBatch, MmapResource, MmuExtOp, MultiCallEntry,
|
||||
VcpuGuestContext, VcpuGuestContextAny, XenCapabilitiesInfo, HYPERVISOR_DOMCTL,
|
||||
HYPERVISOR_EVENT_CHANNEL_OP, HYPERVISOR_MEMORY_OP, HYPERVISOR_MMUEXT_OP, HYPERVISOR_MULTICALL,
|
||||
HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN,
|
||||
XEN_DOMCTL_GETDOMAININFO, XEN_DOMCTL_GETPAGEFRAMEINFO3, XEN_DOMCTL_GETVCPUCONTEXT,
|
||||
XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_INTERFACE_VERSION, XEN_DOMCTL_MAX_MEM,
|
||||
XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_PAUSEDOMAIN, XEN_DOMCTL_SETVCPUCONTEXT,
|
||||
XEN_DOMCTL_SET_ADDRESS_SIZE, XEN_DOMCTL_UNPAUSEDOMAIN, XEN_MEM_CLAIM_PAGES, XEN_MEM_MEMORY_MAP,
|
||||
XEN_MEM_POPULATE_PHYSMAP,
|
||||
};
|
||||
use libc::{c_int, mmap, usleep, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
|
||||
use log::trace;
|
||||
use nix::errno::Errno;
|
||||
use std::ffi::{c_long, c_uint, c_ulong, c_void};
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::ptr::addr_of_mut;
|
||||
use std::slice;
|
||||
|
||||
pub struct XenCall {
|
||||
pub handle: File,
|
||||
}
|
||||
|
||||
impl XenCall {
|
||||
pub fn open() -> Result<XenCall> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/xen/privcmd")?;
|
||||
Ok(XenCall { handle: file })
|
||||
}
|
||||
|
||||
pub fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
||||
trace!(
|
||||
"call fd={} mmap addr={:#x} len={}",
|
||||
self.handle.as_raw_fd(),
|
||||
addr,
|
||||
len
|
||||
);
|
||||
unsafe {
|
||||
let ptr = mmap(
|
||||
addr as *mut c_void,
|
||||
len as usize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
self.handle.as_raw_fd(),
|
||||
0,
|
||||
);
|
||||
if ptr == MAP_FAILED {
|
||||
None
|
||||
} else {
|
||||
Some(ptr as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result<c_long> {
|
||||
trace!(
|
||||
"call fd={} hypercall op={:#x}, arg={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
op,
|
||||
arg
|
||||
);
|
||||
unsafe {
|
||||
let mut call = Hypercall { op, arg };
|
||||
let result = sys::hypercall(self.handle.as_raw_fd(), &mut call)?;
|
||||
Ok(result as c_long)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hypercall0(&self, op: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn hypercall1(&self, op: c_ulong, arg1: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn hypercall2(&self, op: c_ulong, arg1: c_ulong, arg2: c_ulong) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, 0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn hypercall3(
|
||||
&self,
|
||||
op: c_ulong,
|
||||
arg1: c_ulong,
|
||||
arg2: c_ulong,
|
||||
arg3: c_ulong,
|
||||
) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, arg3, 0, 0])
|
||||
}
|
||||
|
||||
pub fn hypercall4(
|
||||
&self,
|
||||
op: c_ulong,
|
||||
arg1: c_ulong,
|
||||
arg2: c_ulong,
|
||||
arg3: c_ulong,
|
||||
arg4: c_ulong,
|
||||
) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, arg3, arg4, 0])
|
||||
}
|
||||
|
||||
pub fn hypercall5(
|
||||
&self,
|
||||
op: c_ulong,
|
||||
arg1: c_ulong,
|
||||
arg2: c_ulong,
|
||||
arg3: c_ulong,
|
||||
arg4: c_ulong,
|
||||
arg5: c_ulong,
|
||||
) -> Result<c_long> {
|
||||
self.hypercall(op, [arg1, arg2, arg3, arg4, arg5])
|
||||
}
|
||||
|
||||
pub fn multicall(&self, calls: &mut [MultiCallEntry]) -> Result<()> {
|
||||
trace!(
|
||||
"call fd={} multicall calls={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
calls
|
||||
);
|
||||
self.hypercall2(
|
||||
HYPERVISOR_MULTICALL,
|
||||
calls.as_mut_ptr() as c_ulong,
|
||||
calls.len() as c_ulong,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn map_resource(
|
||||
&self,
|
||||
domid: u32,
|
||||
typ: u32,
|
||||
id: u32,
|
||||
idx: u32,
|
||||
num: u64,
|
||||
addr: u64,
|
||||
) -> Result<()> {
|
||||
let mut resource = MmapResource {
|
||||
dom: domid as u16,
|
||||
typ,
|
||||
id,
|
||||
idx,
|
||||
num,
|
||||
addr,
|
||||
};
|
||||
unsafe {
|
||||
sys::mmap_resource(self.handle.as_raw_fd(), &mut resource)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mmap_batch(&self, domid: u32, num: u64, addr: u64, mfns: Vec<u64>) -> Result<c_long> {
|
||||
trace!(
|
||||
"call fd={} mmap_batch domid={} num={} addr={:#x} mfns={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
num,
|
||||
addr,
|
||||
mfns
|
||||
);
|
||||
unsafe {
|
||||
let mut mfns = mfns.clone();
|
||||
let mut errors = vec![0i32; mfns.len()];
|
||||
let mut batch = MmapBatch {
|
||||
num: num as u32,
|
||||
domid: domid as u16,
|
||||
addr,
|
||||
mfns: mfns.as_mut_ptr(),
|
||||
errors: errors.as_mut_ptr(),
|
||||
};
|
||||
|
||||
let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch);
|
||||
if let Err(errno) = result {
|
||||
if errno != Errno::ENOENT {
|
||||
return Err(errno)?;
|
||||
}
|
||||
|
||||
usleep(100);
|
||||
|
||||
let mut i: usize = 0;
|
||||
let mut paged: usize = 0;
|
||||
loop {
|
||||
if errors[i] != libc::ENOENT {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
paged += 1;
|
||||
let mut batch = MmapBatch {
|
||||
num: 1,
|
||||
domid: domid as u16,
|
||||
addr: addr + ((i as u64) << 12),
|
||||
mfns: mfns.as_mut_ptr().add(i),
|
||||
errors: errors.as_mut_ptr().add(i),
|
||||
};
|
||||
|
||||
loop {
|
||||
i += 1;
|
||||
if i < num as usize {
|
||||
if errors[i] != libc::ENOENT {
|
||||
break;
|
||||
}
|
||||
batch.num += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch);
|
||||
if let Err(n) = result {
|
||||
if n != Errno::ENOENT {
|
||||
return Err(n)?;
|
||||
}
|
||||
}
|
||||
|
||||
if i < num as usize {
|
||||
break;
|
||||
}
|
||||
|
||||
let count = result.unwrap();
|
||||
if count <= 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(paged as c_long);
|
||||
}
|
||||
Ok(result.unwrap() as c_long)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_version_capabilities(&self) -> Result<XenCapabilitiesInfo> {
|
||||
trace!(
|
||||
"call fd={} get_version_capabilities",
|
||||
self.handle.as_raw_fd()
|
||||
);
|
||||
let mut info = XenCapabilitiesInfo {
|
||||
capabilities: [0; 1024],
|
||||
};
|
||||
self.hypercall2(
|
||||
HYPERVISOR_XEN_VERSION,
|
||||
XENVER_CAPABILITIES,
|
||||
addr_of_mut!(info) as c_ulong,
|
||||
)?;
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
pub fn evtchn_op(&self, cmd: c_int, arg: u64) -> Result<()> {
|
||||
self.hypercall2(HYPERVISOR_EVENT_CHANNEL_OP, cmd as c_ulong, arg)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn evtchn_alloc_unbound(&self, domid: u32, remote_domid: u32) -> Result<u32> {
|
||||
let mut alloc_unbound = EvtChnAllocUnbound {
|
||||
dom: domid as u16,
|
||||
remote_dom: remote_domid as u16,
|
||||
port: 0,
|
||||
};
|
||||
self.evtchn_op(6, addr_of_mut!(alloc_unbound) as c_ulong)?;
|
||||
Ok(alloc_unbound.port)
|
||||
}
|
||||
|
||||
pub fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo> {
|
||||
trace!(
|
||||
"domctl fd={} get_domain_info domid={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_GETDOMAININFO,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
get_domain_info: GetDomainInfo {
|
||||
domid: 0,
|
||||
pad1: 0,
|
||||
flags: 0,
|
||||
total_pages: 0,
|
||||
max_pages: 0,
|
||||
outstanding_pages: 0,
|
||||
shr_pages: 0,
|
||||
paged_pages: 0,
|
||||
shared_info_frame: 0,
|
||||
cpu_time: 0,
|
||||
number_online_vcpus: 0,
|
||||
max_vcpu_id: 0,
|
||||
ssidref: 0,
|
||||
handle: [0; 16],
|
||||
cpupool: 0,
|
||||
gpaddr_bits: 0,
|
||||
pad2: [0; 7],
|
||||
arch: ArchDomainConfig {
|
||||
emulation_flags: 0,
|
||||
misc_flags: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(unsafe { domctl.value.get_domain_info })
|
||||
}
|
||||
|
||||
pub fn create_domain(&self, create_domain: CreateDomain) -> Result<u32> {
|
||||
trace!(
|
||||
"domctl fd={} create_domain create_domain={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
create_domain
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_CREATEDOMAIN,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid: 0,
|
||||
value: DomCtlValue { create_domain },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(domctl.domid)
|
||||
}
|
||||
|
||||
pub fn pause_domain(&self, domid: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} pause_domain domid={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_PAUSEDOMAIN,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue { pad: [0; 128] },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpause_domain(&self, domid: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} unpause_domain domid={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_UNPAUSEDOMAIN,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue { pad: [0; 128] },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_max_mem(&self, domid: u32, memkb: u64) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_max_mem domid={} memkb={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
memkb
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_MAX_MEM,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
max_mem: MaxMem { max_memkb: memkb },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_max_vcpus(&self, domid: u32, max_vcpus: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_max_vcpus domid={} max_vcpus={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
max_vcpus
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_MAX_VCPUS,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
max_cpus: MaxVcpus { max_vcpus },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_address_size(&self, domid: u32, size: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_address_size domid={} size={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
size,
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_SET_ADDRESS_SIZE,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
address_size: AddressSize { size },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_vcpu_context(&self, domid: u32, vcpu: u32) -> Result<VcpuGuestContext> {
|
||||
trace!(
|
||||
"domctl fd={} get_vcpu_context domid={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
);
|
||||
let mut wrapper = VcpuGuestContextAny {
|
||||
value: VcpuGuestContext::default(),
|
||||
};
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_GETVCPUCONTEXT,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
vcpu_context: DomCtlVcpuContext {
|
||||
vcpu,
|
||||
ctx: addr_of_mut!(wrapper) as c_ulong,
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(unsafe { wrapper.value })
|
||||
}
|
||||
|
||||
pub fn set_vcpu_context(
|
||||
&self,
|
||||
domid: u32,
|
||||
vcpu: u32,
|
||||
context: &VcpuGuestContext,
|
||||
) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} set_vcpu_context domid={} context={:?}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
context,
|
||||
);
|
||||
|
||||
let mut value = VcpuGuestContextAny { value: *context };
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_SETVCPUCONTEXT,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
vcpu_context: DomCtlVcpuContext {
|
||||
vcpu,
|
||||
ctx: addr_of_mut!(value) as c_ulong,
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_page_frame_info(&self, domid: u32, frames: &[u64]) -> Result<Vec<u64>> {
|
||||
let mut buffer: Vec<u64> = frames.to_vec();
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_GETPAGEFRAMEINFO3,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
get_page_frame_info: GetPageFrameInfo3 {
|
||||
num: buffer.len() as u64,
|
||||
array: buffer.as_mut_ptr() as c_ulong,
|
||||
},
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
let slice = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
domctl.value.get_page_frame_info.array as *mut u64,
|
||||
domctl.value.get_page_frame_info.num as usize,
|
||||
)
|
||||
};
|
||||
Ok(slice.to_vec())
|
||||
}
|
||||
|
||||
pub fn hypercall_init(&self, domid: u32, gmfn: u64) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} hypercall_init domid={} gmfn={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
gmfn
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_HYPERCALL_INIT,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue {
|
||||
hypercall_init: HypercallInit { gmfn },
|
||||
},
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn destroy_domain(&self, domid: u32) -> Result<()> {
|
||||
trace!(
|
||||
"domctl fd={} destroy_domain domid={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid
|
||||
);
|
||||
let mut domctl = DomCtl {
|
||||
cmd: XEN_DOMCTL_DESTROYDOMAIN,
|
||||
interface_version: XEN_DOMCTL_INTERFACE_VERSION,
|
||||
domid,
|
||||
value: DomCtlValue { pad: [0; 128] },
|
||||
};
|
||||
self.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_memory_map(&self, size_of_entry: usize) -> Result<Vec<u8>> {
|
||||
let mut memory_map = MemoryMap {
|
||||
count: 0,
|
||||
buffer: 0,
|
||||
};
|
||||
self.hypercall2(
|
||||
HYPERVISOR_MEMORY_OP,
|
||||
XEN_MEM_MEMORY_MAP as c_ulong,
|
||||
addr_of_mut!(memory_map) as c_ulong,
|
||||
)?;
|
||||
let mut buffer = vec![0u8; memory_map.count as usize * size_of_entry];
|
||||
memory_map.buffer = buffer.as_mut_ptr() as c_ulong;
|
||||
self.hypercall2(
|
||||
HYPERVISOR_MEMORY_OP,
|
||||
XEN_MEM_MEMORY_MAP as c_ulong,
|
||||
addr_of_mut!(memory_map) as c_ulong,
|
||||
)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn populate_physmap(
|
||||
&self,
|
||||
domid: u32,
|
||||
nr_extents: u64,
|
||||
extent_order: u32,
|
||||
mem_flags: u32,
|
||||
extent_starts: &[u64],
|
||||
) -> Result<Vec<u64>> {
|
||||
trace!("memory fd={} populate_physmap domid={} nr_extents={} extent_order={} mem_flags={} extent_starts={:?}", self.handle.as_raw_fd(), domid, nr_extents, extent_order, mem_flags, extent_starts);
|
||||
let mut extent_starts = extent_starts.to_vec();
|
||||
let ptr = extent_starts.as_mut_ptr();
|
||||
|
||||
let mut reservation = MemoryReservation {
|
||||
extent_start: ptr as c_ulong,
|
||||
nr_extents,
|
||||
extent_order,
|
||||
mem_flags,
|
||||
domid: domid as u16,
|
||||
};
|
||||
|
||||
let calls = &mut [MultiCallEntry {
|
||||
op: HYPERVISOR_MEMORY_OP,
|
||||
result: 0,
|
||||
args: [
|
||||
XEN_MEM_POPULATE_PHYSMAP as c_ulong,
|
||||
addr_of_mut!(reservation) as c_ulong,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
}];
|
||||
self.multicall(calls)?;
|
||||
let code = calls[0].result;
|
||||
if code > !0xfff {
|
||||
return Err(Error::PopulatePhysmapFailed);
|
||||
}
|
||||
if code as usize > extent_starts.len() {
|
||||
return Err(Error::PopulatePhysmapFailed);
|
||||
}
|
||||
let extents = extent_starts[0..code as usize].to_vec();
|
||||
Ok(extents)
|
||||
}
|
||||
|
||||
pub fn claim_pages(&self, domid: u32, pages: u64) -> Result<()> {
|
||||
trace!(
|
||||
"memory fd={} claim_pages domid={} pages={}",
|
||||
self.handle.as_raw_fd(),
|
||||
domid,
|
||||
pages
|
||||
);
|
||||
let mut reservation = MemoryReservation {
|
||||
extent_start: 0,
|
||||
nr_extents: pages,
|
||||
extent_order: 0,
|
||||
mem_flags: 0,
|
||||
domid: domid as u16,
|
||||
};
|
||||
self.hypercall2(
|
||||
HYPERVISOR_MEMORY_OP,
|
||||
XEN_MEM_CLAIM_PAGES as c_ulong,
|
||||
addr_of_mut!(reservation) as c_ulong,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mmuext(&self, domid: u32, cmd: c_uint, arg1: u64, arg2: u64) -> Result<()> {
|
||||
let mut ops = MmuExtOp { cmd, arg1, arg2 };
|
||||
|
||||
self.hypercall4(
|
||||
HYPERVISOR_MMUEXT_OP,
|
||||
addr_of_mut!(ops) as c_ulong,
|
||||
1,
|
||||
0,
|
||||
domid as c_ulong,
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
507
libs/xen/xencall/src/sys.rs
Normal file
507
libs/xen/xencall/src/sys.rs
Normal file
@ -0,0 +1,507 @@
|
||||
/// Handwritten hypercall bindings.
|
||||
use nix::ioctl_readwrite_bad;
|
||||
use std::ffi::{c_char, c_int, c_uint, c_ulong};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Hypercall {
|
||||
pub op: c_ulong,
|
||||
pub arg: [c_ulong; 5],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct MmapEntry {
|
||||
pub va: u64,
|
||||
pub mfn: u64,
|
||||
pub npages: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct MmapResource {
|
||||
pub dom: u16,
|
||||
pub typ: u32,
|
||||
pub id: u32,
|
||||
pub idx: u32,
|
||||
pub num: u64,
|
||||
pub addr: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MmapBatch {
|
||||
pub num: u32,
|
||||
pub domid: u16,
|
||||
pub addr: u64,
|
||||
pub mfns: *mut u64,
|
||||
pub errors: *mut c_int,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Mmap {
|
||||
pub num: c_int,
|
||||
pub dom: u16,
|
||||
pub entry: *mut MmapEntry,
|
||||
}
|
||||
|
||||
const IOCTL_PRIVCMD_HYPERCALL: u64 = 0x305000;
|
||||
const IOCTL_PRIVCMD_MMAP: u64 = 0x105002;
|
||||
const IOCTL_PRIVCMD_MMAPBATCH_V2: u64 = 0x205004;
|
||||
const IOCTL_PRIVCMD_MMAP_RESOURCE: u64 = 0x205007;
|
||||
|
||||
ioctl_readwrite_bad!(hypercall, IOCTL_PRIVCMD_HYPERCALL, Hypercall);
|
||||
ioctl_readwrite_bad!(mmap, IOCTL_PRIVCMD_MMAP, Mmap);
|
||||
ioctl_readwrite_bad!(mmapbatch, IOCTL_PRIVCMD_MMAPBATCH_V2, MmapBatch);
|
||||
ioctl_readwrite_bad!(mmap_resource, IOCTL_PRIVCMD_MMAP_RESOURCE, MmapResource);
|
||||
|
||||
pub const HYPERVISOR_SET_TRAP_TABLE: c_ulong = 0;
|
||||
pub const HYPERVISOR_MMU_UPDATE: c_ulong = 1;
|
||||
pub const HYPERVISOR_SET_GDT: c_ulong = 2;
|
||||
pub const HYPERVISOR_STACK_SWITCH: c_ulong = 3;
|
||||
pub const HYPERVISOR_SET_CALLBACKS: c_ulong = 4;
|
||||
pub const HYPERVISOR_FPU_TASKSWITCH: c_ulong = 5;
|
||||
pub const HYPERVISOR_SCHED_OP_COMPAT: c_ulong = 6;
|
||||
pub const HYPERVISOR_PLATFORM_OP: c_ulong = 7;
|
||||
pub const HYPERVISOR_SET_DEBUGREG: c_ulong = 8;
|
||||
pub const HYPERVISOR_GET_DEBUGREG: c_ulong = 9;
|
||||
pub const HYPERVISOR_UPDATE_DESCRIPTOR: c_ulong = 10;
|
||||
pub const HYPERVISOR_MEMORY_OP: c_ulong = 12;
|
||||
pub const HYPERVISOR_MULTICALL: c_ulong = 13;
|
||||
pub const HYPERVISOR_UPDATE_VA_MAPPING: c_ulong = 14;
|
||||
pub const HYPERVISOR_SET_TIMER_OP: c_ulong = 15;
|
||||
pub const HYPERVISOR_EVENT_CHANNEL_OP_COMPAT: c_ulong = 16;
|
||||
pub const HYPERVISOR_XEN_VERSION: c_ulong = 17;
|
||||
pub const HYPERVISOR_CONSOLE_IO: c_ulong = 18;
|
||||
pub const HYPERVISOR_PHYSDEV_OP_COMPAT: c_ulong = 19;
|
||||
pub const HYPERVISOR_GRANT_TABLE_OP: c_ulong = 20;
|
||||
pub const HYPERVISOR_VM_ASSIST: c_ulong = 21;
|
||||
pub const HYPERVISOR_UPDATE_VA_MAPPING_OTHERDOMAIN: c_ulong = 22;
|
||||
pub const HYPERVISOR_IRET: c_ulong = 23;
|
||||
pub const HYPERVISOR_VCPU_OP: c_ulong = 24;
|
||||
pub const HYPERVISOR_SET_SEGMENT_BASE: c_ulong = 25;
|
||||
pub const HYPERVISOR_MMUEXT_OP: c_ulong = 26;
|
||||
pub const HYPERVISOR_XSM_OP: c_ulong = 27;
|
||||
pub const HYPERVISOR_NMI_OP: c_ulong = 28;
|
||||
pub const HYPERVISOR_SCHED_OP: c_ulong = 29;
|
||||
pub const HYPERVISOR_CALLBACK_OP: c_ulong = 30;
|
||||
pub const HYPERVISOR_XENOPROF_OP: c_ulong = 31;
|
||||
pub const HYPERVISOR_EVENT_CHANNEL_OP: c_ulong = 32;
|
||||
pub const HYPERVISOR_PHYSDEV_OP: c_ulong = 33;
|
||||
pub const HYPERVISOR_HVM_OP: c_ulong = 34;
|
||||
pub const HYPERVISOR_SYSCTL: c_ulong = 35;
|
||||
pub const HYPERVISOR_DOMCTL: c_ulong = 36;
|
||||
pub const HYPERVISOR_KEXEC_OP: c_ulong = 37;
|
||||
pub const HYPERVISOR_TMEM_OP: c_ulong = 38;
|
||||
pub const HYPERVISOR_XC_RESERVED_OP: c_ulong = 39;
|
||||
pub const HYPERVISOR_XENPMU_OP: c_ulong = 40;
|
||||
pub const HYPERVISOR_DM_OP: c_ulong = 41;
|
||||
|
||||
pub const XEN_DOMCTL_CDF_HVM_GUEST: u32 = 1 << 0;
|
||||
pub const XEN_DOMCTL_CDF_HAP: u32 = 1 << 1;
|
||||
pub const XEN_DOMCTL_CDF_S3_INTEGRITY: u32 = 1 << 2;
|
||||
pub const XEN_DOMCTL_CDF_OOS_OFF: u32 = 1 << 3;
|
||||
pub const XEN_DOMCTL_CDF_XS_DOMAIN: u32 = 1 << 4;
|
||||
|
||||
pub const XEN_X86_EMU_LAPIC: u32 = 1 << 0;
|
||||
pub const XEN_X86_EMU_HPET: u32 = 1 << 1;
|
||||
pub const XEN_X86_EMU_PM: u32 = 1 << 2;
|
||||
pub const XEN_X86_EMU_RTC: u32 = 1 << 3;
|
||||
pub const XEN_X86_EMU_IOAPIC: u32 = 1 << 4;
|
||||
pub const XEN_X86_EMU_PIC: u32 = 1 << 5;
|
||||
pub const XEN_X86_EMU_VGA: u32 = 1 << 6;
|
||||
pub const XEN_X86_EMU_IOMMU: u32 = 1 << 7;
|
||||
pub const XEN_X86_EMU_PIT: u32 = 1 << 8;
|
||||
pub const XEN_X86_EMU_USE_PIRQ: u32 = 1 << 9;
|
||||
|
||||
pub const XEN_X86_EMU_ALL: u32 = XEN_X86_EMU_LAPIC
|
||||
| XEN_X86_EMU_HPET
|
||||
| XEN_X86_EMU_PM
|
||||
| XEN_X86_EMU_RTC
|
||||
| XEN_X86_EMU_IOAPIC
|
||||
| XEN_X86_EMU_PIC
|
||||
| XEN_X86_EMU_VGA
|
||||
| XEN_X86_EMU_IOMMU
|
||||
| XEN_X86_EMU_PIT
|
||||
| XEN_X86_EMU_USE_PIRQ;
|
||||
|
||||
pub const XEN_DOMCTL_CREATEDOMAIN: u32 = 1;
|
||||
pub const XEN_DOMCTL_DESTROYDOMAIN: u32 = 2;
|
||||
pub const XEN_DOMCTL_PAUSEDOMAIN: u32 = 3;
|
||||
pub const XEN_DOMCTL_UNPAUSEDOMAIN: u32 = 4;
|
||||
pub const XEN_DOMCTL_GETDOMAININFO: u32 = 5;
|
||||
pub const XEN_DOMCTL_GETMEMLIST: u32 = 6;
|
||||
pub const XEN_DOMCTL_SETVCPUAFFINITY: u32 = 9;
|
||||
pub const XEN_DOMCTL_SHADOW_OP: u32 = 10;
|
||||
pub const XEN_DOMCTL_MAX_MEM: u32 = 11;
|
||||
pub const XEN_DOMCTL_SETVCPUCONTEXT: u32 = 12;
|
||||
pub const XEN_DOMCTL_GETVCPUCONTEXT: u32 = 13;
|
||||
pub const XEN_DOMCTL_GETVCPUINFO: u32 = 14;
|
||||
pub const XEN_DOMCTL_MAX_VCPUS: u32 = 15;
|
||||
pub const XEN_DOMCTL_SCHEDULER_OP: u32 = 16;
|
||||
pub const XEN_DOMCTL_SETDOMAINHANDLE: u32 = 17;
|
||||
pub const XEN_DOMCTL_SETDEBUGGING: u32 = 18;
|
||||
pub const XEN_DOMCTL_IRQ_PERMISSION: u32 = 19;
|
||||
pub const XEN_DOMCTL_IOMEM_PERMISSION: u32 = 20;
|
||||
pub const XEN_DOMCTL_IOPORT_PERMISSION: u32 = 21;
|
||||
pub const XEN_DOMCTL_HYPERCALL_INIT: u32 = 22;
|
||||
pub const XEN_DOMCTL_SETTIMEOFFSET: u32 = 24;
|
||||
pub const XEN_DOMCTL_GETVCPUAFFINITY: u32 = 25;
|
||||
pub const XEN_DOMCTL_RESUMEDOMAIN: u32 = 27;
|
||||
pub const XEN_DOMCTL_SENDTRIGGER: u32 = 28;
|
||||
pub const XEN_DOMCTL_SUBSCRIBE: u32 = 29;
|
||||
pub const XEN_DOMCTL_GETHVMCONTEXT: u32 = 33;
|
||||
pub const XEN_DOMCTL_SETHVMCONTEXT: u32 = 34;
|
||||
pub const XEN_DOMCTL_SET_ADDRESS_SIZE: u32 = 35;
|
||||
pub const XEN_DOMCTL_GET_ADDRESS_SIZE: u32 = 36;
|
||||
pub const XEN_DOMCTL_ASSIGN_DEVICE: u32 = 37;
|
||||
pub const XEN_DOMCTL_BIND_PT_IRQ: u32 = 38;
|
||||
pub const XEN_DOMCTL_MEMORY_MAPPING: u32 = 39;
|
||||
pub const XEN_DOMCTL_IOPORT_MAPPING: u32 = 40;
|
||||
pub const XEN_DOMCTL_PIN_MEM_CACHEATTR: u32 = 41;
|
||||
pub const XEN_DOMCTL_SET_EXT_VCPUCONTEXT: u32 = 42;
|
||||
pub const XEN_DOMCTL_GET_EXT_VCPUCONTEXT: u32 = 43;
|
||||
pub const XEN_DOMCTL_TEST_ASSIGN_DEVICE: u32 = 45;
|
||||
pub const XEN_DOMCTL_SET_TARGET: u32 = 46;
|
||||
pub const XEN_DOMCTL_DEASSIGN_DEVICE: u32 = 47;
|
||||
pub const XEN_DOMCTL_UNBIND_PT_IRQ: u32 = 48;
|
||||
pub const XEN_DOMCTL_SET_CPUID: u32 = 49;
|
||||
pub const XEN_DOMCTL_GET_DEVICE_GROUP: u32 = 50;
|
||||
pub const XEN_DOMCTL_SET_MACHINE_ADDRESS_SIZE: u32 = 51;
|
||||
pub const XEN_DOMCTL_GET_MACHINE_ADDRESS_SIZE: u32 = 52;
|
||||
pub const XEN_DOMCTL_SUPPRESS_SPURIOUS_PAGE_FAULTS: u32 = 53;
|
||||
pub const XEN_DOMCTL_DEBUG_OP: u32 = 54;
|
||||
pub const XEN_DOMCTL_GETHVMCONTEXT_PARTIAL: u32 = 55;
|
||||
pub const XEN_DOMCTL_VM_EVENT_OP: u32 = 56;
|
||||
pub const XEN_DOMCTL_MEM_SHARING_OP: u32 = 57;
|
||||
pub const XEN_DOMCTL_DISABLE_MIGRATE: u32 = 58;
|
||||
pub const XEN_DOMCTL_GETTSCINFO: u32 = 59;
|
||||
pub const XEN_DOMCTL_SETTSCINFO: u32 = 60;
|
||||
pub const XEN_DOMCTL_GETPAGEFRAMEINFO3: u32 = 61;
|
||||
pub const XEN_DOMCTL_SETVCPUEXTSTATE: u32 = 62;
|
||||
pub const XEN_DOMCTL_GETVCPUEXTSTATE: u32 = 63;
|
||||
pub const XEN_DOMCTL_SET_ACCESS_REQUIRED: u32 = 64;
|
||||
pub const XEN_DOMCTL_AUDIT_P2M: u32 = 65;
|
||||
pub const XEN_DOMCTL_SET_VIRQ_HANDLER: u32 = 66;
|
||||
pub const XEN_DOMCTL_SET_BROKEN_PAGE_P2M: u32 = 67;
|
||||
pub const XEN_DOMCTL_SETNODEAFFINITY: u32 = 68;
|
||||
pub const XEN_DOMCTL_GETNODEAFFINITY: u32 = 69;
|
||||
pub const XEN_DOMCTL_SET_MAX_EVTCHN: u32 = 70;
|
||||
pub const XEN_DOMCTL_CACHEFLUSH: u32 = 71;
|
||||
pub const XEN_DOMCTL_GET_VCPU_MSRS: u32 = 72;
|
||||
pub const XEN_DOMCTL_SET_VCPU_MSRS: u32 = 73;
|
||||
pub const XEN_DOMCTL_SETVNUMAINFO: u32 = 74;
|
||||
pub const XEN_DOMCTL_PSR_CMT_OP: u32 = 75;
|
||||
pub const XEN_DOMCTL_MONITOR_OP: u32 = 77;
|
||||
pub const XEN_DOMCTL_PSR_CAT_OP: u32 = 78;
|
||||
pub const XEN_DOMCTL_SOFT_RESET: u32 = 79;
|
||||
pub const XEN_DOMCTL_SET_GNTTAB_LIMITS: u32 = 80;
|
||||
pub const XEN_DOMCTL_VUART_OP: u32 = 81;
|
||||
pub const XEN_DOMCTL_GDBSX_GUESTMEMIO: u32 = 1000;
|
||||
pub const XEN_DOMCTL_GDBSX_PAUSEVCPU: u32 = 1001;
|
||||
pub const XEN_DOMCTL_GDBSX_UNPAUSEVCPU: u32 = 1002;
|
||||
pub const XEN_DOMCTL_GDBSX_DOMSTATUS: u32 = 1003;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DomCtl {
|
||||
pub cmd: u32,
|
||||
pub interface_version: u32,
|
||||
pub domid: u32,
|
||||
pub value: DomCtlValue,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DomCtlVcpuContext {
|
||||
pub vcpu: u32,
|
||||
pub ctx: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct AddressSize {
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union DomCtlValue {
|
||||
pub create_domain: CreateDomain,
|
||||
pub get_domain_info: GetDomainInfo,
|
||||
pub max_mem: MaxMem,
|
||||
pub max_cpus: MaxVcpus,
|
||||
pub hypercall_init: HypercallInit,
|
||||
pub vcpu_context: DomCtlVcpuContext,
|
||||
pub address_size: AddressSize,
|
||||
pub get_page_frame_info: GetPageFrameInfo3,
|
||||
pub pad: [u8; 128],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct CreateDomain {
|
||||
pub ssidref: u32,
|
||||
pub handle: [u8; 16],
|
||||
pub flags: u32,
|
||||
pub iommu_opts: u32,
|
||||
pub max_vcpus: u32,
|
||||
pub max_evtchn_port: u32,
|
||||
pub max_grant_frames: i32,
|
||||
pub max_maptrack_frames: i32,
|
||||
pub grant_opts: u32,
|
||||
pub vmtrace_size: u32,
|
||||
pub cpupool_id: u32,
|
||||
pub arch_domain_config: ArchDomainConfig,
|
||||
}
|
||||
|
||||
impl Default for CreateDomain {
|
||||
fn default() -> Self {
|
||||
CreateDomain {
|
||||
ssidref: SECINITSID_DOMU,
|
||||
handle: Uuid::new_v4().into_bytes(),
|
||||
flags: 0,
|
||||
iommu_opts: 0,
|
||||
max_vcpus: 1,
|
||||
max_evtchn_port: 1023,
|
||||
max_grant_frames: -1,
|
||||
max_maptrack_frames: -1,
|
||||
grant_opts: 2,
|
||||
vmtrace_size: 0,
|
||||
cpupool_id: 0,
|
||||
arch_domain_config: ArchDomainConfig {
|
||||
emulation_flags: 0,
|
||||
misc_flags: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct GetDomainInfo {
|
||||
pub domid: u16,
|
||||
pub pad1: u16,
|
||||
pub flags: u32,
|
||||
pub total_pages: u64,
|
||||
pub max_pages: u64,
|
||||
pub outstanding_pages: u64,
|
||||
pub shr_pages: u64,
|
||||
pub paged_pages: u64,
|
||||
pub shared_info_frame: u64,
|
||||
pub cpu_time: u64,
|
||||
pub number_online_vcpus: u32,
|
||||
pub max_vcpu_id: u32,
|
||||
pub ssidref: u32,
|
||||
pub handle: [u8; 16],
|
||||
pub cpupool: u32,
|
||||
pub gpaddr_bits: u8,
|
||||
pub pad2: [u8; 7],
|
||||
pub arch: ArchDomainConfig,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct GetPageFrameInfo3 {
|
||||
pub num: u64,
|
||||
pub array: c_ulong,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ArchDomainConfig {
|
||||
pub emulation_flags: u32,
|
||||
pub misc_flags: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MaxMem {
|
||||
pub max_memkb: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MaxVcpus {
|
||||
pub max_vcpus: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct HypercallInit {
|
||||
pub gmfn: u64,
|
||||
}
|
||||
|
||||
pub const XEN_DOMCTL_INTERFACE_VERSION: u32 = 0x00000015;
|
||||
pub const SECINITSID_DOMU: u32 = 12;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct XenCapabilitiesInfo {
|
||||
pub capabilities: [c_char; 1024],
|
||||
}
|
||||
|
||||
pub const XENVER_CAPABILITIES: u64 = 3;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MemoryReservation {
|
||||
pub extent_start: c_ulong,
|
||||
pub nr_extents: c_ulong,
|
||||
pub extent_order: c_uint,
|
||||
pub mem_flags: c_uint,
|
||||
pub domid: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MultiCallEntry {
|
||||
pub op: c_ulong,
|
||||
pub result: c_ulong,
|
||||
pub args: [c_ulong; 6],
|
||||
}
|
||||
|
||||
pub const XEN_MEM_POPULATE_PHYSMAP: u32 = 6;
|
||||
pub const XEN_MEM_MEMORY_MAP: u32 = 9;
|
||||
pub const XEN_MEM_CLAIM_PAGES: u32 = 24;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MemoryMap {
|
||||
pub count: c_uint,
|
||||
pub buffer: c_ulong,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct VcpuGuestContextFpuCtx {
|
||||
pub x: [c_char; 512],
|
||||
}
|
||||
|
||||
impl Default for VcpuGuestContextFpuCtx {
|
||||
fn default() -> Self {
|
||||
VcpuGuestContextFpuCtx { x: [0; 512] }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct CpuUserRegs {
|
||||
pub r15: u64,
|
||||
pub r14: u64,
|
||||
pub r13: u64,
|
||||
pub r12: u64,
|
||||
pub rbp: u64,
|
||||
pub rbx: u64,
|
||||
pub r11: u64,
|
||||
pub r10: u64,
|
||||
pub r9: u64,
|
||||
pub r8: u64,
|
||||
pub rax: u64,
|
||||
pub rcx: u64,
|
||||
pub rdx: u64,
|
||||
pub rsi: u64,
|
||||
pub rdi: u64,
|
||||
pub error_code: u32,
|
||||
pub entry_vector: u32,
|
||||
pub rip: u64,
|
||||
pub cs: u16,
|
||||
_pad0: [u16; 1],
|
||||
pub saved_upcall_mask: u8,
|
||||
_pad1: [u8; 3],
|
||||
pub rflags: u64,
|
||||
pub rsp: u64,
|
||||
pub ss: u16,
|
||||
_pad2: [u16; 3],
|
||||
pub es: u16,
|
||||
_pad3: [u16; 3],
|
||||
pub ds: u16,
|
||||
_pad4: [u16; 3],
|
||||
pub fs: u16,
|
||||
_pad5: [u16; 3],
|
||||
pub gs: u16,
|
||||
_pad6: [u16; 3],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct TrapInfo {
|
||||
pub vector: u8,
|
||||
pub flags: u8,
|
||||
pub cs: u16,
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct VcpuGuestContext {
|
||||
pub fpu_ctx: VcpuGuestContextFpuCtx,
|
||||
pub flags: u64,
|
||||
pub user_regs: CpuUserRegs,
|
||||
pub trap_ctx: [TrapInfo; 256],
|
||||
pub ldt_base: u64,
|
||||
pub ldt_ents: u64,
|
||||
pub gdt_frames: [u64; 16],
|
||||
pub gdt_ents: u64,
|
||||
pub kernel_ss: u64,
|
||||
pub kernel_sp: u64,
|
||||
pub ctrlreg: [u64; 8],
|
||||
pub debugreg: [u64; 8],
|
||||
pub event_callback_eip: u64,
|
||||
pub failsafe_callback_eip: u64,
|
||||
pub syscall_callback_eip: u64,
|
||||
pub vm_assist: u64,
|
||||
pub fs_base: u64,
|
||||
pub gs_base_kernel: u64,
|
||||
pub gs_base_user: u64,
|
||||
}
|
||||
|
||||
impl Default for VcpuGuestContext {
|
||||
fn default() -> Self {
|
||||
VcpuGuestContext {
|
||||
fpu_ctx: Default::default(),
|
||||
flags: 0,
|
||||
user_regs: Default::default(),
|
||||
trap_ctx: [TrapInfo::default(); 256],
|
||||
ldt_base: 0,
|
||||
ldt_ents: 0,
|
||||
gdt_frames: [0; 16],
|
||||
gdt_ents: 0,
|
||||
kernel_ss: 0,
|
||||
kernel_sp: 0,
|
||||
ctrlreg: [0; 8],
|
||||
debugreg: [0; 8],
|
||||
event_callback_eip: 0,
|
||||
failsafe_callback_eip: 0,
|
||||
syscall_callback_eip: 0,
|
||||
vm_assist: 0,
|
||||
fs_base: 0,
|
||||
gs_base_kernel: 0,
|
||||
gs_base_user: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub union VcpuGuestContextAny {
|
||||
pub value: VcpuGuestContext,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct MmuExtOp {
|
||||
pub cmd: c_uint,
|
||||
pub arg1: c_ulong,
|
||||
pub arg2: c_ulong,
|
||||
}
|
||||
|
||||
pub const MMUEXT_PIN_L4_TABLE: u32 = 3;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EvtChnAllocUnbound {
|
||||
pub dom: u16,
|
||||
pub remote_dom: u16,
|
||||
pub port: u32,
|
||||
}
|
35
libs/xen/xenclient/Cargo.toml
Normal file
35
libs/xen/xenclient/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "xenclient"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
elf = { workspace = true }
|
||||
flate2 = { workspace = true }
|
||||
xz2 = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
slice-copy = { workspace = true }
|
||||
log = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
||||
[dependencies.xencall]
|
||||
path = "../xencall"
|
||||
|
||||
[dependencies.xenstore]
|
||||
path = "../xenstore"
|
||||
|
||||
[dependencies.xenevtchn]
|
||||
path = "../xenevtchn"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { workspace = true }
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xenclient-boot"
|
||||
path = "examples/boot.rs"
|
31
libs/xen/xenclient/examples/boot.rs
Normal file
31
libs/xen/xenclient/examples/boot.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::{env, process};
|
||||
use xenclient::error::Result;
|
||||
use xenclient::{DomainConfig, XenClient};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 3 {
|
||||
println!("usage: boot <kernel-image> <initrd>");
|
||||
process::exit(1);
|
||||
}
|
||||
let kernel_image_path = args.get(1).expect("argument not specified");
|
||||
let initrd_path = args.get(2).expect("argument not specified");
|
||||
let mut client = XenClient::open()?;
|
||||
let config = DomainConfig {
|
||||
backend_domid: 0,
|
||||
name: "xenclient-test",
|
||||
max_vcpus: 1,
|
||||
mem_mb: 512,
|
||||
kernel_path: kernel_image_path.as_str(),
|
||||
initrd_path: initrd_path.as_str(),
|
||||
cmdline: "debug elevator=noop",
|
||||
disks: vec![],
|
||||
filesystems: vec![],
|
||||
extra_keys: vec![],
|
||||
};
|
||||
let domid = client.create(&config)?;
|
||||
println!("created domain {}", domid);
|
||||
Ok(())
|
||||
}
|
358
libs/xen/xenclient/src/boot.rs
Normal file
358
libs/xen/xenclient/src/boot.rs
Normal file
@ -0,0 +1,358 @@
|
||||
use crate::error::Result;
|
||||
use crate::mem::PhysicalPages;
|
||||
use crate::sys::{GrantEntry, XEN_PAGE_SHIFT};
|
||||
use crate::Error;
|
||||
use libc::munmap;
|
||||
use log::debug;
|
||||
use slice_copy::copy;
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::slice;
|
||||
use xencall::XenCall;
|
||||
|
||||
pub trait BootImageLoader {
|
||||
fn parse(&self) -> Result<BootImageInfo>;
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()>;
|
||||
}
|
||||
|
||||
pub const XEN_UNSET_ADDR: u64 = -1i64 as u64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BootImageInfo {
|
||||
pub start: u64,
|
||||
pub virt_base: u64,
|
||||
pub virt_kstart: u64,
|
||||
pub virt_kend: u64,
|
||||
pub virt_hypercall: u64,
|
||||
pub virt_entry: u64,
|
||||
pub virt_p2m_base: u64,
|
||||
pub unmapped_initrd: bool,
|
||||
}
|
||||
|
||||
pub struct BootSetup<'a> {
|
||||
pub(crate) call: &'a XenCall,
|
||||
pub phys: PhysicalPages<'a>,
|
||||
pub(crate) domid: u32,
|
||||
pub(crate) virt_alloc_end: u64,
|
||||
pub(crate) pfn_alloc_end: u64,
|
||||
pub(crate) virt_pgtab_end: u64,
|
||||
pub(crate) total_pages: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainSegment {
|
||||
pub(crate) vstart: u64,
|
||||
vend: u64,
|
||||
pub pfn: u64,
|
||||
pub(crate) addr: u64,
|
||||
pub(crate) size: u64,
|
||||
pub(crate) pages: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BootState {
|
||||
pub kernel_segment: DomainSegment,
|
||||
pub start_info_segment: DomainSegment,
|
||||
pub xenstore_segment: DomainSegment,
|
||||
pub console_segment: DomainSegment,
|
||||
pub boot_stack_segment: DomainSegment,
|
||||
pub p2m_segment: DomainSegment,
|
||||
pub page_table_segment: DomainSegment,
|
||||
pub image_info: BootImageInfo,
|
||||
pub shared_info_frame: u64,
|
||||
pub initrd_segment: DomainSegment,
|
||||
pub store_evtchn: u32,
|
||||
pub console_evtchn: u32,
|
||||
}
|
||||
|
||||
impl BootSetup<'_> {
|
||||
pub fn new(call: &XenCall, domid: u32) -> BootSetup {
|
||||
BootSetup {
|
||||
call,
|
||||
phys: PhysicalPages::new(call, domid),
|
||||
domid,
|
||||
virt_alloc_end: 0,
|
||||
pfn_alloc_end: 0,
|
||||
virt_pgtab_end: 0,
|
||||
total_pages: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_memory(&mut self, arch: &mut dyn ArchBootSetup, total_pages: u64) -> Result<()> {
|
||||
self.call.set_address_size(self.domid, 64)?;
|
||||
arch.meminit(self, total_pages)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn initialize(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
initrd: &[u8],
|
||||
max_vcpus: u32,
|
||||
mem_mb: u64,
|
||||
) -> Result<BootState> {
|
||||
debug!(
|
||||
"BootSetup initialize max_vcpus={:?} mem_mb={:?}",
|
||||
max_vcpus, mem_mb
|
||||
);
|
||||
|
||||
let total_pages = mem_mb << (20 - arch.page_shift());
|
||||
self.initialize_memory(arch, total_pages)?;
|
||||
|
||||
let image_info = image_loader.parse()?;
|
||||
debug!("BootSetup initialize image_info={:?}", image_info);
|
||||
self.virt_alloc_end = image_info.virt_base;
|
||||
let kernel_segment = self.load_kernel_segment(arch, image_loader, &image_info)?;
|
||||
let mut p2m_segment: Option<DomainSegment> = None;
|
||||
if image_info.virt_p2m_base >= image_info.virt_base
|
||||
|| (image_info.virt_p2m_base & ((1 << arch.page_shift()) - 1)) != 0
|
||||
{
|
||||
p2m_segment = Some(arch.alloc_p2m_segment(self, &image_info)?);
|
||||
}
|
||||
let start_info_segment = self.alloc_page(arch)?;
|
||||
let xenstore_segment = self.alloc_page(arch)?;
|
||||
let console_segment = self.alloc_page(arch)?;
|
||||
let page_table_segment = arch.alloc_page_tables(self, &image_info)?;
|
||||
let boot_stack_segment = self.alloc_page(arch)?;
|
||||
|
||||
if self.virt_pgtab_end > 0 {
|
||||
self.alloc_padding_pages(arch, self.virt_pgtab_end)?;
|
||||
}
|
||||
|
||||
let mut initrd_segment: Option<DomainSegment> = None;
|
||||
if !image_info.unmapped_initrd {
|
||||
initrd_segment = Some(self.alloc_module(arch, initrd)?);
|
||||
}
|
||||
if p2m_segment.is_none() {
|
||||
let mut segment = arch.alloc_p2m_segment(self, &image_info)?;
|
||||
segment.vstart = image_info.virt_p2m_base;
|
||||
p2m_segment = Some(segment);
|
||||
}
|
||||
let p2m_segment = p2m_segment.unwrap();
|
||||
|
||||
if image_info.unmapped_initrd {
|
||||
initrd_segment = Some(self.alloc_module(arch, initrd)?);
|
||||
}
|
||||
|
||||
let initrd_segment = initrd_segment.unwrap();
|
||||
let store_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?;
|
||||
let console_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?;
|
||||
let state = BootState {
|
||||
kernel_segment,
|
||||
start_info_segment,
|
||||
xenstore_segment,
|
||||
console_segment,
|
||||
boot_stack_segment,
|
||||
p2m_segment,
|
||||
page_table_segment,
|
||||
image_info,
|
||||
initrd_segment,
|
||||
store_evtchn,
|
||||
console_evtchn,
|
||||
shared_info_frame: 0,
|
||||
};
|
||||
debug!("BootSetup initialize state={:?}", state);
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn boot(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
state: &mut BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<()> {
|
||||
let domain_info = self.call.get_domain_info(self.domid)?;
|
||||
let shared_info_frame = domain_info.shared_info_frame;
|
||||
state.shared_info_frame = shared_info_frame;
|
||||
arch.setup_page_tables(self, state)?;
|
||||
arch.setup_start_info(self, state, cmdline)?;
|
||||
arch.setup_hypercall_page(self, &state.image_info)?;
|
||||
arch.bootlate(self, state)?;
|
||||
arch.setup_shared_info(self, state.shared_info_frame)?;
|
||||
arch.vcpu(self, state)?;
|
||||
self.phys.unmap_all()?;
|
||||
self.gnttab_seed(state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gnttab_seed(&mut self, state: &mut BootState) -> Result<()> {
|
||||
let console_gfn = self.phys.p2m[state.console_segment.pfn as usize];
|
||||
let xenstore_gfn = self.phys.p2m[state.xenstore_segment.pfn as usize];
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, 1 << XEN_PAGE_SHIFT)
|
||||
.ok_or(Error::MmapFailed)?;
|
||||
self.call.map_resource(self.domid, 1, 0, 0, 1, addr)?;
|
||||
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
|
||||
entries[0].flags = 1 << 0;
|
||||
entries[0].domid = 0;
|
||||
entries[0].frame = console_gfn as u32;
|
||||
entries[1].flags = 1 << 0;
|
||||
entries[1].domid = 0;
|
||||
entries[1].frame = xenstore_gfn as u32;
|
||||
unsafe {
|
||||
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
|
||||
if result != 0 {
|
||||
return Err(Error::UnmapFailed);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_kernel_segment(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment> {
|
||||
let kernel_segment = self.alloc_segment(
|
||||
arch,
|
||||
image_info.virt_kstart,
|
||||
image_info.virt_kend - image_info.virt_kstart,
|
||||
)?;
|
||||
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
||||
let kernel_segment_slice =
|
||||
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
||||
image_loader.load(image_info, kernel_segment_slice)?;
|
||||
Ok(kernel_segment)
|
||||
}
|
||||
|
||||
pub(crate) fn round_up(addr: u64, mask: u64) -> u64 {
|
||||
addr | mask
|
||||
}
|
||||
|
||||
pub(crate) fn bits_to_mask(bits: u64) -> u64 {
|
||||
(1 << bits) - 1
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_segment(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
start: u64,
|
||||
size: u64,
|
||||
) -> Result<DomainSegment> {
|
||||
if start > 0 {
|
||||
self.alloc_padding_pages(arch, start)?;
|
||||
}
|
||||
|
||||
let page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
|
||||
let pages = (size + page_size as u64 - 1) / page_size as u64;
|
||||
let start = self.virt_alloc_end;
|
||||
|
||||
let mut segment = DomainSegment {
|
||||
vstart: start,
|
||||
vend: 0,
|
||||
pfn: self.pfn_alloc_end,
|
||||
addr: 0,
|
||||
size,
|
||||
pages,
|
||||
};
|
||||
|
||||
self.chk_alloc_pages(arch, pages)?;
|
||||
|
||||
let ptr = self.phys.pfn_to_ptr(segment.pfn, pages)?;
|
||||
segment.addr = ptr;
|
||||
let slice = unsafe {
|
||||
slice::from_raw_parts_mut(ptr as *mut u8, (pages * page_size as u64) as usize)
|
||||
};
|
||||
slice.fill(0);
|
||||
segment.vend = self.virt_alloc_end;
|
||||
debug!(
|
||||
"BootSetup alloc_segment {:#x} -> {:#x} (pfn {:#x} + {:#x} pages)",
|
||||
start, segment.vend, segment.pfn, pages
|
||||
);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_page(&mut self, arch: &mut dyn ArchBootSetup) -> Result<DomainSegment> {
|
||||
let start = self.virt_alloc_end;
|
||||
let pfn = self.pfn_alloc_end;
|
||||
|
||||
self.chk_alloc_pages(arch, 1)?;
|
||||
debug!("BootSetup alloc_page {:#x} (pfn {:#x})", start, pfn);
|
||||
Ok(DomainSegment {
|
||||
vstart: start,
|
||||
vend: (start + arch.page_size()) - 1,
|
||||
pfn,
|
||||
addr: 0,
|
||||
size: 0,
|
||||
pages: 1,
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_module(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
buffer: &[u8],
|
||||
) -> Result<DomainSegment> {
|
||||
let segment = self.alloc_segment(arch, 0, buffer.len() as u64)?;
|
||||
let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) };
|
||||
copy(slice, buffer);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_padding_pages(&mut self, arch: &mut dyn ArchBootSetup, boundary: u64) -> Result<()> {
|
||||
if (boundary & (arch.page_size() - 1)) != 0 {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
|
||||
if boundary < self.virt_alloc_end {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
let pages = (boundary - self.virt_alloc_end) / arch.page_size();
|
||||
self.chk_alloc_pages(arch, pages)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn chk_alloc_pages(&mut self, arch: &mut dyn ArchBootSetup, pages: u64) -> Result<()> {
|
||||
if pages > self.total_pages
|
||||
|| self.pfn_alloc_end > self.total_pages
|
||||
|| pages > self.total_pages - self.pfn_alloc_end
|
||||
{
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
|
||||
self.pfn_alloc_end += pages;
|
||||
self.virt_alloc_end += pages * arch.page_size();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ArchBootSetup {
|
||||
fn page_size(&mut self) -> u64;
|
||||
fn page_shift(&mut self) -> u64;
|
||||
|
||||
fn alloc_p2m_segment(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment>;
|
||||
|
||||
fn alloc_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment>;
|
||||
|
||||
fn setup_page_tables(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
|
||||
fn setup_start_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<()>;
|
||||
|
||||
fn setup_shared_info(&mut self, setup: &mut BootSetup, shared_info_frame: u64) -> Result<()>;
|
||||
|
||||
fn setup_hypercall_page(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<()>;
|
||||
|
||||
fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<()>;
|
||||
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
}
|
289
libs/xen/xenclient/src/elfloader.rs
Normal file
289
libs/xen/xenclient/src/elfloader.rs
Normal file
@ -0,0 +1,289 @@
|
||||
use crate::boot::{BootImageInfo, BootImageLoader, XEN_UNSET_ADDR};
|
||||
use crate::error::Result;
|
||||
use crate::sys::{
|
||||
XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HYPERCALL_PAGE, XEN_ELFNOTE_INIT_P2M, XEN_ELFNOTE_MOD_START_PFN,
|
||||
XEN_ELFNOTE_PADDR_OFFSET, XEN_ELFNOTE_TYPES, XEN_ELFNOTE_VIRT_BASE,
|
||||
};
|
||||
use crate::Error;
|
||||
use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE};
|
||||
use elf::endian::AnyEndian;
|
||||
use elf::note::Note;
|
||||
use elf::ElfBytes;
|
||||
use flate2::bufread::GzDecoder;
|
||||
use log::debug;
|
||||
use memchr::memmem::find_iter;
|
||||
use slice_copy::copy;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::mem::size_of;
|
||||
use xz2::bufread::XzDecoder;
|
||||
|
||||
pub struct ElfImageLoader {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
fn xen_note_value_as_u64(endian: AnyEndian, value: &[u8]) -> Option<u64> {
|
||||
let bytes = value.to_vec();
|
||||
match value.len() {
|
||||
1 => {
|
||||
let bytes: Option<[u8; size_of::<u8>()]> = bytes.try_into().ok();
|
||||
Some(match endian {
|
||||
AnyEndian::Little => u8::from_le_bytes(bytes?),
|
||||
AnyEndian::Big => u8::from_be_bytes(bytes?),
|
||||
} as u64)
|
||||
}
|
||||
2 => {
|
||||
let bytes: Option<[u8; size_of::<u16>()]> = bytes.try_into().ok();
|
||||
Some(match endian {
|
||||
AnyEndian::Little => u16::from_le_bytes(bytes?),
|
||||
AnyEndian::Big => u16::from_be_bytes(bytes?),
|
||||
} as u64)
|
||||
}
|
||||
4 => {
|
||||
let bytes: Option<[u8; size_of::<u32>()]> = bytes.try_into().ok();
|
||||
Some(match endian {
|
||||
AnyEndian::Little => u32::from_le_bytes(bytes?),
|
||||
AnyEndian::Big => u32::from_be_bytes(bytes?),
|
||||
} as u64)
|
||||
}
|
||||
8 => {
|
||||
let bytes: Option<[u8; size_of::<u64>()]> = bytes.try_into().ok();
|
||||
Some(match endian {
|
||||
AnyEndian::Little => u64::from_le_bytes(bytes?),
|
||||
AnyEndian::Big => u64::from_be_bytes(bytes?),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl ElfImageLoader {
|
||||
pub fn new(data: Vec<u8>) -> ElfImageLoader {
|
||||
ElfImageLoader { data }
|
||||
}
|
||||
|
||||
pub fn load_file(path: &str) -> Result<ElfImageLoader> {
|
||||
let data = std::fs::read(path)?;
|
||||
Ok(ElfImageLoader::new(data))
|
||||
}
|
||||
|
||||
pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
let buff = BufReader::new(data);
|
||||
let image = ElfImageLoader::read_one_stream(&mut GzDecoder::new(buff))?;
|
||||
Ok(ElfImageLoader::new(image))
|
||||
}
|
||||
|
||||
pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
let buff = BufReader::new(data);
|
||||
let image = ElfImageLoader::read_one_stream(&mut XzDecoder::new(buff))?;
|
||||
Ok(ElfImageLoader::new(image))
|
||||
}
|
||||
|
||||
fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>> {
|
||||
let mut result: Vec<u8> = Vec::new();
|
||||
let mut buffer = [0u8; 8192];
|
||||
|
||||
loop {
|
||||
match read.read(&mut buffer) {
|
||||
Ok(size) => {
|
||||
if size == 0 {
|
||||
break;
|
||||
}
|
||||
result.extend_from_slice(&buffer[0..size])
|
||||
}
|
||||
Err(error) => {
|
||||
if !result.is_empty() {
|
||||
break;
|
||||
}
|
||||
return Err(Error::from(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn load_file_gz(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
ElfImageLoader::load_gz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_xz(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
ElfImageLoader::load_xz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_kernel(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
|
||||
for start in find_iter(file.as_slice(), &[0x1f, 0x8b]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_gz(&file[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
for start in find_iter(file.as_slice(), &[0xfd, 0x37, 0x7a, 0x58]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_xz(&file[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ElfCompressionUnknown)
|
||||
}
|
||||
}
|
||||
|
||||
struct ElfNoteValue {
|
||||
value: u64,
|
||||
}
|
||||
|
||||
impl BootImageLoader for ElfImageLoader {
|
||||
fn parse(&self) -> Result<BootImageInfo> {
|
||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||
let headers = elf.section_headers().ok_or(Error::ElfInvalidImage)?;
|
||||
let mut linux_notes: HashMap<u64, Vec<u8>> = HashMap::new();
|
||||
let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new();
|
||||
|
||||
for header in headers {
|
||||
if header.sh_type != SHT_NOTE {
|
||||
continue;
|
||||
}
|
||||
|
||||
let notes = elf.section_data_as_notes(&header)?;
|
||||
for note in notes {
|
||||
if let Note::Unknown(note) = note {
|
||||
if note.name == "Linux" {
|
||||
linux_notes.insert(note.n_type, note.desc.to_vec());
|
||||
}
|
||||
|
||||
if note.name == "Xen" {
|
||||
for typ in XEN_ELFNOTE_TYPES {
|
||||
if typ.id != note.n_type {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = if !typ.is_string {
|
||||
xen_note_value_as_u64(elf.ehdr.endianness, note.desc).unwrap_or(0)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
xen_notes.insert(typ.id, ElfNoteValue { value });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if linux_notes.is_empty() {
|
||||
return Err(Error::ElfInvalidImage);
|
||||
}
|
||||
|
||||
if xen_notes.is_empty() {
|
||||
return Err(Error::ElfXenSupportMissing);
|
||||
}
|
||||
|
||||
let paddr_offset = xen_notes
|
||||
.get(&XEN_ELFNOTE_PADDR_OFFSET)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.value;
|
||||
let virt_base = xen_notes
|
||||
.get(&XEN_ELFNOTE_VIRT_BASE)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.value;
|
||||
let entry = xen_notes
|
||||
.get(&XEN_ELFNOTE_ENTRY)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.value;
|
||||
let virt_hypercall = xen_notes
|
||||
.get(&XEN_ELFNOTE_HYPERCALL_PAGE)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.value;
|
||||
let init_p2m = xen_notes
|
||||
.get(&XEN_ELFNOTE_INIT_P2M)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.value;
|
||||
let mod_start_pfn = xen_notes
|
||||
.get(&XEN_ELFNOTE_MOD_START_PFN)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.value;
|
||||
|
||||
let mut start: u64 = u64::MAX;
|
||||
let mut end: u64 = 0;
|
||||
|
||||
let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
|
||||
|
||||
for header in segments {
|
||||
if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 {
|
||||
continue;
|
||||
}
|
||||
let paddr = header.p_paddr;
|
||||
let memsz = header.p_memsz;
|
||||
if start > paddr {
|
||||
start = paddr;
|
||||
}
|
||||
|
||||
if end < paddr + memsz {
|
||||
end = paddr + memsz;
|
||||
}
|
||||
}
|
||||
|
||||
if paddr_offset != XEN_UNSET_ADDR && virt_base == XEN_UNSET_ADDR {
|
||||
return Err(Error::ElfInvalidImage);
|
||||
}
|
||||
|
||||
let virt_offset = virt_base - paddr_offset;
|
||||
let virt_kstart = start + virt_offset;
|
||||
let virt_kend = end + virt_offset;
|
||||
let virt_entry = entry;
|
||||
|
||||
let image_info = BootImageInfo {
|
||||
start,
|
||||
virt_base,
|
||||
virt_kstart,
|
||||
virt_kend,
|
||||
virt_hypercall,
|
||||
virt_entry,
|
||||
virt_p2m_base: init_p2m,
|
||||
unmapped_initrd: mod_start_pfn != 0,
|
||||
};
|
||||
Ok(image_info)
|
||||
}
|
||||
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||
let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
|
||||
|
||||
debug!(
|
||||
"ElfImageLoader load dst={:#x} segments={}",
|
||||
dst.as_ptr() as u64,
|
||||
segments.len()
|
||||
);
|
||||
for header in segments {
|
||||
let paddr = header.p_paddr;
|
||||
let filesz = header.p_filesz;
|
||||
let memsz = header.p_memsz;
|
||||
let base_offset = paddr - image_info.start;
|
||||
let data = elf.segment_data(&header)?;
|
||||
let segment_dst = &mut dst[base_offset as usize..];
|
||||
let copy_slice = &data[0..filesz as usize];
|
||||
debug!(
|
||||
"ElfImageLoader load copy hdr={:?} dst={:#x} len={}",
|
||||
header,
|
||||
copy_slice.as_ptr() as u64,
|
||||
copy_slice.len()
|
||||
);
|
||||
copy(segment_dst, copy_slice);
|
||||
if (memsz - filesz) > 0 {
|
||||
let remaining = &mut segment_dst[filesz as usize..memsz as usize];
|
||||
debug!(
|
||||
"ElfImageLoader load fill_zero hdr={:?} dst={:#x} len={}",
|
||||
header.p_offset,
|
||||
remaining.as_ptr() as u64,
|
||||
remaining.len()
|
||||
);
|
||||
remaining.fill(0);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
39
libs/xen/xenclient/src/error.rs
Normal file
39
libs/xen/xenclient/src/error.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use std::io;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("io issue encountered")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("xenstore issue encountered")]
|
||||
XenStore(#[from] xenstore::error::Error),
|
||||
#[error("xencall issue encountered")]
|
||||
XenCall(#[from] xencall::error::Error),
|
||||
#[error("domain does not have a tty")]
|
||||
TtyNotFound,
|
||||
#[error("introducing the domain failed")]
|
||||
IntroduceDomainFailed,
|
||||
#[error("string conversion of a path failed")]
|
||||
PathStringConversion,
|
||||
#[error("parent of path not found")]
|
||||
PathParentNotFound,
|
||||
#[error("domain does not exist")]
|
||||
DomainNonExistent,
|
||||
#[error("elf parse failed")]
|
||||
ElfParseFailed(#[from] elf::ParseError),
|
||||
#[error("mmap failed")]
|
||||
MmapFailed,
|
||||
#[error("munmap failed")]
|
||||
UnmapFailed,
|
||||
#[error("memory setup failed")]
|
||||
MemorySetupFailed,
|
||||
#[error("populate physmap failed: wanted={0}, received={1}, input_extents={2}")]
|
||||
PopulatePhysmapFailed(usize, usize, usize),
|
||||
#[error("unknown elf compression method")]
|
||||
ElfCompressionUnknown,
|
||||
#[error("expected elf image format not found")]
|
||||
ElfInvalidImage,
|
||||
#[error("provided elf image does not contain xen support")]
|
||||
ElfXenSupportMissing,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
575
libs/xen/xenclient/src/lib.rs
Normal file
575
libs/xen/xenclient/src/lib.rs
Normal file
@ -0,0 +1,575 @@
|
||||
pub mod boot;
|
||||
pub mod elfloader;
|
||||
pub mod error;
|
||||
pub mod mem;
|
||||
pub mod sys;
|
||||
pub mod x86;
|
||||
|
||||
use crate::boot::BootSetup;
|
||||
use crate::elfloader::ElfImageLoader;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::x86::X86BootSetup;
|
||||
use log::{trace, warn};
|
||||
|
||||
use std::fs::{read, File, OpenOptions};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
use xencall::sys::CreateDomain;
|
||||
use xencall::XenCall;
|
||||
use xenstore::client::{
|
||||
XsPermission, XsdClient, XsdInterface, XS_PERM_NONE, XS_PERM_READ, XS_PERM_READ_WRITE,
|
||||
};
|
||||
|
||||
pub struct XenClient {
|
||||
pub store: XsdClient,
|
||||
call: XenCall,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlockDeviceRef {
|
||||
pub path: String,
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainDisk<'a> {
|
||||
pub vdev: &'a str,
|
||||
pub block: &'a BlockDeviceRef,
|
||||
pub writable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainFilesystem<'a> {
|
||||
pub path: &'a str,
|
||||
pub tag: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainConfig<'a> {
|
||||
pub backend_domid: u32,
|
||||
pub name: &'a str,
|
||||
pub max_vcpus: u32,
|
||||
pub mem_mb: u64,
|
||||
pub kernel_path: &'a str,
|
||||
pub initrd_path: &'a str,
|
||||
pub cmdline: &'a str,
|
||||
pub disks: Vec<DomainDisk<'a>>,
|
||||
pub filesystems: Vec<DomainFilesystem<'a>>,
|
||||
pub extra_keys: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl XenClient {
|
||||
pub fn open() -> Result<XenClient> {
|
||||
let store = XsdClient::open()?;
|
||||
let call = XenCall::open()?;
|
||||
Ok(XenClient { store, call })
|
||||
}
|
||||
|
||||
pub fn create(&mut self, config: &DomainConfig) -> Result<u32> {
|
||||
let domain = CreateDomain {
|
||||
max_vcpus: config.max_vcpus,
|
||||
..Default::default()
|
||||
};
|
||||
let domid = self.call.create_domain(domain)?;
|
||||
match self.init(domid, &domain, config) {
|
||||
Ok(_) => Ok(domid),
|
||||
Err(err) => {
|
||||
// ignore since destroying a domain is best
|
||||
// effort when an error occurs
|
||||
let _ = self.destroy(domid);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(&mut self, domid: u32) -> Result<()> {
|
||||
if let Err(err) = self.destroy_store(domid) {
|
||||
warn!("failed to destroy store for domain {}: {}", domid, err);
|
||||
}
|
||||
self.call.destroy_domain(domid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy_store(&mut self, domid: u32) -> Result<()> {
|
||||
let dom_path = self.store.get_domain_path(domid)?;
|
||||
let vm_path = self.store.read_string(&format!("{}/vm", dom_path))?;
|
||||
if vm_path.is_empty() {
|
||||
return Err(Error::DomainNonExistent);
|
||||
}
|
||||
|
||||
let mut backend_paths: Vec<String> = Vec::new();
|
||||
let console_frontend_path = format!("{}/console", dom_path);
|
||||
let console_backend_path = self
|
||||
.store
|
||||
.read_string_optional(format!("{}/backend", console_frontend_path).as_str())?;
|
||||
|
||||
for device_category in self
|
||||
.store
|
||||
.list_any(format!("{}/device", dom_path).as_str())?
|
||||
{
|
||||
for device_id in self
|
||||
.store
|
||||
.list_any(format!("{}/device/{}", dom_path, device_category).as_str())?
|
||||
{
|
||||
let device_path = format!("{}/device/{}/{}", dom_path, device_category, device_id);
|
||||
let backend_path = self
|
||||
.store
|
||||
.read_string(format!("{}/backend", device_path).as_str())?;
|
||||
backend_paths.push(backend_path);
|
||||
}
|
||||
}
|
||||
|
||||
for backend in &backend_paths {
|
||||
let state_path = format!("{}/state", backend);
|
||||
let online_path = format!("{}/online", backend);
|
||||
let mut tx = self.store.transaction()?;
|
||||
let state = tx.read_string(&state_path)?;
|
||||
if state.is_empty() {
|
||||
break;
|
||||
}
|
||||
tx.write_string(&online_path, "0")?;
|
||||
if !state.is_empty() && u32::from_str(&state).unwrap_or(0) != 6 {
|
||||
tx.write_string(&state_path, "5")?;
|
||||
}
|
||||
tx.commit()?;
|
||||
|
||||
let mut count: u32 = 0;
|
||||
loop {
|
||||
if count >= 100 {
|
||||
warn!("unable to safely destroy backend: {}", backend);
|
||||
break;
|
||||
}
|
||||
let state = self.store.read_string(&state_path)?;
|
||||
let state = i64::from_str(&state).unwrap_or(-1);
|
||||
if state == 6 {
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tx = self.store.transaction()?;
|
||||
let mut backend_removals: Vec<String> = Vec::new();
|
||||
backend_removals.extend_from_slice(backend_paths.as_slice());
|
||||
if let Some(backend) = console_backend_path {
|
||||
backend_removals.push(backend);
|
||||
}
|
||||
for path in &backend_removals {
|
||||
let path = PathBuf::from(path);
|
||||
let parent = path.parent().ok_or(Error::PathParentNotFound)?;
|
||||
tx.rm(parent.to_str().ok_or(Error::PathStringConversion)?)?;
|
||||
}
|
||||
tx.rm(&vm_path)?;
|
||||
tx.rm(&dom_path)?;
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init(&mut self, domid: u32, domain: &CreateDomain, config: &DomainConfig) -> Result<()> {
|
||||
trace!(
|
||||
"XenClient init domid={} domain={:?} config={:?}",
|
||||
domid,
|
||||
domain,
|
||||
config
|
||||
);
|
||||
let backend_dom_path = self.store.get_domain_path(0)?;
|
||||
let dom_path = self.store.get_domain_path(domid)?;
|
||||
let uuid_string = Uuid::from_bytes(domain.handle).to_string();
|
||||
let vm_path = format!("/vm/{}", uuid_string);
|
||||
|
||||
let ro_perm = &[
|
||||
XsPermission {
|
||||
id: 0,
|
||||
perms: XS_PERM_NONE,
|
||||
},
|
||||
XsPermission {
|
||||
id: domid,
|
||||
perms: XS_PERM_READ,
|
||||
},
|
||||
];
|
||||
|
||||
let rw_perm = &[XsPermission {
|
||||
id: domid,
|
||||
perms: XS_PERM_READ_WRITE,
|
||||
}];
|
||||
|
||||
let no_perm = &[XsPermission {
|
||||
id: 0,
|
||||
perms: XS_PERM_NONE,
|
||||
}];
|
||||
|
||||
{
|
||||
let mut tx = self.store.transaction()?;
|
||||
|
||||
tx.rm(dom_path.as_str())?;
|
||||
tx.mknod(dom_path.as_str(), ro_perm)?;
|
||||
|
||||
tx.rm(vm_path.as_str())?;
|
||||
tx.mknod(vm_path.as_str(), ro_perm)?;
|
||||
|
||||
tx.mknod(vm_path.as_str(), no_perm)?;
|
||||
tx.mknod(format!("{}/device", vm_path).as_str(), no_perm)?;
|
||||
|
||||
tx.write_string(format!("{}/vm", dom_path).as_str(), &vm_path)?;
|
||||
|
||||
tx.mknod(format!("{}/cpu", dom_path).as_str(), ro_perm)?;
|
||||
tx.mknod(format!("{}/memory", dom_path).as_str(), ro_perm)?;
|
||||
|
||||
tx.mknod(format!("{}/control", dom_path).as_str(), ro_perm)?;
|
||||
|
||||
tx.mknod(format!("{}/control/shutdown", dom_path).as_str(), rw_perm)?;
|
||||
tx.mknod(
|
||||
format!("{}/control/feature-poweroff", dom_path).as_str(),
|
||||
rw_perm,
|
||||
)?;
|
||||
tx.mknod(
|
||||
format!("{}/control/feature-reboot", dom_path).as_str(),
|
||||
rw_perm,
|
||||
)?;
|
||||
tx.mknod(
|
||||
format!("{}/control/feature-suspend", dom_path).as_str(),
|
||||
rw_perm,
|
||||
)?;
|
||||
tx.mknod(format!("{}/control/sysrq", dom_path).as_str(), rw_perm)?;
|
||||
|
||||
tx.mknod(format!("{}/data", dom_path).as_str(), rw_perm)?;
|
||||
tx.mknod(format!("{}/drivers", dom_path).as_str(), rw_perm)?;
|
||||
tx.mknod(format!("{}/feature", dom_path).as_str(), rw_perm)?;
|
||||
tx.mknod(format!("{}/attr", dom_path).as_str(), rw_perm)?;
|
||||
tx.mknod(format!("{}/error", dom_path).as_str(), rw_perm)?;
|
||||
|
||||
tx.write_string(
|
||||
format!("{}/uuid", vm_path).as_str(),
|
||||
&Uuid::from_bytes(domain.handle).to_string(),
|
||||
)?;
|
||||
tx.write_string(format!("{}/name", dom_path).as_str(), config.name)?;
|
||||
tx.write_string(format!("{}/name", vm_path).as_str(), config.name)?;
|
||||
|
||||
for (key, value) in &config.extra_keys {
|
||||
tx.write_string(format!("{}/{}", dom_path, key).as_str(), value)?;
|
||||
}
|
||||
|
||||
tx.commit()?;
|
||||
}
|
||||
|
||||
self.call.set_max_vcpus(domid, config.max_vcpus)?;
|
||||
self.call.set_max_mem(domid, config.mem_mb * 1024)?;
|
||||
let image_loader = ElfImageLoader::load_file_kernel(config.kernel_path)?;
|
||||
|
||||
let console_evtchn: u32;
|
||||
let xenstore_evtchn: u32;
|
||||
let console_mfn: u64;
|
||||
let xenstore_mfn: u64;
|
||||
|
||||
{
|
||||
let mut boot = BootSetup::new(&self.call, domid);
|
||||
let mut arch = X86BootSetup::new();
|
||||
let initrd = read(config.initrd_path)?;
|
||||
let mut state = boot.initialize(
|
||||
&mut arch,
|
||||
&image_loader,
|
||||
initrd.as_slice(),
|
||||
config.max_vcpus,
|
||||
config.mem_mb,
|
||||
)?;
|
||||
boot.boot(&mut arch, &mut state, config.cmdline)?;
|
||||
console_evtchn = state.console_evtchn;
|
||||
xenstore_evtchn = state.store_evtchn;
|
||||
console_mfn = boot.phys.p2m[state.console_segment.pfn as usize];
|
||||
xenstore_mfn = boot.phys.p2m[state.xenstore_segment.pfn as usize];
|
||||
}
|
||||
|
||||
{
|
||||
let mut tx = self.store.transaction()?;
|
||||
tx.write_string(format!("{}/image/os_type", vm_path).as_str(), "linux")?;
|
||||
tx.write_string(
|
||||
format!("{}/image/kernel", vm_path).as_str(),
|
||||
config.kernel_path,
|
||||
)?;
|
||||
tx.write_string(
|
||||
format!("{}/image/ramdisk", vm_path).as_str(),
|
||||
config.initrd_path,
|
||||
)?;
|
||||
tx.write_string(
|
||||
format!("{}/image/cmdline", vm_path).as_str(),
|
||||
config.cmdline,
|
||||
)?;
|
||||
|
||||
tx.write_string(
|
||||
format!("{}/memory/static-max", dom_path).as_str(),
|
||||
&(config.mem_mb * 1024).to_string(),
|
||||
)?;
|
||||
tx.write_string(
|
||||
format!("{}/memory/target", dom_path).as_str(),
|
||||
&(config.mem_mb * 1024).to_string(),
|
||||
)?;
|
||||
tx.write_string(format!("{}/memory/videoram", dom_path).as_str(), "0")?;
|
||||
tx.write_string(format!("{}/domid", dom_path).as_str(), &domid.to_string())?;
|
||||
tx.write_string(
|
||||
format!("{}/store/port", dom_path).as_str(),
|
||||
&xenstore_evtchn.to_string(),
|
||||
)?;
|
||||
tx.write_string(
|
||||
format!("{}/store/ring-ref", dom_path).as_str(),
|
||||
&xenstore_mfn.to_string(),
|
||||
)?;
|
||||
for i in 0..config.max_vcpus {
|
||||
let path = format!("{}/cpu/{}", dom_path, i);
|
||||
tx.mkdir(&path)?;
|
||||
tx.set_perms(&path, ro_perm)?;
|
||||
let path = format!("{}/cpu/{}/availability", dom_path, i);
|
||||
tx.write_string(&path, "online")?;
|
||||
tx.set_perms(&path, ro_perm)?;
|
||||
}
|
||||
tx.commit()?;
|
||||
}
|
||||
if !self
|
||||
.store
|
||||
.introduce_domain(domid, xenstore_mfn, xenstore_evtchn)?
|
||||
{
|
||||
return Err(Error::IntroduceDomainFailed);
|
||||
}
|
||||
self.console_device_add(
|
||||
&dom_path,
|
||||
&backend_dom_path,
|
||||
config.backend_domid,
|
||||
domid,
|
||||
console_evtchn,
|
||||
console_mfn,
|
||||
)?;
|
||||
for (index, disk) in config.disks.iter().enumerate() {
|
||||
self.disk_device_add(
|
||||
&dom_path,
|
||||
&backend_dom_path,
|
||||
config.backend_domid,
|
||||
domid,
|
||||
index,
|
||||
disk,
|
||||
)?;
|
||||
}
|
||||
for (index, filesystem) in config.filesystems.iter().enumerate() {
|
||||
self.fs_9p_device_add(
|
||||
&dom_path,
|
||||
&backend_dom_path,
|
||||
config.backend_domid,
|
||||
domid,
|
||||
index,
|
||||
filesystem,
|
||||
)?;
|
||||
}
|
||||
self.call.unpause_domain(domid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disk_device_add(
|
||||
&mut self,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
index: usize,
|
||||
disk: &DomainDisk,
|
||||
) -> Result<()> {
|
||||
let id = (202 << 8) | (index << 4) as u64;
|
||||
let backend_items: Vec<(&str, String)> = vec![
|
||||
("frontend-id", domid.to_string()),
|
||||
("online", "1".to_string()),
|
||||
("removable", "0".to_string()),
|
||||
("bootable", "1".to_string()),
|
||||
("state", "1".to_string()),
|
||||
("dev", disk.vdev.to_string()),
|
||||
("type", "phy".to_string()),
|
||||
("mode", if disk.writable { "w" } else { "r" }.to_string()),
|
||||
("device-type", "disk".to_string()),
|
||||
("discard-enable", "0".to_string()),
|
||||
("specification", "xen".to_string()),
|
||||
("physical-device-path", disk.block.path.to_string()),
|
||||
(
|
||||
"physical-device",
|
||||
format!("{:02x}:{:02x}", disk.block.major, disk.block.minor),
|
||||
),
|
||||
];
|
||||
|
||||
let frontend_items: Vec<(&str, String)> = vec![
|
||||
("backend-id", backend_domid.to_string()),
|
||||
("state", "1".to_string()),
|
||||
("virtual-device", id.to_string()),
|
||||
("device-type", "disk".to_string()),
|
||||
("trusted", "1".to_string()),
|
||||
("protocol", "x86_64-abi".to_string()),
|
||||
];
|
||||
|
||||
self.device_add(
|
||||
"vbd",
|
||||
id,
|
||||
dom_path,
|
||||
backend_dom_path,
|
||||
backend_domid,
|
||||
domid,
|
||||
frontend_items,
|
||||
backend_items,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn console_device_add(
|
||||
&mut self,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
port: u32,
|
||||
mfn: u64,
|
||||
) -> Result<()> {
|
||||
let backend_entries = vec![
|
||||
("frontend-id", domid.to_string()),
|
||||
("online", "1".to_string()),
|
||||
("state", "1".to_string()),
|
||||
("protocol", "vt100".to_string()),
|
||||
];
|
||||
|
||||
let frontend_entries = vec![
|
||||
("backend-id", backend_domid.to_string()),
|
||||
("limit", "1048576".to_string()),
|
||||
("type", "xenconsoled".to_string()),
|
||||
("output", "pty".to_string()),
|
||||
("tty", "".to_string()),
|
||||
("port", port.to_string()),
|
||||
("ring-ref", mfn.to_string()),
|
||||
];
|
||||
|
||||
self.device_add(
|
||||
"console",
|
||||
0,
|
||||
dom_path,
|
||||
backend_dom_path,
|
||||
backend_domid,
|
||||
domid,
|
||||
frontend_entries,
|
||||
backend_entries,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fs_9p_device_add(
|
||||
&mut self,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
index: usize,
|
||||
filesystem: &DomainFilesystem,
|
||||
) -> Result<()> {
|
||||
let id = 90 + index as u64;
|
||||
let backend_items: Vec<(&str, String)> = vec![
|
||||
("frontend-id", domid.to_string()),
|
||||
("online", "1".to_string()),
|
||||
("state", "1".to_string()),
|
||||
("path", filesystem.path.to_string()),
|
||||
("security-model", "none".to_string()),
|
||||
];
|
||||
|
||||
let frontend_items: Vec<(&str, String)> = vec![
|
||||
("backend-id", backend_domid.to_string()),
|
||||
("state", "1".to_string()),
|
||||
("tag", filesystem.tag.to_string()),
|
||||
];
|
||||
|
||||
self.device_add(
|
||||
"9pfs",
|
||||
id,
|
||||
dom_path,
|
||||
backend_dom_path,
|
||||
backend_domid,
|
||||
domid,
|
||||
frontend_items,
|
||||
backend_items,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn device_add(
|
||||
&mut self,
|
||||
typ: &str,
|
||||
id: u64,
|
||||
dom_path: &str,
|
||||
backend_dom_path: &str,
|
||||
backend_domid: u32,
|
||||
domid: u32,
|
||||
frontend_items: Vec<(&str, String)>,
|
||||
backend_items: Vec<(&str, String)>,
|
||||
) -> Result<()> {
|
||||
let console_zero = typ == "console" && id == 0;
|
||||
|
||||
let frontend_path = if console_zero {
|
||||
format!("{}/console", dom_path)
|
||||
} else {
|
||||
format!("{}/device/{}/{}", dom_path, typ, id)
|
||||
};
|
||||
let backend_path = format!("{}/backend/{}/{}/{}", backend_dom_path, typ, domid, id);
|
||||
|
||||
let mut backend_items: Vec<(&str, String)> = backend_items.clone();
|
||||
let mut frontend_items: Vec<(&str, String)> = frontend_items.clone();
|
||||
backend_items.push(("frontend", frontend_path.clone()));
|
||||
frontend_items.push(("backend", backend_path.clone()));
|
||||
let frontend_perms = &[
|
||||
XsPermission {
|
||||
id: domid,
|
||||
perms: XS_PERM_NONE,
|
||||
},
|
||||
XsPermission {
|
||||
id: backend_domid,
|
||||
perms: XS_PERM_READ,
|
||||
},
|
||||
];
|
||||
|
||||
let backend_perms = &[
|
||||
XsPermission {
|
||||
id: backend_domid,
|
||||
perms: XS_PERM_NONE,
|
||||
},
|
||||
XsPermission {
|
||||
id: domid,
|
||||
perms: XS_PERM_READ,
|
||||
},
|
||||
];
|
||||
|
||||
let mut tx = self.store.transaction()?;
|
||||
tx.mknod(&frontend_path, frontend_perms)?;
|
||||
for (p, value) in &frontend_items {
|
||||
let path = format!("{}/{}", frontend_path, *p);
|
||||
tx.write_string(&path, value)?;
|
||||
if !console_zero {
|
||||
tx.set_perms(&path, frontend_perms)?;
|
||||
}
|
||||
}
|
||||
tx.mknod(&backend_path, backend_perms)?;
|
||||
for (p, value) in &backend_items {
|
||||
let path = format!("{}/{}", backend_path, *p);
|
||||
tx.write_string(&path, value)?;
|
||||
}
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn open_console(&mut self, domid: u32) -> Result<(File, File)> {
|
||||
let dom_path = self.store.get_domain_path(domid)?;
|
||||
let console_tty_path = format!("{}/console/tty", dom_path);
|
||||
let tty = self
|
||||
.store
|
||||
.read_string_optional(&console_tty_path)?
|
||||
.unwrap_or("".to_string());
|
||||
if tty.is_empty() {
|
||||
return Err(Error::TtyNotFound);
|
||||
}
|
||||
let read = OpenOptions::new().read(true).write(false).open(&tty)?;
|
||||
let write = OpenOptions::new().read(false).write(true).open(&tty)?;
|
||||
Ok((read, write))
|
||||
}
|
||||
}
|
186
libs/xen/xenclient/src/mem.rs
Normal file
186
libs/xen/xenclient/src/mem.rs
Normal file
@ -0,0 +1,186 @@
|
||||
use crate::error::Result;
|
||||
use crate::sys::{XEN_PAGE_SHIFT, XEN_PAGE_SIZE};
|
||||
use crate::Error;
|
||||
use libc::munmap;
|
||||
use log::debug;
|
||||
use std::ffi::c_void;
|
||||
|
||||
use crate::x86::X86_PAGE_SHIFT;
|
||||
use xencall::sys::MmapEntry;
|
||||
use xencall::XenCall;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PhysicalPage {
|
||||
pfn: u64,
|
||||
ptr: u64,
|
||||
count: u64,
|
||||
}
|
||||
|
||||
pub struct PhysicalPages<'a> {
|
||||
domid: u32,
|
||||
pub(crate) p2m: Vec<u64>,
|
||||
call: &'a XenCall,
|
||||
pages: Vec<PhysicalPage>,
|
||||
}
|
||||
|
||||
impl PhysicalPages<'_> {
|
||||
pub fn new(call: &XenCall, domid: u32) -> PhysicalPages {
|
||||
PhysicalPages {
|
||||
domid,
|
||||
p2m: Vec::new(),
|
||||
call,
|
||||
pages: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_p2m(&mut self, p2m: Vec<u64>) {
|
||||
self.p2m = p2m;
|
||||
}
|
||||
|
||||
pub fn p2m_size(&mut self) -> u64 {
|
||||
self.p2m.len() as u64
|
||||
}
|
||||
|
||||
pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
for page in &self.pages {
|
||||
if pfn >= page.pfn + page.count {
|
||||
continue;
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
if (pfn + count) <= page.pfn {
|
||||
continue;
|
||||
}
|
||||
|
||||
if pfn < page.pfn || (pfn + count) > page.pfn + page.count {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
} else {
|
||||
if pfn < page.pfn {
|
||||
continue;
|
||||
}
|
||||
|
||||
if pfn >= page.pfn + page.count {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(page.ptr + ((pfn - page.pfn) << X86_PAGE_SHIFT));
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
|
||||
self.pfn_alloc(pfn, count)
|
||||
}
|
||||
|
||||
fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
let mut entries = vec![MmapEntry::default(); count as usize];
|
||||
for (i, entry) in entries.iter_mut().enumerate() {
|
||||
entry.mfn = self.p2m[pfn as usize + i];
|
||||
}
|
||||
let chunk_size = 1 << XEN_PAGE_SHIFT;
|
||||
let num_per_entry = chunk_size >> XEN_PAGE_SHIFT;
|
||||
let num = num_per_entry * count as usize;
|
||||
let mut pfns = vec![u64::MAX; num];
|
||||
for i in 0..count as usize {
|
||||
for j in 0..num_per_entry {
|
||||
pfns[i * num_per_entry + j] = entries[i].mfn + j as u64;
|
||||
}
|
||||
}
|
||||
|
||||
let actual_mmap_len = (num as u64) << XEN_PAGE_SHIFT;
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, actual_mmap_len)
|
||||
.ok_or(Error::MmapFailed)?;
|
||||
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
|
||||
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
|
||||
if result != 0 {
|
||||
return Err(Error::MmapFailed);
|
||||
}
|
||||
let page = PhysicalPage {
|
||||
pfn,
|
||||
ptr: addr,
|
||||
count,
|
||||
};
|
||||
debug!(
|
||||
"alloc_pfn {:#x}+{:#x} at {:#x}",
|
||||
page.pfn, page.count, page.ptr
|
||||
);
|
||||
self.pages.push(page);
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result<u64> {
|
||||
let num = ((size + XEN_PAGE_SIZE - 1) >> XEN_PAGE_SHIFT) as usize;
|
||||
let mut pfns = vec![u64::MAX; num];
|
||||
for (i, item) in pfns.iter_mut().enumerate().take(num) {
|
||||
*item = mfn + i as u64;
|
||||
}
|
||||
|
||||
let actual_mmap_len = (num as u64) << XEN_PAGE_SHIFT;
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, actual_mmap_len)
|
||||
.ok_or(Error::MmapFailed)?;
|
||||
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
|
||||
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
|
||||
if result != 0 {
|
||||
return Err(Error::MmapFailed);
|
||||
}
|
||||
let page = PhysicalPage {
|
||||
pfn: u64::MAX,
|
||||
ptr: addr,
|
||||
count: num as u64,
|
||||
};
|
||||
debug!(
|
||||
"alloc_mfn {:#x}+{:#x} at {:#x}",
|
||||
page.pfn, page.count, page.ptr
|
||||
);
|
||||
self.pages.push(page);
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn unmap_all(&mut self) -> Result<()> {
|
||||
for page in &self.pages {
|
||||
unsafe {
|
||||
let err = munmap(
|
||||
page.ptr as *mut c_void,
|
||||
(page.count << X86_PAGE_SHIFT) as usize,
|
||||
);
|
||||
if err != 0 {
|
||||
return Err(Error::UnmapFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.pages.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, pfn: u64) -> Result<()> {
|
||||
let page = self.pages.iter().enumerate().find(|(_, x)| x.pfn == pfn);
|
||||
if page.is_none() {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
let (i, page) = page.unwrap();
|
||||
|
||||
unsafe {
|
||||
let err = munmap(
|
||||
page.ptr as *mut c_void,
|
||||
(page.count << X86_PAGE_SHIFT) as usize,
|
||||
);
|
||||
debug!(
|
||||
"unmapped {:#x} foreign bytes at {:#x}",
|
||||
(page.count << X86_PAGE_SHIFT) as usize,
|
||||
page.ptr
|
||||
);
|
||||
if err != 0 {
|
||||
return Err(Error::UnmapFailed);
|
||||
}
|
||||
self.pages.remove(i);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
130
libs/xen/xenclient/src/sys.rs
Normal file
130
libs/xen/xenclient/src/sys.rs
Normal file
@ -0,0 +1,130 @@
|
||||
pub const XEN_ELFNOTE_INFO: u64 = 0;
|
||||
pub const XEN_ELFNOTE_ENTRY: u64 = 1;
|
||||
pub const XEN_ELFNOTE_HYPERCALL_PAGE: u64 = 2;
|
||||
pub const XEN_ELFNOTE_VIRT_BASE: u64 = 3;
|
||||
pub const XEN_ELFNOTE_PADDR_OFFSET: u64 = 4;
|
||||
pub const XEN_ELFNOTE_XEN_VERSION: u64 = 5;
|
||||
pub const XEN_ELFNOTE_GUEST_OS: u64 = 6;
|
||||
pub const XEN_ELFNOTE_GUEST_VERSION: u64 = 7;
|
||||
pub const XEN_ELFNOTE_LOADER: u64 = 8;
|
||||
pub const XEN_ELFNOTE_PAE_MODE: u64 = 9;
|
||||
pub const XEN_ELFNOTE_FEATURES: u64 = 10;
|
||||
pub const XEN_ELFNOTE_BSD_SYMTAB: u64 = 11;
|
||||
pub const XEN_ELFNOTE_HV_START_LOW: u64 = 12;
|
||||
pub const XEN_ELFNOTE_L1_MFN_VALID: u64 = 13;
|
||||
pub const XEN_ELFNOTE_SUSPEND_CANCEL: u64 = 14;
|
||||
pub const XEN_ELFNOTE_INIT_P2M: u64 = 15;
|
||||
pub const XEN_ELFNOTE_MOD_START_PFN: u64 = 16;
|
||||
pub const XEN_ELFNOTE_SUPPORTED_FEATURES: u64 = 17;
|
||||
pub const XEN_ELFNOTE_PHYS32_ENTRY: u64 = 18;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ElfNoteXenType {
|
||||
pub id: u64,
|
||||
pub name: &'static str,
|
||||
pub is_string: bool,
|
||||
}
|
||||
|
||||
pub const XEN_ELFNOTE_TYPES: &[ElfNoteXenType] = &[
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_ENTRY,
|
||||
name: "ENTRY",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_HYPERCALL_PAGE,
|
||||
name: "HYPERCALL_PAGE",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_VIRT_BASE,
|
||||
name: "VIRT_BASE",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_INIT_P2M,
|
||||
name: "INIT_P2M",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_PADDR_OFFSET,
|
||||
name: "PADDR_OFFSET",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_HV_START_LOW,
|
||||
name: "HV_START_LOW",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_XEN_VERSION,
|
||||
name: "XEN_VERSION",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_GUEST_OS,
|
||||
name: "GUEST_OS",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_GUEST_VERSION,
|
||||
name: "GUEST_VERSION",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_LOADER,
|
||||
name: "LOADER",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_PAE_MODE,
|
||||
name: "PAE_MODE",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_FEATURES,
|
||||
name: "FEATURES",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_SUPPORTED_FEATURES,
|
||||
name: "SUPPORTED_FEATURES",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_BSD_SYMTAB,
|
||||
name: "BSD_SYMTAB",
|
||||
is_string: true,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_SUSPEND_CANCEL,
|
||||
name: "SUSPEND_CANCEL",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_MOD_START_PFN,
|
||||
name: "MOD_START_PFN",
|
||||
is_string: false,
|
||||
},
|
||||
ElfNoteXenType {
|
||||
id: XEN_ELFNOTE_PHYS32_ENTRY,
|
||||
name: "PHYS32_ENTRY",
|
||||
is_string: false,
|
||||
},
|
||||
];
|
||||
|
||||
pub const XEN_PAGE_SHIFT: u64 = 12;
|
||||
pub const XEN_PAGE_SIZE: u64 = 1 << XEN_PAGE_SHIFT;
|
||||
pub const XEN_PAGE_MASK: u64 = !(XEN_PAGE_SIZE - 1);
|
||||
pub const SUPERPAGE_BATCH_SIZE: u64 = 512;
|
||||
pub const SUPERPAGE_2MB_SHIFT: u64 = 9;
|
||||
pub const SUPERPAGE_2MB_NR_PFNS: u64 = 1u64 << SUPERPAGE_2MB_SHIFT;
|
||||
pub const VGCF_IN_KERNEL: u64 = 1 << 2;
|
||||
pub const VGCF_ONLINE: u64 = 1 << 5;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct GrantEntry {
|
||||
pub flags: u16,
|
||||
pub domid: u16,
|
||||
pub frame: u32,
|
||||
}
|
627
libs/xen/xenclient/src/x86.rs
Normal file
627
libs/xen/xenclient/src/x86.rs
Normal file
@ -0,0 +1,627 @@
|
||||
use crate::boot::{
|
||||
ArchBootSetup, BootImageInfo, BootSetup, BootState, DomainSegment, XEN_UNSET_ADDR,
|
||||
};
|
||||
use crate::error::Result;
|
||||
use crate::sys::{
|
||||
SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, VGCF_IN_KERNEL, VGCF_ONLINE,
|
||||
XEN_PAGE_SHIFT,
|
||||
};
|
||||
use crate::Error;
|
||||
use libc::c_char;
|
||||
use log::{debug, trace};
|
||||
use slice_copy::copy;
|
||||
use std::cmp::{max, min};
|
||||
use std::mem::size_of;
|
||||
use std::slice;
|
||||
use xencall::sys::{VcpuGuestContext, MMUEXT_PIN_L4_TABLE};
|
||||
|
||||
pub const X86_PAGE_SHIFT: u64 = 12;
|
||||
pub const X86_PAGE_SIZE: u64 = 1 << X86_PAGE_SHIFT;
|
||||
pub const X86_VIRT_BITS: u64 = 48;
|
||||
pub const X86_VIRT_MASK: u64 = (1 << X86_VIRT_BITS) - 1;
|
||||
pub const X86_PGTABLE_LEVELS: u64 = 4;
|
||||
pub const X86_PGTABLE_LEVEL_SHIFT: u64 = 9;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct PageTableMappingLevel {
|
||||
pub from: u64,
|
||||
pub to: u64,
|
||||
pub pfn: u64,
|
||||
pub pgtables: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct PageTableMapping {
|
||||
pub area: PageTableMappingLevel,
|
||||
pub levels: [PageTableMappingLevel; X86_PGTABLE_LEVELS as usize],
|
||||
}
|
||||
|
||||
pub const X86_PAGE_TABLE_MAX_MAPPINGS: usize = 2;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct PageTable {
|
||||
pub mappings_count: usize,
|
||||
pub mappings: [PageTableMapping; X86_PAGE_TABLE_MAX_MAPPINGS],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct StartInfoConsole {
|
||||
pub mfn: u64,
|
||||
pub evtchn: u32,
|
||||
}
|
||||
|
||||
pub const MAX_GUEST_CMDLINE: usize = 1024;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct StartInfo {
|
||||
pub magic: [c_char; 32],
|
||||
pub nr_pages: u64,
|
||||
pub shared_info: u64,
|
||||
pub flags: u32,
|
||||
pub store_mfn: u64,
|
||||
pub store_evtchn: u32,
|
||||
pub console: StartInfoConsole,
|
||||
pub pt_base: u64,
|
||||
pub nr_pt_frames: u64,
|
||||
pub mfn_list: u64,
|
||||
pub mod_start: u64,
|
||||
pub mod_len: u64,
|
||||
pub cmdline: [c_char; MAX_GUEST_CMDLINE],
|
||||
pub first_p2m_pfn: u64,
|
||||
pub nr_p2m_frames: u64,
|
||||
}
|
||||
|
||||
pub const X86_GUEST_MAGIC: &str = "xen-3.0-x86_64";
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct ArchVcpuInfo {
|
||||
pub cr2: u64,
|
||||
pub pad: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VcpuInfoTime {
|
||||
pub version: u32,
|
||||
pub pad0: u32,
|
||||
pub tsc_timestamp: u64,
|
||||
pub system_time: u64,
|
||||
pub tsc_to_system_mul: u32,
|
||||
pub tsc_shift: i8,
|
||||
pub flags: u8,
|
||||
pub pad1: [u8; 2],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VcpuInfo {
|
||||
pub evtchn_upcall_pending: u8,
|
||||
pub evtchn_upcall_mask: u8,
|
||||
pub evtchn_pending_sel: u64,
|
||||
pub arch_vcpu_info: ArchVcpuInfo,
|
||||
pub vcpu_info_time: VcpuInfoTime,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct SharedInfo {
|
||||
pub vcpu_info: [VcpuInfo; 32],
|
||||
pub evtchn_pending: [u64; u64::BITS as usize],
|
||||
pub evtchn_mask: [u64; u64::BITS as usize],
|
||||
pub wc_version: u32,
|
||||
pub wc_sec: u32,
|
||||
pub wc_nsec: u32,
|
||||
pub wc_sec_hi: u32,
|
||||
|
||||
// arch shared info
|
||||
pub max_pfn: u64,
|
||||
pub pfn_to_mfn_frame_list_list: u64,
|
||||
pub nmi_reason: u64,
|
||||
pub p2m_cr3: u64,
|
||||
pub p2m_vaddr: u64,
|
||||
pub p2m_generation: u64,
|
||||
}
|
||||
|
||||
pub struct X86BootSetup {
|
||||
table: PageTable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VmemRange {
|
||||
start: u64,
|
||||
end: u64,
|
||||
_flags: u32,
|
||||
_nid: u32,
|
||||
}
|
||||
|
||||
impl Default for X86BootSetup {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl X86BootSetup {
|
||||
pub fn new() -> X86BootSetup {
|
||||
X86BootSetup {
|
||||
table: PageTable::default(),
|
||||
}
|
||||
}
|
||||
|
||||
const PAGE_PRESENT: u64 = 0x001;
|
||||
const PAGE_RW: u64 = 0x002;
|
||||
const PAGE_USER: u64 = 0x004;
|
||||
const PAGE_ACCESSED: u64 = 0x020;
|
||||
const PAGE_DIRTY: u64 = 0x040;
|
||||
fn get_pg_prot(&mut self, l: usize, pfn: u64) -> u64 {
|
||||
let prot = [
|
||||
X86BootSetup::PAGE_PRESENT | X86BootSetup::PAGE_RW | X86BootSetup::PAGE_ACCESSED,
|
||||
X86BootSetup::PAGE_PRESENT
|
||||
| X86BootSetup::PAGE_RW
|
||||
| X86BootSetup::PAGE_ACCESSED
|
||||
| X86BootSetup::PAGE_DIRTY
|
||||
| X86BootSetup::PAGE_USER,
|
||||
X86BootSetup::PAGE_PRESENT
|
||||
| X86BootSetup::PAGE_RW
|
||||
| X86BootSetup::PAGE_ACCESSED
|
||||
| X86BootSetup::PAGE_DIRTY
|
||||
| X86BootSetup::PAGE_USER,
|
||||
X86BootSetup::PAGE_PRESENT
|
||||
| X86BootSetup::PAGE_RW
|
||||
| X86BootSetup::PAGE_ACCESSED
|
||||
| X86BootSetup::PAGE_DIRTY
|
||||
| X86BootSetup::PAGE_USER,
|
||||
];
|
||||
|
||||
let prot = prot[l];
|
||||
if l > 0 {
|
||||
return prot;
|
||||
}
|
||||
|
||||
for m in 0..self.table.mappings_count {
|
||||
let map = &self.table.mappings[m];
|
||||
let pfn_s = map.levels[(X86_PGTABLE_LEVELS - 1) as usize].pfn;
|
||||
let pfn_e = map.area.pgtables as u64 + pfn_s;
|
||||
if pfn >= pfn_s && pfn < pfn_e {
|
||||
return prot & !X86BootSetup::PAGE_RW;
|
||||
}
|
||||
}
|
||||
prot
|
||||
}
|
||||
|
||||
fn count_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
from: u64,
|
||||
to: u64,
|
||||
pfn: u64,
|
||||
) -> Result<usize> {
|
||||
debug!("counting pgtables from={} to={} pfn={}", from, to, pfn);
|
||||
if self.table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
|
||||
let m = self.table.mappings_count;
|
||||
|
||||
let pfn_end = pfn + ((to - from) >> X86_PAGE_SHIFT);
|
||||
if pfn_end >= setup.phys.p2m_size() {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
|
||||
for idx in 0..self.table.mappings_count {
|
||||
if from < self.table.mappings[idx].area.to && to > self.table.mappings[idx].area.from {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
}
|
||||
let mut map = PageTableMapping::default();
|
||||
map.area.from = from & X86_VIRT_MASK;
|
||||
map.area.to = to & X86_VIRT_MASK;
|
||||
|
||||
for l in (0usize..X86_PGTABLE_LEVELS as usize).rev() {
|
||||
map.levels[l].pfn = setup.pfn_alloc_end + map.area.pgtables as u64;
|
||||
if l as u64 == X86_PGTABLE_LEVELS - 1 {
|
||||
if self.table.mappings_count == 0 {
|
||||
map.levels[l].from = 0;
|
||||
map.levels[l].to = X86_VIRT_MASK;
|
||||
map.levels[l].pgtables = 1;
|
||||
map.area.pgtables += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let bits = X86_PAGE_SHIFT + (l + 1) as u64 * X86_PGTABLE_LEVEL_SHIFT;
|
||||
let mask = BootSetup::bits_to_mask(bits);
|
||||
map.levels[l].from = map.area.from & !mask;
|
||||
map.levels[l].to = map.area.to | mask;
|
||||
|
||||
for cmp in &mut self.table.mappings[0..self.table.mappings_count] {
|
||||
if cmp.levels[l].from == cmp.levels[l].to {
|
||||
continue;
|
||||
}
|
||||
|
||||
if map.levels[l].from >= cmp.levels[l].from && map.levels[l].to <= cmp.levels[l].to
|
||||
{
|
||||
map.levels[l].from = 0;
|
||||
map.levels[l].to = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if map.levels[l].from >= cmp.levels[l].from
|
||||
&& map.levels[l].from <= cmp.levels[l].to
|
||||
{
|
||||
map.levels[l].from = cmp.levels[l].to + 1;
|
||||
}
|
||||
|
||||
if map.levels[l].to >= cmp.levels[l].from && map.levels[l].to <= cmp.levels[l].to {
|
||||
map.levels[l].to = cmp.levels[l].from - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if map.levels[l].from < map.levels[l].to {
|
||||
map.levels[l].pgtables =
|
||||
(((map.levels[l].to - map.levels[l].from) >> bits) + 1) as usize;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"BootSetup count_pgtables {:#x}/{}: {:#x} -> {:#x}, {} tables",
|
||||
mask, bits, map.levels[l].from, map.levels[l].to, map.levels[l].pgtables
|
||||
);
|
||||
map.area.pgtables += map.levels[l].pgtables;
|
||||
}
|
||||
self.table.mappings[m] = map;
|
||||
Ok(m)
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchBootSetup for X86BootSetup {
|
||||
fn page_size(&mut self) -> u64 {
|
||||
X86_PAGE_SIZE
|
||||
}
|
||||
|
||||
fn page_shift(&mut self) -> u64 {
|
||||
X86_PAGE_SHIFT
|
||||
}
|
||||
|
||||
fn alloc_p2m_segment(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment> {
|
||||
let mut p2m_alloc_size =
|
||||
((setup.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1);
|
||||
let from = image_info.virt_p2m_base;
|
||||
let to = from + p2m_alloc_size - 1;
|
||||
let m = self.count_page_tables(setup, from, to, setup.pfn_alloc_end)?;
|
||||
|
||||
let pgtables: usize;
|
||||
{
|
||||
let map = &mut self.table.mappings[m];
|
||||
map.area.pfn = setup.pfn_alloc_end;
|
||||
for lvl_idx in 0..4 {
|
||||
map.levels[lvl_idx].pfn += p2m_alloc_size >> X86_PAGE_SHIFT;
|
||||
}
|
||||
pgtables = map.area.pgtables;
|
||||
}
|
||||
self.table.mappings_count += 1;
|
||||
p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64;
|
||||
let p2m_segment = setup.alloc_segment(self, 0, p2m_alloc_size)?;
|
||||
Ok(p2m_segment)
|
||||
}
|
||||
|
||||
fn alloc_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment> {
|
||||
let mut extra_pages = 1;
|
||||
extra_pages += (512 * 1024) / X86_PAGE_SIZE;
|
||||
let mut pages = extra_pages;
|
||||
|
||||
let mut try_virt_end: u64;
|
||||
let mut m: usize;
|
||||
loop {
|
||||
try_virt_end = BootSetup::round_up(
|
||||
setup.virt_alloc_end + pages * X86_PAGE_SIZE,
|
||||
BootSetup::bits_to_mask(22),
|
||||
);
|
||||
m = self.count_page_tables(setup, image_info.virt_base, try_virt_end, 0)?;
|
||||
pages = self.table.mappings[m].area.pgtables as u64 + extra_pages;
|
||||
if setup.virt_alloc_end + pages * X86_PAGE_SIZE <= try_virt_end + 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.table.mappings[m].area.pfn = 0;
|
||||
self.table.mappings_count += 1;
|
||||
setup.virt_pgtab_end = try_virt_end + 1;
|
||||
let size = self.table.mappings[m].area.pgtables as u64 * X86_PAGE_SIZE;
|
||||
let segment = setup.alloc_segment(self, 0, size)?;
|
||||
debug!(
|
||||
"BootSetup alloc_page_tables table={:?} segment={:?}",
|
||||
self.table, segment
|
||||
);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn setup_page_tables(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let p2m_guest = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
state.p2m_segment.addr as *mut u64,
|
||||
setup.phys.p2m_size() as usize,
|
||||
)
|
||||
};
|
||||
copy(p2m_guest, &setup.phys.p2m);
|
||||
|
||||
for l in (0usize..X86_PGTABLE_LEVELS as usize).rev() {
|
||||
for m1 in 0usize..self.table.mappings_count {
|
||||
let map1 = &self.table.mappings[m1];
|
||||
let from = map1.levels[l].from;
|
||||
let to = map1.levels[l].to;
|
||||
let pg_ptr = setup.phys.pfn_to_ptr(map1.levels[l].pfn, 0)? as *mut u64;
|
||||
for m2 in 0usize..self.table.mappings_count {
|
||||
let map2 = &self.table.mappings[m2];
|
||||
let lvl = if l > 0 {
|
||||
&map2.levels[l - 1]
|
||||
} else {
|
||||
&map2.area
|
||||
};
|
||||
|
||||
if l > 0 && lvl.pgtables == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if lvl.from >= to || lvl.to <= from {
|
||||
continue;
|
||||
}
|
||||
|
||||
let p_s = (max(from, lvl.from) - from)
|
||||
>> (X86_PAGE_SHIFT + l as u64 * X86_PGTABLE_LEVEL_SHIFT);
|
||||
let p_e = (min(to, lvl.to) - from)
|
||||
>> (X86_PAGE_SHIFT + l as u64 * X86_PGTABLE_LEVEL_SHIFT);
|
||||
let rhs = X86_PAGE_SHIFT as usize + l * X86_PGTABLE_LEVEL_SHIFT as usize;
|
||||
let mut pfn = ((max(from, lvl.from) - lvl.from) >> rhs) + lvl.pfn;
|
||||
|
||||
debug!(
|
||||
"BootSetup setup_page_tables lvl={} map_1={} map_2={} pfn={:#x} p_s={:#x} p_e={:#x}",
|
||||
l, m1, m2, pfn, p_s, p_e
|
||||
);
|
||||
|
||||
let pg = unsafe { slice::from_raw_parts_mut(pg_ptr, (p_e + 1) as usize) };
|
||||
for p in p_s..p_e + 1 {
|
||||
let prot = self.get_pg_prot(l, pfn);
|
||||
let pfn_paddr = setup.phys.p2m[pfn as usize] << X86_PAGE_SHIFT;
|
||||
let value = pfn_paddr | prot;
|
||||
pg[p as usize] = value;
|
||||
pfn += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_start_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<()> {
|
||||
let ptr = setup.phys.pfn_to_ptr(state.start_info_segment.pfn, 1)?;
|
||||
let byte_slice =
|
||||
unsafe { slice::from_raw_parts_mut(ptr as *mut u8, X86_PAGE_SIZE as usize) };
|
||||
byte_slice.fill(0);
|
||||
let info = ptr as *mut StartInfo;
|
||||
unsafe {
|
||||
for (i, c) in X86_GUEST_MAGIC.chars().enumerate() {
|
||||
(*info).magic[i] = c as c_char;
|
||||
}
|
||||
(*info).magic[X86_GUEST_MAGIC.len()] = 0 as c_char;
|
||||
(*info).nr_pages = setup.total_pages;
|
||||
(*info).shared_info = state.shared_info_frame << X86_PAGE_SHIFT;
|
||||
(*info).pt_base = state.page_table_segment.vstart;
|
||||
(*info).nr_pt_frames = self.table.mappings[0].area.pgtables as u64;
|
||||
(*info).mfn_list = state.p2m_segment.vstart;
|
||||
(*info).first_p2m_pfn = state.p2m_segment.pfn;
|
||||
(*info).nr_p2m_frames = state.p2m_segment.pages;
|
||||
(*info).flags = 0;
|
||||
(*info).store_evtchn = state.store_evtchn;
|
||||
(*info).store_mfn = setup.phys.p2m[state.xenstore_segment.pfn as usize];
|
||||
(*info).console.mfn = setup.phys.p2m[state.console_segment.pfn as usize];
|
||||
(*info).console.evtchn = state.console_evtchn;
|
||||
(*info).mod_start = state.initrd_segment.vstart;
|
||||
(*info).mod_len = state.initrd_segment.size;
|
||||
for (i, c) in cmdline.chars().enumerate() {
|
||||
(*info).cmdline[i] = c as c_char;
|
||||
}
|
||||
(*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0;
|
||||
trace!("BootSetup setup_start_info start_info={:?}", *info);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_shared_info(&mut self, setup: &mut BootSetup, shared_info_frame: u64) -> Result<()> {
|
||||
let info = setup
|
||||
.phys
|
||||
.map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)?
|
||||
as *mut SharedInfo;
|
||||
unsafe {
|
||||
let size = size_of::<SharedInfo>();
|
||||
let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size);
|
||||
info_as_buff.fill(0);
|
||||
for i in 0..32 {
|
||||
(*info).vcpu_info[i].evtchn_upcall_mask = 1;
|
||||
}
|
||||
trace!("BootSetup setup_shared_info shared_info={:?}", *info);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_hypercall_page(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<()> {
|
||||
if image_info.virt_hypercall == XEN_UNSET_ADDR {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pfn = (image_info.virt_hypercall - image_info.virt_base) >> X86_PAGE_SHIFT;
|
||||
let mfn = setup.phys.p2m[pfn as usize];
|
||||
setup.call.hypercall_init(setup.domid, mfn)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<()> {
|
||||
setup.call.claim_pages(setup.domid, total_pages)?;
|
||||
let mut vmemranges: Vec<VmemRange> = Vec::new();
|
||||
let stub = VmemRange {
|
||||
start: 0,
|
||||
end: total_pages << XEN_PAGE_SHIFT,
|
||||
_flags: 0,
|
||||
_nid: 0,
|
||||
};
|
||||
vmemranges.push(stub);
|
||||
let mut p2m_size: u64 = 0;
|
||||
let mut total: u64 = 0;
|
||||
for range in &vmemranges {
|
||||
total += (range.end - range.start) >> XEN_PAGE_SHIFT;
|
||||
p2m_size = p2m_size.max(range.end >> XEN_PAGE_SHIFT);
|
||||
}
|
||||
|
||||
if total != total_pages {
|
||||
return Err(Error::MemorySetupFailed);
|
||||
}
|
||||
|
||||
setup.total_pages = total;
|
||||
|
||||
let mut p2m = vec![u64::MAX; p2m_size as usize];
|
||||
for range in &vmemranges {
|
||||
let mut extents_init = vec![0u64; SUPERPAGE_BATCH_SIZE as usize];
|
||||
let pages = (range.end - range.start) >> XEN_PAGE_SHIFT;
|
||||
let pfn_base = range.start >> XEN_PAGE_SHIFT;
|
||||
|
||||
for pfn in pfn_base..pfn_base + pages {
|
||||
p2m[pfn as usize] = pfn;
|
||||
}
|
||||
|
||||
let mut super_pages = pages >> SUPERPAGE_2MB_SHIFT;
|
||||
let mut pfn_base_idx: u64 = pfn_base;
|
||||
while super_pages > 0 {
|
||||
let count = super_pages.min(SUPERPAGE_BATCH_SIZE);
|
||||
super_pages -= count;
|
||||
|
||||
let mut j: usize = 0;
|
||||
let mut pfn: u64 = pfn_base_idx;
|
||||
loop {
|
||||
if pfn >= pfn_base_idx + (count << SUPERPAGE_2MB_SHIFT) {
|
||||
break;
|
||||
}
|
||||
extents_init[j] = p2m[pfn as usize];
|
||||
pfn += SUPERPAGE_2MB_NR_PFNS;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
let extents_init_slice = extents_init.as_slice();
|
||||
let extents = setup.call.populate_physmap(
|
||||
setup.domid,
|
||||
count,
|
||||
SUPERPAGE_2MB_SHIFT as u32,
|
||||
0,
|
||||
&extents_init_slice[0usize..count as usize],
|
||||
)?;
|
||||
|
||||
pfn = pfn_base_idx;
|
||||
for mfn in extents {
|
||||
for k in 0..SUPERPAGE_2MB_NR_PFNS {
|
||||
p2m[pfn as usize] = mfn + k;
|
||||
pfn += 1;
|
||||
}
|
||||
}
|
||||
pfn_base_idx = pfn;
|
||||
}
|
||||
|
||||
let mut j = pfn_base_idx - pfn_base;
|
||||
loop {
|
||||
if j >= pages {
|
||||
break;
|
||||
}
|
||||
|
||||
let allocsz = (1024 * 1024).min(pages - j);
|
||||
let p2m_idx = (pfn_base + j) as usize;
|
||||
let p2m_end_idx = p2m_idx + allocsz as usize;
|
||||
let input_extent_starts = &p2m[p2m_idx..p2m_end_idx];
|
||||
let result =
|
||||
setup
|
||||
.call
|
||||
.populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?;
|
||||
|
||||
if result.len() != allocsz as usize {
|
||||
return Err(Error::PopulatePhysmapFailed(
|
||||
allocsz as usize,
|
||||
result.len(),
|
||||
input_extent_starts.len(),
|
||||
));
|
||||
}
|
||||
|
||||
for (i, item) in result.iter().enumerate() {
|
||||
let p = (pfn_base + j + i as u64) as usize;
|
||||
let m = *item;
|
||||
p2m[p] = m;
|
||||
}
|
||||
j += allocsz;
|
||||
}
|
||||
}
|
||||
|
||||
setup.phys.load_p2m(p2m);
|
||||
setup.call.claim_pages(setup.domid, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let pg_pfn = state.page_table_segment.pfn;
|
||||
let pg_mfn = setup.phys.p2m[pg_pfn as usize];
|
||||
setup.phys.unmap(pg_pfn)?;
|
||||
setup.phys.unmap(state.p2m_segment.pfn)?;
|
||||
setup
|
||||
.call
|
||||
.mmuext(setup.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let pg_pfn = state.page_table_segment.pfn;
|
||||
let pg_mfn = setup.phys.p2m[pg_pfn as usize];
|
||||
let mut vcpu = VcpuGuestContext::default();
|
||||
vcpu.user_regs.rip = state.image_info.virt_entry;
|
||||
vcpu.user_regs.rsp =
|
||||
state.image_info.virt_base + (state.boot_stack_segment.pfn + 1) * self.page_size();
|
||||
vcpu.user_regs.rsi =
|
||||
state.image_info.virt_base + (state.start_info_segment.pfn) * self.page_size();
|
||||
vcpu.user_regs.rflags = 1 << 9;
|
||||
vcpu.debugreg[6] = 0xffff0ff0;
|
||||
vcpu.debugreg[7] = 0x00000400;
|
||||
vcpu.flags = VGCF_IN_KERNEL | VGCF_ONLINE;
|
||||
let cr3_pfn = pg_mfn;
|
||||
debug!(
|
||||
"cr3: pfn {:#x} mfn {:#x}",
|
||||
state.page_table_segment.pfn, cr3_pfn
|
||||
);
|
||||
vcpu.ctrlreg[3] = cr3_pfn << 12;
|
||||
vcpu.user_regs.ds = 0x0;
|
||||
vcpu.user_regs.es = 0x0;
|
||||
vcpu.user_regs.fs = 0x0;
|
||||
vcpu.user_regs.gs = 0x0;
|
||||
vcpu.user_regs.ss = 0xe02b;
|
||||
vcpu.user_regs.cs = 0xe033;
|
||||
vcpu.kernel_ss = vcpu.user_regs.ss as u64;
|
||||
vcpu.kernel_sp = vcpu.user_regs.rsp;
|
||||
debug!("vcpu context: {:?}", vcpu);
|
||||
setup.call.set_vcpu_context(setup.domid, 0, &vcpu)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
20
libs/xen/xenevtchn/Cargo.toml
Normal file
20
libs/xen/xenevtchn/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "xenevtchn"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
[dependencies.nix]
|
||||
workspace = true
|
||||
features = ["ioctl"]
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xenevtchn-simple"
|
||||
path = "examples/simple.rs"
|
11
libs/xen/xenevtchn/examples/simple.rs
Normal file
11
libs/xen/xenevtchn/examples/simple.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use xenevtchn::error::Result;
|
||||
use xenevtchn::EventChannel;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut channel = EventChannel::open()?;
|
||||
println!("Channel opened.");
|
||||
let port = channel.bind_unbound_port(1)?;
|
||||
println!("port: {}", port);
|
||||
channel.unbind(port)?;
|
||||
Ok(())
|
||||
}
|
11
libs/xen/xenevtchn/src/error.rs
Normal file
11
libs/xen/xenevtchn/src/error.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use std::io;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("kernel error")]
|
||||
Kernel(#[from] nix::errno::Errno),
|
||||
#[error("io issue encountered")]
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
66
libs/xen/xenevtchn/src/lib.rs
Normal file
66
libs/xen/xenevtchn/src/lib.rs
Normal file
@ -0,0 +1,66 @@
|
||||
pub mod error;
|
||||
pub mod sys;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::sys::{BindInterdomain, BindUnboundPort, BindVirq, Notify, UnbindPort};
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
pub struct EventChannel {
|
||||
pub handle: File,
|
||||
}
|
||||
|
||||
impl EventChannel {
|
||||
pub fn open() -> Result<EventChannel> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/xen/evtchn")?;
|
||||
Ok(EventChannel { handle: file })
|
||||
}
|
||||
|
||||
pub fn bind_virq(&mut self, virq: u32) -> Result<u32> {
|
||||
unsafe {
|
||||
let mut request = BindVirq { virq };
|
||||
Ok(sys::bind_virq(self.handle.as_raw_fd(), &mut request)? as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_interdomain(&mut self, domid: u32, port: u32) -> Result<u32> {
|
||||
unsafe {
|
||||
let mut request = BindInterdomain {
|
||||
remote_domain: domid,
|
||||
remote_port: port,
|
||||
};
|
||||
Ok(sys::bind_interdomain(self.handle.as_raw_fd(), &mut request)? as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_unbound_port(&mut self, domid: u32) -> Result<u32> {
|
||||
unsafe {
|
||||
let mut request = BindUnboundPort {
|
||||
remote_domain: domid,
|
||||
};
|
||||
Ok(sys::bind_unbound_port(self.handle.as_raw_fd(), &mut request)? as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unbind(&mut self, port: u32) -> Result<u32> {
|
||||
unsafe {
|
||||
let mut request = UnbindPort { port };
|
||||
Ok(sys::unbind(self.handle.as_raw_fd(), &mut request)? as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&mut self, port: u32) -> Result<u32> {
|
||||
unsafe {
|
||||
let mut request = Notify { port };
|
||||
Ok(sys::notify(self.handle.as_raw_fd(), &mut request)? as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Result<u32> {
|
||||
unsafe { Ok(sys::reset(self.handle.as_raw_fd())? as u32) }
|
||||
}
|
||||
}
|
43
libs/xen/xenevtchn/src/sys.rs
Normal file
43
libs/xen/xenevtchn/src/sys.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use nix::{ioctl_none, ioctl_readwrite_bad, request_code_none};
|
||||
use std::ffi::c_uint;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BindVirq {
|
||||
pub virq: c_uint,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BindInterdomain {
|
||||
pub remote_domain: c_uint,
|
||||
pub remote_port: c_uint,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BindUnboundPort {
|
||||
pub remote_domain: c_uint,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UnbindPort {
|
||||
pub port: c_uint,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Notify {
|
||||
pub port: c_uint,
|
||||
}
|
||||
|
||||
ioctl_readwrite_bad!(bind_virq, request_code_none!(b'E', 0), BindVirq);
|
||||
ioctl_readwrite_bad!(
|
||||
bind_interdomain,
|
||||
request_code_none!(b'E', 1),
|
||||
BindInterdomain
|
||||
);
|
||||
ioctl_readwrite_bad!(
|
||||
bind_unbound_port,
|
||||
request_code_none!(b'E', 2),
|
||||
BindUnboundPort
|
||||
);
|
||||
ioctl_readwrite_bad!(unbind, request_code_none!(b'E', 3), UnbindPort);
|
||||
ioctl_readwrite_bad!(notify, request_code_none!(b'E', 4), Notify);
|
||||
ioctl_none!(reset, b'E', 5);
|
21
libs/xen/xenstore/Cargo.toml
Normal file
21
libs/xen/xenstore/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "xenstore"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
[dependencies.bytemuck]
|
||||
workspace = true
|
||||
features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "xenstore-ls"
|
||||
path = "examples/list.rs"
|
35
libs/xen/xenstore/examples/list.rs
Normal file
35
libs/xen/xenstore/examples/list.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use xenstore::client::{XsdClient, XsdInterface};
|
||||
use xenstore::error::Result;
|
||||
use xenstore::sys::XSD_ERROR_EINVAL;
|
||||
|
||||
fn list_recursive(client: &mut XsdClient, level: usize, path: &str) -> Result<()> {
|
||||
let children = match client.list(path) {
|
||||
Ok(children) => children,
|
||||
Err(error) => {
|
||||
return if error.to_string() == XSD_ERROR_EINVAL.error {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for child in children {
|
||||
let full = format!("{}/{}", if path == "/" { "" } else { path }, child);
|
||||
let value = client.read(full.as_str())?;
|
||||
println!(
|
||||
"{}{} = {:?}",
|
||||
" ".repeat(level),
|
||||
child,
|
||||
String::from_utf8(value)?
|
||||
);
|
||||
list_recursive(client, level + 1, full.as_str())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut client = XsdClient::open()?;
|
||||
list_recursive(&mut client, 0, "/")?;
|
||||
Ok(())
|
||||
}
|
109
libs/xen/xenstore/src/bus.rs
Normal file
109
libs/xen/xenstore/src/bus.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sys::{XsdMessageHeader, XSD_ERROR};
|
||||
use std::ffi::CString;
|
||||
use std::fs::metadata;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::net::Shutdown;
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
const XEN_BUS_PATHS: &[&str] = &["/var/run/xenstored/socket"];
|
||||
|
||||
fn find_bus_path() -> Option<String> {
|
||||
for path in XEN_BUS_PATHS {
|
||||
match metadata(path) {
|
||||
Ok(_) => return Some(String::from(*path)),
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub struct XsdSocket {
|
||||
handle: UnixStream,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XsdResponse {
|
||||
pub header: XsdMessageHeader,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl XsdResponse {
|
||||
pub fn parse_string(&self) -> Result<String> {
|
||||
Ok(CString::from_vec_with_nul(self.payload.clone())?.into_string()?)
|
||||
}
|
||||
|
||||
pub fn parse_string_vec(&self) -> Result<Vec<String>> {
|
||||
let mut strings: Vec<String> = Vec::new();
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
for b in &self.payload {
|
||||
if *b == 0 {
|
||||
let string = String::from_utf8(buffer.clone())?;
|
||||
strings.push(string);
|
||||
buffer.clear();
|
||||
continue;
|
||||
}
|
||||
buffer.push(*b);
|
||||
}
|
||||
Ok(strings)
|
||||
}
|
||||
|
||||
pub fn parse_bool(&self) -> Result<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl XsdSocket {
|
||||
pub fn dial() -> Result<XsdSocket> {
|
||||
let path = match find_bus_path() {
|
||||
Some(path) => path,
|
||||
None => return Err(Error::BusNotFound),
|
||||
};
|
||||
let stream = UnixStream::connect(path)?;
|
||||
Ok(XsdSocket { handle: stream })
|
||||
}
|
||||
|
||||
pub fn send(&mut self, tx: u32, typ: u32, buf: &[u8]) -> Result<XsdResponse> {
|
||||
let header = XsdMessageHeader {
|
||||
typ,
|
||||
req: 0,
|
||||
tx,
|
||||
len: buf.len() as u32,
|
||||
};
|
||||
self.handle.write_all(bytemuck::bytes_of(&header))?;
|
||||
self.handle.write_all(buf)?;
|
||||
let mut result_buf = vec![0u8; size_of::<XsdMessageHeader>()];
|
||||
self.handle.read_exact(result_buf.as_mut_slice())?;
|
||||
let result_header = bytemuck::from_bytes::<XsdMessageHeader>(&result_buf);
|
||||
let mut payload = vec![0u8; result_header.len as usize];
|
||||
self.handle.read_exact(payload.as_mut_slice())?;
|
||||
if result_header.typ == XSD_ERROR {
|
||||
let error = CString::from_vec_with_nul(payload)?;
|
||||
return Err(Error::ResponseError(error.into_string()?));
|
||||
}
|
||||
let response = XsdResponse { header, payload };
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn send_single(&mut self, tx: u32, typ: u32, string: &str) -> Result<XsdResponse> {
|
||||
let text = CString::new(string)?;
|
||||
let buf = text.as_bytes_with_nul();
|
||||
self.send(tx, typ, buf)
|
||||
}
|
||||
|
||||
pub fn send_multiple(&mut self, tx: u32, typ: u32, array: &[&str]) -> Result<XsdResponse> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
for item in array {
|
||||
buf.extend_from_slice(item.as_bytes());
|
||||
buf.push(0);
|
||||
}
|
||||
self.send(tx, typ, buf.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for XsdSocket {
|
||||
fn drop(&mut self) {
|
||||
self.handle.shutdown(Shutdown::Both).unwrap()
|
||||
}
|
||||
}
|
259
libs/xen/xenstore/src/client.rs
Normal file
259
libs/xen/xenstore/src/client.rs
Normal file
@ -0,0 +1,259 @@
|
||||
use crate::bus::XsdSocket;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sys::{
|
||||
XSD_DIRECTORY, XSD_GET_DOMAIN_PATH, XSD_INTRODUCE, XSD_MKDIR, XSD_READ, XSD_RM, XSD_SET_PERMS,
|
||||
XSD_TRANSACTION_END, XSD_TRANSACTION_START, XSD_WRITE,
|
||||
};
|
||||
use log::trace;
|
||||
use std::ffi::CString;
|
||||
|
||||
pub const XS_PERM_NONE: u32 = 0x00;
|
||||
pub const XS_PERM_READ: u32 = 0x01;
|
||||
pub const XS_PERM_WRITE: u32 = 0x02;
|
||||
pub const XS_PERM_READ_WRITE: u32 = XS_PERM_READ | XS_PERM_WRITE;
|
||||
|
||||
pub struct XsdClient {
|
||||
pub socket: XsdSocket,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct XsPermission {
|
||||
pub id: u32,
|
||||
pub perms: u32,
|
||||
}
|
||||
|
||||
impl XsPermission {
|
||||
pub fn encode(&self) -> Result<String> {
|
||||
let c = match self.perms {
|
||||
XS_PERM_READ_WRITE => 'b',
|
||||
XS_PERM_WRITE => 'w',
|
||||
XS_PERM_READ => 'r',
|
||||
XS_PERM_NONE => 'n',
|
||||
_ => return Err(Error::InvalidPermissions),
|
||||
};
|
||||
Ok(format!("{}{}", c, self.id))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait XsdInterface {
|
||||
fn list(&mut self, path: &str) -> Result<Vec<String>>;
|
||||
fn read(&mut self, path: &str) -> Result<Vec<u8>>;
|
||||
fn read_string(&mut self, path: &str) -> Result<String>;
|
||||
fn write(&mut self, path: &str, data: Vec<u8>) -> Result<bool>;
|
||||
fn write_string(&mut self, path: &str, data: &str) -> Result<bool>;
|
||||
fn mkdir(&mut self, path: &str) -> Result<bool>;
|
||||
fn rm(&mut self, path: &str) -> Result<bool>;
|
||||
fn set_perms(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool>;
|
||||
|
||||
fn mknod(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool> {
|
||||
let result1 = self.write_string(path, "")?;
|
||||
let result2 = self.set_perms(path, perms)?;
|
||||
Ok(result1 && result2)
|
||||
}
|
||||
|
||||
fn read_string_optional(&mut self, path: &str) -> Result<Option<String>> {
|
||||
Ok(match self.read_string(path) {
|
||||
Ok(value) => Some(value),
|
||||
Err(error) => {
|
||||
if error.is_noent_response() {
|
||||
None
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn list_any(&mut self, path: &str) -> Result<Vec<String>> {
|
||||
Ok(match self.list(path) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
if error.is_noent_response() {
|
||||
Vec::new()
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl XsdClient {
|
||||
pub fn open() -> Result<XsdClient> {
|
||||
let socket = XsdSocket::dial()?;
|
||||
Ok(XsdClient { socket })
|
||||
}
|
||||
|
||||
fn list(&mut self, tx: u32, path: &str) -> Result<Vec<String>> {
|
||||
trace!("list tx={tx} path={path}");
|
||||
let response = self.socket.send_single(tx, XSD_DIRECTORY, path)?;
|
||||
response.parse_string_vec()
|
||||
}
|
||||
|
||||
fn read(&mut self, tx: u32, path: &str) -> Result<Vec<u8>> {
|
||||
trace!("read tx={tx} path={path}");
|
||||
let response = self.socket.send_single(tx, XSD_READ, path)?;
|
||||
Ok(response.payload)
|
||||
}
|
||||
|
||||
fn write(&mut self, tx: u32, path: &str, data: Vec<u8>) -> Result<bool> {
|
||||
trace!("write tx={tx} path={path} data={:?}", data);
|
||||
let mut buffer = Vec::new();
|
||||
let path = CString::new(path)?;
|
||||
buffer.extend_from_slice(path.as_bytes_with_nul());
|
||||
buffer.extend_from_slice(data.as_slice());
|
||||
let response = self.socket.send(tx, XSD_WRITE, buffer.as_slice())?;
|
||||
response.parse_bool()
|
||||
}
|
||||
|
||||
fn mkdir(&mut self, tx: u32, path: &str) -> Result<bool> {
|
||||
trace!("mkdir tx={tx} path={path}");
|
||||
self.socket.send_single(tx, XSD_MKDIR, path)?.parse_bool()
|
||||
}
|
||||
|
||||
fn rm(&mut self, tx: u32, path: &str) -> Result<bool> {
|
||||
trace!("rm tx={tx} path={path}");
|
||||
let result = self.socket.send_single(tx, XSD_RM, path);
|
||||
if let Err(error) = result {
|
||||
if error.is_noent_response() {
|
||||
return Ok(true);
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
result.unwrap().parse_bool()
|
||||
}
|
||||
|
||||
fn set_perms(&mut self, tx: u32, path: &str, perms: &[XsPermission]) -> Result<bool> {
|
||||
trace!("set_perms tx={tx} path={path} perms={:?}", perms);
|
||||
let mut items: Vec<String> = Vec::new();
|
||||
items.push(path.to_string());
|
||||
for perm in perms {
|
||||
items.push(perm.encode()?);
|
||||
}
|
||||
let items_str: Vec<&str> = items.iter().map(|x| x.as_str()).collect();
|
||||
let response = self.socket.send_multiple(tx, XSD_SET_PERMS, &items_str)?;
|
||||
response.parse_bool()
|
||||
}
|
||||
|
||||
pub fn transaction(&mut self) -> Result<XsdTransaction> {
|
||||
trace!("transaction start");
|
||||
let response = self.socket.send_single(0, XSD_TRANSACTION_START, "")?;
|
||||
let str = response.parse_string()?;
|
||||
let tx = str.parse::<u32>()?;
|
||||
Ok(XsdTransaction { client: self, tx })
|
||||
}
|
||||
|
||||
pub fn get_domain_path(&mut self, domid: u32) -> Result<String> {
|
||||
let response =
|
||||
self.socket
|
||||
.send_single(0, XSD_GET_DOMAIN_PATH, domid.to_string().as_str())?;
|
||||
response.parse_string()
|
||||
}
|
||||
|
||||
pub fn introduce_domain(&mut self, domid: u32, mfn: u64, evtchn: u32) -> Result<bool> {
|
||||
trace!("introduce domain domid={domid} mfn={mfn} evtchn={evtchn}");
|
||||
let response = self.socket.send_multiple(
|
||||
0,
|
||||
XSD_INTRODUCE,
|
||||
&[
|
||||
domid.to_string().as_str(),
|
||||
mfn.to_string().as_str(),
|
||||
evtchn.to_string().as_str(),
|
||||
],
|
||||
)?;
|
||||
response.parse_bool()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XsdTransaction<'a> {
|
||||
client: &'a mut XsdClient,
|
||||
tx: u32,
|
||||
}
|
||||
|
||||
impl XsdInterface for XsdClient {
|
||||
fn list(&mut self, path: &str) -> Result<Vec<String>> {
|
||||
self.list(0, path)
|
||||
}
|
||||
|
||||
fn read(&mut self, path: &str) -> Result<Vec<u8>> {
|
||||
self.read(0, path)
|
||||
}
|
||||
|
||||
fn read_string(&mut self, path: &str) -> Result<String> {
|
||||
Ok(String::from_utf8(self.read(0, path)?)?)
|
||||
}
|
||||
|
||||
fn write(&mut self, path: &str, data: Vec<u8>) -> Result<bool> {
|
||||
self.write(0, path, data)
|
||||
}
|
||||
|
||||
fn write_string(&mut self, path: &str, data: &str) -> Result<bool> {
|
||||
self.write(0, path, data.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn mkdir(&mut self, path: &str) -> Result<bool> {
|
||||
self.mkdir(0, path)
|
||||
}
|
||||
|
||||
fn rm(&mut self, path: &str) -> Result<bool> {
|
||||
self.rm(0, path)
|
||||
}
|
||||
|
||||
fn set_perms(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool> {
|
||||
self.set_perms(0, path, perms)
|
||||
}
|
||||
}
|
||||
|
||||
impl XsdInterface for XsdTransaction<'_> {
|
||||
fn list(&mut self, path: &str) -> Result<Vec<String>> {
|
||||
self.client.list(self.tx, path)
|
||||
}
|
||||
|
||||
fn read(&mut self, path: &str) -> Result<Vec<u8>> {
|
||||
self.client.read(self.tx, path)
|
||||
}
|
||||
|
||||
fn read_string(&mut self, path: &str) -> Result<String> {
|
||||
Ok(String::from_utf8(self.client.read(self.tx, path)?)?)
|
||||
}
|
||||
|
||||
fn write(&mut self, path: &str, data: Vec<u8>) -> Result<bool> {
|
||||
self.client.write(self.tx, path, data)
|
||||
}
|
||||
|
||||
fn write_string(&mut self, path: &str, data: &str) -> Result<bool> {
|
||||
self.client.write(self.tx, path, data.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn mkdir(&mut self, path: &str) -> Result<bool> {
|
||||
self.client.mkdir(self.tx, path)
|
||||
}
|
||||
|
||||
fn rm(&mut self, path: &str) -> Result<bool> {
|
||||
self.client.rm(self.tx, path)
|
||||
}
|
||||
|
||||
fn set_perms(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool> {
|
||||
self.client.set_perms(self.tx, path, perms)
|
||||
}
|
||||
}
|
||||
|
||||
impl XsdTransaction<'_> {
|
||||
pub fn end(&mut self, abort: bool) -> Result<bool> {
|
||||
let abort_str = if abort { "F" } else { "T" };
|
||||
|
||||
trace!("transaction end abort={}", abort);
|
||||
self.client
|
||||
.socket
|
||||
.send_single(self.tx, XSD_TRANSACTION_END, abort_str)?
|
||||
.parse_bool()
|
||||
}
|
||||
|
||||
pub fn commit(&mut self) -> Result<bool> {
|
||||
self.end(false)
|
||||
}
|
||||
|
||||
pub fn abort(&mut self) -> Result<bool> {
|
||||
self.end(true)
|
||||
}
|
||||
}
|
40
libs/xen/xenstore/src/error.rs
Normal file
40
libs/xen/xenstore/src/error.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use std::ffi::{FromVecWithNulError, IntoStringError, NulError};
|
||||
use std::io;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("io issue encountered")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("utf8 string decode failed")]
|
||||
Utf8DecodeString(#[from] FromUtf8Error),
|
||||
#[error("utf8 str decode failed")]
|
||||
Utf8DecodeStr(#[from] Utf8Error),
|
||||
#[error("unable to decode cstring as utf8")]
|
||||
Utf8DecodeCstring(#[from] IntoStringError),
|
||||
#[error("nul byte found in string")]
|
||||
NulByteFoundString(#[from] NulError),
|
||||
#[error("unable to find nul byte in vec")]
|
||||
VecNulByteNotFound(#[from] FromVecWithNulError),
|
||||
#[error("unable to parse integer")]
|
||||
ParseInt(#[from] ParseIntError),
|
||||
#[error("bus was not found on any available path")]
|
||||
BusNotFound,
|
||||
#[error("store responded with error: `{0}`")]
|
||||
ResponseError(String),
|
||||
#[error("invalid permissions provided")]
|
||||
InvalidPermissions,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn is_noent_response(&self) -> bool {
|
||||
match self {
|
||||
Error::ResponseError(message) => message == "ENOENT",
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
4
libs/xen/xenstore/src/lib.rs
Normal file
4
libs/xen/xenstore/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod bus;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod sys;
|
141
libs/xen/xenstore/src/sys.rs
Normal file
141
libs/xen/xenstore/src/sys.rs
Normal file
@ -0,0 +1,141 @@
|
||||
/// Handwritten protocol definitions for XenStore.
|
||||
/// Used xen/include/public/io/xs_wire.h as a reference.
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use libc;
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct XsdMessageHeader {
|
||||
pub typ: u32,
|
||||
pub req: u32,
|
||||
pub tx: u32,
|
||||
pub len: u32,
|
||||
}
|
||||
|
||||
pub const XSD_CONTROL: u32 = 0;
|
||||
pub const XSD_DIRECTORY: u32 = 1;
|
||||
pub const XSD_READ: u32 = 2;
|
||||
pub const XSD_GET_PERMS: u32 = 3;
|
||||
pub const XSD_WATCH: u32 = 4;
|
||||
pub const XSD_UNWATCH: u32 = 5;
|
||||
pub const XSD_TRANSACTION_START: u32 = 6;
|
||||
pub const XSD_TRANSACTION_END: u32 = 7;
|
||||
pub const XSD_INTRODUCE: u32 = 8;
|
||||
pub const XSD_RELEASE: u32 = 9;
|
||||
pub const XSD_GET_DOMAIN_PATH: u32 = 10;
|
||||
pub const XSD_WRITE: u32 = 11;
|
||||
pub const XSD_MKDIR: u32 = 12;
|
||||
pub const XSD_RM: u32 = 13;
|
||||
pub const XSD_SET_PERMS: u32 = 14;
|
||||
pub const XSD_WATCH_EVENT: u32 = 15;
|
||||
pub const XSD_ERROR: u32 = 16;
|
||||
pub const XSD_IS_DOMAIN_INTRODUCED: u32 = 17;
|
||||
pub const XSD_RESUME: u32 = 18;
|
||||
pub const XSD_SET_TARGET: u32 = 19;
|
||||
pub const XSD_RESET_WATCHES: u32 = XSD_SET_TARGET + 2;
|
||||
pub const XSD_DIRECTORY_PART: u32 = 20;
|
||||
pub const XSD_TYPE_COUNT: u32 = 21;
|
||||
pub const XSD_INVALID: u32 = 0xffff;
|
||||
|
||||
pub const XSD_WRITE_NONE: &str = "NONE";
|
||||
pub const XSD_WRITE_CREATE: &str = "CREATE";
|
||||
pub const XSD_WRITE_CREATE_EXCL: &str = "CREATE|EXCL";
|
||||
|
||||
#[repr(C)]
|
||||
pub struct XsdError<'a> {
|
||||
pub num: i32,
|
||||
pub error: &'a str,
|
||||
}
|
||||
|
||||
pub const XSD_ERROR_EINVAL: XsdError = XsdError {
|
||||
num: libc::EINVAL,
|
||||
error: "EINVAL",
|
||||
};
|
||||
pub const XSD_ERROR_EACCES: XsdError = XsdError {
|
||||
num: libc::EACCES,
|
||||
error: "EACCES",
|
||||
};
|
||||
pub const XSD_ERROR_EEXIST: XsdError = XsdError {
|
||||
num: libc::EEXIST,
|
||||
error: "EEXIST",
|
||||
};
|
||||
pub const XSD_ERROR_EISDIR: XsdError = XsdError {
|
||||
num: libc::EISDIR,
|
||||
error: "EISDIR",
|
||||
};
|
||||
pub const XSD_ERROR_ENOENT: XsdError = XsdError {
|
||||
num: libc::ENOENT,
|
||||
error: "ENOENT",
|
||||
};
|
||||
pub const XSD_ERROR_ENOMEM: XsdError = XsdError {
|
||||
num: libc::ENOMEM,
|
||||
error: "ENOMEM",
|
||||
};
|
||||
pub const XSD_ERROR_ENOSPC: XsdError = XsdError {
|
||||
num: libc::ENOSPC,
|
||||
error: "ENOSPC",
|
||||
};
|
||||
pub const XSD_ERROR_EIO: XsdError = XsdError {
|
||||
num: libc::EIO,
|
||||
error: "EIO",
|
||||
};
|
||||
pub const XSD_ERROR_ENOTEMPTY: XsdError = XsdError {
|
||||
num: libc::ENOTEMPTY,
|
||||
error: "ENOTEMPTY",
|
||||
};
|
||||
pub const XSD_ERROR_ENOSYS: XsdError = XsdError {
|
||||
num: libc::ENOSYS,
|
||||
error: "ENOSYS",
|
||||
};
|
||||
pub const XSD_ERROR_EROFS: XsdError = XsdError {
|
||||
num: libc::EROFS,
|
||||
error: "EROFS",
|
||||
};
|
||||
pub const XSD_ERROR_EBUSY: XsdError = XsdError {
|
||||
num: libc::EBUSY,
|
||||
error: "EBUSY",
|
||||
};
|
||||
pub const XSD_ERROR_EAGAIN: XsdError = XsdError {
|
||||
num: libc::EAGAIN,
|
||||
error: "EAGAIN",
|
||||
};
|
||||
pub const XSD_ERROR_EISCONN: XsdError = XsdError {
|
||||
num: libc::EISCONN,
|
||||
error: "EISCONN",
|
||||
};
|
||||
pub const XSD_ERROR_E2BIG: XsdError = XsdError {
|
||||
num: libc::E2BIG,
|
||||
error: "E2BIG",
|
||||
};
|
||||
pub const XSD_ERROR_EPERM: XsdError = XsdError {
|
||||
num: libc::EPERM,
|
||||
error: "EPERM",
|
||||
};
|
||||
|
||||
pub const XSD_WATCH_PATH: u32 = 0;
|
||||
pub const XSD_WATCH_TOKEN: u32 = 1;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct XenDomainInterface {
|
||||
req: [i8; 1024],
|
||||
rsp: [i8; 1024],
|
||||
req_cons: u32,
|
||||
req_prod: u32,
|
||||
rsp_cons: u32,
|
||||
rsp_prod: u32,
|
||||
server_features: u32,
|
||||
connection: u32,
|
||||
error: u32,
|
||||
}
|
||||
|
||||
pub const XS_PAYLOAD_MAX: u32 = 4096;
|
||||
pub const XS_ABS_PATH_MAX: u32 = 3072;
|
||||
pub const XS_REL_PATH_MAX: u32 = 2048;
|
||||
pub const XS_SERVER_FEATURE_RECONNECTION: u32 = 1;
|
||||
pub const XS_SERVER_FEATURE_ERROR: u32 = 2;
|
||||
pub const XS_CONNECTED: u32 = 0;
|
||||
pub const XS_RECONNECT: u32 = 1;
|
||||
pub const XS_ERROR_NONE: u32 = 0;
|
||||
pub const XS_ERROR_COMM: u32 = 1;
|
||||
pub const XS_ERROR_RINGIDX: u32 = 2;
|
||||
pub const XS_ERROR_PROTO: u32 = 3;
|
Reference in New Issue
Block a user