Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ members = [
"crates/xen/xenclient",
"crates/xen/xenevtchn",
"crates/xen/xengnt",
"crates/xen/xenvchan",
"crates/xen/xenvchan-sys",
"crates/xen/xenplatform",
"crates/xen/xenstore",
]
Expand Down
44 changes: 38 additions & 6 deletions crates/xen/xencall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ use std::sync::Arc;
use std::time::Duration;
use sys::{
CpuId, E820Entry, ForeignMemoryMap, PhysdevMapPirq, SetDomainHandle, Sysctl, SysctlCputopo,
SysctlCputopoinfo, SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlReadconsole,
SysctlSetCpuFreqGov, SysctlValue, VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP,
HYPERVISOR_SYSCTL, PHYSDEVOP_MAP_PIRQ, XEN_DOMCTL_MAX_INTERFACE_VERSION,
SysctlCputopoinfo, SysctlGetdomaininfolist, SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue,
SysctlReadconsole, SysctlSetCpuFreqGov, SysctlValue, VcpuGuestContextAny,
HYPERVISOR_PHYSDEV_OP, HYPERVISOR_SYSCTL, PHYSDEVOP_MAP_PIRQ, XEN_DOMCTL_MAX_INTERFACE_VERSION,
XEN_DOMCTL_MIN_INTERFACE_VERSION, XEN_DOMCTL_SETDOMAINHANDLE, XEN_MEM_SET_MEMORY_MAP,
XEN_SYSCTL_CPUTOPOINFO, XEN_SYSCTL_MAX_INTERFACE_VERSION, XEN_SYSCTL_MIN_INTERFACE_VERSION,
XEN_SYSCTL_PHYSINFO, XEN_SYSCTL_PM_OP, XEN_SYSCTL_PM_OP_DISABLE_TURBO,
XEN_SYSCTL_PM_OP_ENABLE_TURBO, XEN_SYSCTL_PM_OP_SET_CPUFREQ_GOV, XEN_SYSCTL_READCONSOLE,
XEN_SYSCTL_CPUTOPOINFO, XEN_SYSCTL_GETDOMAININFOLIST, XEN_SYSCTL_MAX_INTERFACE_VERSION,
XEN_SYSCTL_MIN_INTERFACE_VERSION, XEN_SYSCTL_PHYSINFO, XEN_SYSCTL_PM_OP,
XEN_SYSCTL_PM_OP_DISABLE_TURBO, XEN_SYSCTL_PM_OP_ENABLE_TURBO,
XEN_SYSCTL_PM_OP_SET_CPUFREQ_GOV, XEN_SYSCTL_READCONSOLE,
};
use tokio::time::sleep;

Expand Down Expand Up @@ -417,6 +418,37 @@ impl XenCall {
Ok(unsafe { domctl.value.get_domain_info })
}

/// Enumerate Xen domains starting from `first_domid`.
///
/// Uses XEN_SYSCTL_getdomaininfolist which correctly enumerates domains
/// on all Xen versions (unlike XEN_DOMCTL_GETDOMAININFO which does
/// exact domid lookup on Xen 4.17+).
pub async fn get_domain_info_list(
&self,
first_domid: u16,
max_domains: u32,
) -> Result<Vec<GetDomainInfo>> {
let mut buffer = vec![GetDomainInfo::default(); max_domains as usize];
let mut sysctl = Sysctl {
cmd: XEN_SYSCTL_GETDOMAININFOLIST,
interface_version: self.sysctl_interface_version,
value: SysctlValue {
getdomaininfolist: SysctlGetdomaininfolist {
first_domain: first_domid,
pad: 0,
max_domains,
buffer: buffer.as_mut_ptr() as u64,
num_domains: 0,
},
},
};
self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong)
.await?;
let count = unsafe { sysctl.value.getdomaininfolist.num_domains } as usize;
buffer.truncate(count);
Ok(buffer)
}

pub async fn create_domain(&self, create_domain: CreateDomain) -> Result<u32> {
trace!(
"domctl fd={} create_domain create_domain={:?}",
Expand Down
15 changes: 15 additions & 0 deletions crates/xen/xencall/src/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,10 +807,24 @@ pub struct SysctlCputopoinfo {
pub handle: c_ulong,
}

/// Buffer-based domain enumeration via XEN_SYSCTL_getdomaininfolist.
/// Returns info for domains with domid >= first_domain, up to max_domains.
/// Layout matches xen/include/public/sysctl.h xen_sysctl_getdomaininfolist.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SysctlGetdomaininfolist {
pub first_domain: u16,
pub pad: u16,
pub max_domains: u32,
pub buffer: u64, // XEN_GUEST_HANDLE_64(xen_domctl_getdomaininfo_t)
pub num_domains: u32,
}

#[repr(C)]
pub union SysctlValue {
pub console: SysctlReadconsole,
pub cputopoinfo: SysctlCputopoinfo,
pub getdomaininfolist: SysctlGetdomaininfolist,
pub pm_op: SysctlPmOp,
pub phys_info: SysctlPhysinfo,
pub pad: [u8; 128],
Expand All @@ -825,6 +839,7 @@ pub struct Sysctl {

pub const XEN_SYSCTL_READCONSOLE: u32 = 1;
pub const XEN_SYSCTL_PHYSINFO: u32 = 3;
pub const XEN_SYSCTL_GETDOMAININFOLIST: u32 = 6;
pub const XEN_SYSCTL_PM_OP: u32 = 12;
pub const XEN_SYSCTL_CPUTOPOINFO: u32 = 16;

Expand Down
65 changes: 40 additions & 25 deletions crates/xen/xenstore/src/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,6 @@ const XEN_BUS_PATHS: &[&str] = &["/var/run/xenstored/socket", "/dev/xen/xenbus"]
const XEN_BUS_MAX_PAYLOAD_SIZE: usize = 4096;
const XEN_BUS_MAX_PACKET_SIZE: usize = XsdMessageHeader::SIZE + XEN_BUS_MAX_PAYLOAD_SIZE;

async fn find_bus_path() -> Option<(&'static str, bool)> {
for path in XEN_BUS_PATHS {
match metadata(path).await {
Ok(metadata) => {
return Some((path, metadata.file_type().is_socket()));
}
Err(_) => continue,
}
}
None
}

struct WatchState {
sender: Sender<String>,
}
Expand All @@ -69,21 +57,48 @@ pub struct XsdSocket {

impl XsdSocket {
pub async fn open() -> Result<XsdSocket> {
let (path, socket) = match find_bus_path().await {
Some(path) => path,
None => return Err(Error::BusNotFound),
};
let mut saw_path = false;
let mut last_error = None;

for path in XEN_BUS_PATHS {
let metadata = match metadata(path).await {
Ok(metadata) => metadata,
Err(_) => continue,
};
saw_path = true;

let file = if metadata.file_type().is_socket() {
match UnixStream::connect(path).await {
Ok(stream) => {
let stream = stream.into_std()?;
stream.set_nonblocking(false)?;
unsafe { File::from_raw_fd(stream.into_raw_fd()) }
}
Err(error) => {
warn!("failed to connect to xenstore socket at {path}: {error}");
last_error = Some(Error::from(error));
continue;
}
}
} else {
match File::options().read(true).write(true).open(path).await {
Ok(file) => file,
Err(error) => {
warn!("failed to open xenstore bus at {path}: {error}");
last_error = Some(Error::from(error));
continue;
}
}
};

let file = if socket {
let stream = UnixStream::connect(path).await?;
let stream = stream.into_std()?;
stream.set_nonblocking(false)?;
unsafe { File::from_raw_fd(stream.into_raw_fd()) }
} else {
File::options().read(true).write(true).open(path).await?
};
return XsdSocket::from_handle(file).await;
}

XsdSocket::from_handle(file).await
if saw_path {
Err(last_error.unwrap_or(Error::BusNotFound))
} else {
Err(Error::BusNotFound)
}
}

pub async fn from_handle(handle: File) -> Result<XsdSocket> {
Expand Down
18 changes: 18 additions & 0 deletions crates/xen/xenvchan-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "krata-xenvchan-sys"
description = "Raw libxenvchan FFI bindings for krata"
license.workspace = true
version.workspace = true
homepage.workspace = true
repository.workspace = true
edition = "2021"
resolver = "2"

[dependencies]
libc = { workspace = true }

[build-dependencies]
pkg-config = "0.3"

[lib]
name = "xenvchan_sys"
23 changes: 23 additions & 0 deletions crates/xen/xenvchan-sys/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
fn main() {
if let Ok(lib_dir) = std::env::var("XENVCHAN_LIB_DIR") {
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:rustc-link-lib=dylib=xenvchan");
return;
}

let mut config = pkg_config::Config::new();
if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("musl") {
config.statik(true);
}

config
.probe("xenvchan")
.or_else(|_| {
let mut fallback = pkg_config::Config::new();
if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("musl") {
fallback.statik(true);
}
fallback.probe("libxenvchan")
})
.expect("failed to locate libxenvchan with pkg-config");
}
46 changes: 46 additions & 0 deletions crates/xen/xenvchan-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#![allow(non_camel_case_types)]

use libc::{c_char, c_int, c_void, size_t};

#[repr(C)]
pub struct libxenvchan {
_private: [u8; 0],
}

#[repr(C)]
pub struct xentoollog_logger {
_private: [u8; 0],
}

pub const XENVCHAN_OPEN_CLOSED: c_int = 0;
pub const XENVCHAN_OPEN_CONNECTED: c_int = 1;
pub const XENVCHAN_OPEN_WAITING_FOR_CLIENT: c_int = 2;

unsafe extern "C" {
pub fn libxenvchan_server_init(
logger: *mut xentoollog_logger,
domain: c_int,
xs_path: *const c_char,
read_min: size_t,
write_min: size_t,
) -> *mut libxenvchan;

pub fn libxenvchan_client_init(
logger: *mut xentoollog_logger,
domain: c_int,
xs_path: *const c_char,
) -> *mut libxenvchan;

pub fn libxenvchan_close(ctrl: *mut libxenvchan);

pub fn libxenvchan_recv(ctrl: *mut libxenvchan, data: *mut c_void, size: size_t) -> c_int;
pub fn libxenvchan_read(ctrl: *mut libxenvchan, data: *mut c_void, size: size_t) -> c_int;
pub fn libxenvchan_send(ctrl: *mut libxenvchan, data: *const c_void, size: size_t) -> c_int;
pub fn libxenvchan_write(ctrl: *mut libxenvchan, data: *const c_void, size: size_t) -> c_int;

pub fn libxenvchan_wait(ctrl: *mut libxenvchan) -> c_int;
pub fn libxenvchan_fd_for_select(ctrl: *mut libxenvchan) -> c_int;
pub fn libxenvchan_is_open(ctrl: *mut libxenvchan) -> c_int;
pub fn libxenvchan_data_ready(ctrl: *mut libxenvchan) -> c_int;
pub fn libxenvchan_buffer_space(ctrl: *mut libxenvchan) -> c_int;
}
17 changes: 17 additions & 0 deletions crates/xen/xenvchan/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "krata-xenvchan"
description = "A safe libxenvchan wrapper for krata"
license.workspace = true
version.workspace = true
homepage.workspace = true
repository.workspace = true
edition = "2021"
resolver = "2"

[dependencies]
thiserror = { workspace = true }
libc = { workspace = true }
krata-xenvchan-sys = { path = "../xenvchan-sys", version = "^0.0.24" }

[lib]
name = "xenvchan"
18 changes: 18 additions & 0 deletions crates/xen/xenvchan/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::ffi::NulError;
use std::io;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("{op} failed: {source}")]
Api {
op: &'static str,
#[source]
source: io::Error,
},
#[error("xenstore path contains an interior NUL byte")]
InvalidPath(#[from] NulError),
#[error("vchan peer is closed")]
Closed,
}

pub type Result<T> = std::result::Result<T, Error>;
Loading