From 35f3346858a4090b041c70b075febc4e94f5f8c2 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Tue, 9 Jan 2024 15:40:17 -0800 Subject: [PATCH] implement support for creating domains --- Cargo.toml | 2 +- xencall/Cargo.toml | 12 +- xencall/examples/domain_create.rs | 11 ++ xencall/examples/domain_info.rs | 10 ++ xencall/examples/simple.rs | 11 -- xencall/src/domctl.rs | 73 +++++++++ xencall/src/lib.rs | 9 +- xencall/src/sys.rs | 246 +++++++++++++++++++++++++++++- xencl/examples/simple.rs | 9 -- xencl/src/lib.rs | 68 --------- {xencl => xenclient}/Cargo.toml | 11 +- xenclient/examples/simple.rs | 16 ++ xenclient/src/create.rs | 88 +++++++++++ xenclient/src/lib.rs | 99 ++++++++++++ xenstore/src/bus.rs | 18 ++- xenstore/src/client.rs | 40 ++++- 16 files changed, 618 insertions(+), 105 deletions(-) create mode 100644 xencall/examples/domain_create.rs create mode 100644 xencall/examples/domain_info.rs delete mode 100644 xencall/examples/simple.rs create mode 100644 xencall/src/domctl.rs delete mode 100644 xencl/examples/simple.rs delete mode 100644 xencl/src/lib.rs rename {xencl => xenclient}/Cargo.toml (54%) create mode 100644 xenclient/examples/simple.rs create mode 100644 xenclient/src/create.rs create mode 100644 xenclient/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index c9dac42..aeed5fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,6 @@ members = [ "xenstore", "xenevtchn", "xencall", - "xencl" + "xenclient" ] resolver = "2" diff --git a/xencall/Cargo.toml b/xencall/Cargo.toml index f0fd829..163d157 100644 --- a/xencall/Cargo.toml +++ b/xencall/Cargo.toml @@ -7,10 +7,18 @@ resolver = "2" [lib] path = "src/lib.rs" +[dependencies.uuid] +version = "1.6.1" +features = ["v4"] + [dependencies.nix] version = "0.27.1" features = ["ioctl"] [[example]] -name = "xencall-simple" -path = "examples/simple.rs" +name = "xencall-domain-info" +path = "examples/domain_info.rs" + +[[example]] +name = "xencall-domain-create" +path = "examples/domain_create.rs" diff --git a/xencall/examples/domain_create.rs b/xencall/examples/domain_create.rs new file mode 100644 index 0000000..b041107 --- /dev/null +++ b/xencall/examples/domain_create.rs @@ -0,0 +1,11 @@ +use xencall::domctl::DomainControl; +use xencall::sys::CreateDomain; +use xencall::{XenCall, XenCallError}; + +fn main() -> Result<(), XenCallError> { + let mut call = XenCall::open()?; + let mut domctl: DomainControl = DomainControl::new(&mut call); + let info = domctl.create_domain(CreateDomain::default())?; + println!("created domain {}", info.domid); + Ok(()) +} diff --git a/xencall/examples/domain_info.rs b/xencall/examples/domain_info.rs new file mode 100644 index 0000000..72a06c0 --- /dev/null +++ b/xencall/examples/domain_info.rs @@ -0,0 +1,10 @@ +use xencall::domctl::DomainControl; +use xencall::{XenCall, XenCallError}; + +fn main() -> Result<(), XenCallError> { + let mut call = XenCall::open()?; + let mut domctl: DomainControl = DomainControl::new(&mut call); + let info = domctl.get_domain_info(1)?; + println!("{:?}", info); + Ok(()) +} diff --git a/xencall/examples/simple.rs b/xencall/examples/simple.rs deleted file mode 100644 index a2bbe9f..0000000 --- a/xencall/examples/simple.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::ffi::c_ulong; -use std::ptr::addr_of; -use xencall::{XenCall, XenCallError}; - -fn main() -> Result<(), XenCallError> { - let mut call = XenCall::open()?; - let message = "Hello World"; - let bytes = message.as_bytes(); - call.hypercall3(18, 0, bytes.len() as c_ulong, addr_of!(bytes) as c_ulong)?; - Ok(()) -} diff --git a/xencall/src/domctl.rs b/xencall/src/domctl.rs new file mode 100644 index 0000000..dafb0e2 --- /dev/null +++ b/xencall/src/domctl.rs @@ -0,0 +1,73 @@ +use crate::sys::{ + ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, GetDomainInfo, HYPERVISOR_DOMCTL, + XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_GETDOMAININFO, XEN_DOMCTL_INTERFACE_VERSION, +}; +use crate::{XenCall, XenCallError}; +use std::ffi::c_ulong; +use std::ptr::addr_of; + +pub struct DomainControl<'a> { + call: &'a mut XenCall, +} + +pub struct CreatedDomain { + pub domid: u32, +} + +impl DomainControl<'_> { + pub fn new(call: &mut XenCall) -> DomainControl { + DomainControl { call } + } + + pub fn get_domain_info(&mut self, domid: u32) -> Result { + let domctl = DomCtl { + cmd: XEN_DOMCTL_GETDOMAININFO, + interface_version: XEN_DOMCTL_INTERFACE_VERSION, + domid, + value: DomCtlValue { + get_domain_info: GetDomainInfo { + domid, + pad1: 0, + flags: 0, + total_pages: 0, + max_pages: 0, + outstanding_pages: 0, + shr_pages: 0, + paged_pages: 0, + shared_info_frame: 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.call + .hypercall1(HYPERVISOR_DOMCTL, addr_of!(domctl) as c_ulong)?; + Ok(unsafe { domctl.value.get_domain_info }) + } + + pub fn create_domain( + &mut self, + create_domain: CreateDomain, + ) -> Result { + let domctl = DomCtl { + cmd: XEN_DOMCTL_CREATEDOMAIN, + interface_version: XEN_DOMCTL_INTERFACE_VERSION, + domid: 0, + value: DomCtlValue { create_domain }, + }; + self.call + .hypercall1(HYPERVISOR_DOMCTL, addr_of!(domctl) as c_ulong)?; + Ok(CreatedDomain { + domid: domctl.domid, + }) + } +} diff --git a/xencall/src/lib.rs b/xencall/src/lib.rs index 93f61d7..13ac01a 100644 --- a/xencall/src/lib.rs +++ b/xencall/src/lib.rs @@ -1,4 +1,5 @@ -mod sys; +pub mod domctl; +pub mod sys; use crate::sys::Hypercall; use nix::errno::Errno; @@ -60,9 +61,9 @@ impl XenCall { pub fn hypercall(&mut self, op: c_ulong, arg: [c_ulong; 5]) -> Result { unsafe { - let mut call = Hypercall { op, arg, retval: 0 }; - sys::hypercall(self.handle.as_raw_fd(), &mut call)?; - Ok(call.retval) + let mut call = Hypercall { op, arg }; + let result = sys::hypercall(self.handle.as_raw_fd(), &mut call)?; + Ok(result as c_long) } } diff --git a/xencall/src/sys.rs b/xencall/src/sys.rs index 5822358..2362a70 100644 --- a/xencall/src/sys.rs +++ b/xencall/src/sys.rs @@ -1,11 +1,249 @@ -use nix::{ioctl_readwrite_bad, request_code_none}; -use std::ffi::{c_long, c_ulong}; +/// Handwritten hypercall bindings. +use nix::ioctl_readwrite_bad; +use std::ffi::c_ulong; +use uuid::Uuid; #[repr(C)] +#[derive(Copy, Clone, Debug)] pub struct Hypercall { pub op: c_ulong, pub arg: [c_ulong; 5], - pub retval: c_long, } -ioctl_readwrite_bad!(hypercall, request_code_none!(b'E', 0), Hypercall); +const IOCTL_PRIVCMD_HYPERCALL: u64 = 0x305000; + +ioctl_readwrite_bad!(hypercall, IOCTL_PRIVCMD_HYPERCALL, Hypercall); + +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 union DomCtlValue { + pub create_domain: CreateDomain, + pub get_domain_info: GetDomainInfo, +} + +#[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, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct GetDomainInfo { + pub domid: u32, + 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 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 ArchDomainConfig { + pub emulation_flags: u32, + pub misc_flags: u32, +} + +pub const XEN_DOMCTL_INTERFACE_VERSION: u32 = 0x00000015; +pub const SECINITSID_DOMU: u32 = 13; + +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, + }, + } + } +} diff --git a/xencl/examples/simple.rs b/xencl/examples/simple.rs deleted file mode 100644 index ba18160..0000000 --- a/xencl/examples/simple.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::collections::HashMap; -use xencl::{XenClient, XenClientError}; - -fn main() -> Result<(), XenClientError> { - let mut client = XenClient::open()?; - let entries: HashMap = HashMap::new(); - client.create(2, entries)?; - Ok(()) -} diff --git a/xencl/src/lib.rs b/xencl/src/lib.rs deleted file mode 100644 index 838e3d9..0000000 --- a/xencl/src/lib.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::collections::HashMap; -use std::error::Error; -use std::fmt::{Display, Formatter}; -use xenstore::bus::XsdBusError; -use xenstore::client::{XsdClient, XsdInterface}; - -pub struct XenClient { - store: XsdClient, -} - -#[derive(Debug)] -pub struct XenClientError { - message: String, -} - -impl XenClientError { - pub fn new(msg: &str) -> XenClientError { - XenClientError { - message: msg.to_string(), - } - } -} - -impl Display for XenClientError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.message) - } -} - -impl Error for XenClientError { - fn description(&self) -> &str { - &self.message - } -} - -impl From for XenClientError { - fn from(value: std::io::Error) -> Self { - XenClientError::new(value.to_string().as_str()) - } -} - -impl From for XenClientError { - fn from(value: XsdBusError) -> Self { - XenClientError::new(value.to_string().as_str()) - } -} - -impl XenClient { - pub fn open() -> Result { - let store = XsdClient::open()?; - Ok(XenClient { store }) - } - - pub fn create( - &mut self, - domid: u32, - entries: HashMap, - ) -> Result<(), XenClientError> { - let domain = self.store.get_domain_path(domid)?; - let mut tx = self.store.transaction()?; - for (key, value) in entries { - let path = format!("{}/{}", domain, key); - tx.write(path.as_str(), value.into_bytes())?; - } - tx.commit()?; - Ok(()) - } -} diff --git a/xencl/Cargo.toml b/xenclient/Cargo.toml similarity index 54% rename from xencl/Cargo.toml rename to xenclient/Cargo.toml index c058cf2..bc82c36 100644 --- a/xencl/Cargo.toml +++ b/xenclient/Cargo.toml @@ -1,15 +1,22 @@ [package] -name = "xencl" +name = "xenclient" version = "0.0.1" edition = "2021" resolver = "2" +[dependencies.xencall] +path = "../xencall" + [dependencies.xenstore] path = "../xenstore" +[dependencies.uuid] +version = "1.6.1" +features = ["v4"] + [lib] path = "src/lib.rs" [[example]] -name = "xencl-simple" +name = "xenclient-simple" path = "examples/simple.rs" diff --git a/xenclient/examples/simple.rs b/xenclient/examples/simple.rs new file mode 100644 index 0000000..145b5dc --- /dev/null +++ b/xenclient/examples/simple.rs @@ -0,0 +1,16 @@ +use xenclient::create::{DomainConfig, PvDomainConfig}; +use xenclient::{XenClient, XenClientError}; + +fn main() -> Result<(), XenClientError> { + let mut client = XenClient::open()?; + let mut config = DomainConfig::new(); + config.configure_cpus(1); + config.configure_memory(524288, 524288, 0); + config.configure_pv(PvDomainConfig::new( + "/boot/vmlinuz-6.1.0-17-amd64".to_string(), + None, + None, + )); + client.create(2, config)?; + Ok(()) +} diff --git a/xenclient/src/create.rs b/xenclient/src/create.rs new file mode 100644 index 0000000..50347ce --- /dev/null +++ b/xenclient/src/create.rs @@ -0,0 +1,88 @@ +use std::collections::HashMap; + +pub struct DomainConfig { + vm_entries: HashMap, + domain_entries: HashMap, +} + +pub struct PvDomainConfig { + kernel: String, + ramdisk: Option, + cmdline: Option, +} + +impl DomainConfig { + pub fn new() -> DomainConfig { + DomainConfig { + vm_entries: HashMap::new(), + domain_entries: HashMap::new(), + } + } + + pub fn put_vm(&mut self, key: &str, value: String) { + self.vm_entries.insert(key.to_string(), value); + } + + pub fn put_vm_str(&mut self, key: &str, value: &str) { + self.put_vm(key, value.to_string()); + } + + pub fn put_domain(&mut self, key: &str, value: String) { + self.vm_entries.insert(key.to_string(), value); + } + + pub fn put_domain_str(&mut self, key: &str, value: &str) { + self.put_domain(key, value.to_string()); + } + + pub fn configure_memory(&mut self, maxkb: u32, targetkb: u32, videokb: u32) { + self.put_domain("memory/static-max", maxkb.to_string()); + self.put_domain("memory/target", targetkb.to_string()); + self.put_domain("memory/videoram", videokb.to_string()); + } + + pub fn configure_cpus(&mut self, maxvcpus: u32) { + for i in 0..maxvcpus { + println!("{}", i); + } + } + + pub fn configure_pv(&mut self, pv: PvDomainConfig) { + self.put_vm_str("image/ostype", "linux"); + self.put_vm("image/kernel", pv.kernel); + + match pv.ramdisk { + None => {} + Some(ramdisk) => self.put_vm("image/ramdisk", ramdisk), + } + + match pv.cmdline { + None => {} + Some(cmdline) => self.put_vm("image/cmdline", cmdline), + } + } + + pub fn clone_vm_entries(&self) -> HashMap { + self.vm_entries.clone() + } + + pub fn clone_domain_entries(&self) -> HashMap { + self.domain_entries.clone() + } +} + +impl Default for DomainConfig { + fn default() -> Self { + DomainConfig::new() + } +} + +impl PvDomainConfig { + pub fn new(kernel: String, ramdisk: Option, cmdline: Option) -> PvDomainConfig { + PvDomainConfig { + kernel, + ramdisk, + cmdline, + } + } +} diff --git a/xenclient/src/lib.rs b/xenclient/src/lib.rs new file mode 100644 index 0000000..ba02d76 --- /dev/null +++ b/xenclient/src/lib.rs @@ -0,0 +1,99 @@ +pub mod create; + +use crate::create::DomainConfig; +use std::error::Error; +use std::fmt::{Display, Formatter}; +use std::string::FromUtf8Error; +use xencall::domctl::DomainControl; +use xencall::sys::CreateDomain; +use xencall::{XenCall, XenCallError}; +use xenstore::bus::XsdBusError; +use xenstore::client::{XsdClient, XsdInterface}; + +pub struct XenClient { + store: XsdClient, + call: XenCall, +} + +#[derive(Debug)] +pub struct XenClientError { + message: String, +} + +impl XenClientError { + pub fn new(msg: &str) -> XenClientError { + XenClientError { + message: msg.to_string(), + } + } +} + +impl Display for XenClientError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for XenClientError { + fn description(&self) -> &str { + &self.message + } +} + +impl From for XenClientError { + fn from(value: std::io::Error) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + +impl From for XenClientError { + fn from(value: XsdBusError) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + +impl From for XenClientError { + fn from(value: XenCallError) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + +impl From for XenClientError { + fn from(value: FromUtf8Error) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + +impl XenClient { + pub fn open() -> Result { + let store = XsdClient::open()?; + let call = XenCall::open()?; + Ok(XenClient { store, call }) + } + + pub fn create(&mut self, config: DomainConfig) -> Result<(), XenClientError> { + let mut domctl = DomainControl::new(&mut self.call); + let created = domctl.create_domain(CreateDomain::default())?; + let domain = self.store.get_domain_path(created.domid)?; + let vm = self.store.read_string(format!("{}/vm", domain).as_str())?; + + let mut tx = self.store.transaction()?; + + for (key, value) in config.clone_domain_entries() { + let path = format!("{}/{}", domain, key); + tx.write(path.as_str(), value.into_bytes())?; + } + + let domid_path = format!("{}/domid", domain); + tx.write(domid_path.as_str(), created.domid.to_string().into_bytes())?; + + for (key, value) in config.clone_domain_entries() { + let path = format!("{}/{}", vm, key); + tx.write(path.as_str(), value.into_bytes())?; + } + + tx.commit()?; + + Ok(()) + } +} diff --git a/xenstore/src/bus.rs b/xenstore/src/bus.rs index eb5696e..1191e27 100644 --- a/xenstore/src/bus.rs +++ b/xenstore/src/bus.rs @@ -160,10 +160,24 @@ impl XsdSocket { typ: u32, string: &str, ) -> Result { - let path = CString::new(string)?; - let buf = path.as_bytes_with_nul(); + 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 { + let mut buf: Vec = 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 { diff --git a/xenstore/src/client.rs b/xenstore/src/client.rs index 6a4dd28..750341f 100644 --- a/xenstore/src/client.rs +++ b/xenstore/src/client.rs @@ -1,7 +1,7 @@ use crate::bus::{XsdBusError, XsdSocket}; use crate::sys::{ - XSD_DIRECTORY, XSD_GET_DOMAIN_PATH, XSD_MKDIR, XSD_READ, XSD_RM, XSD_TRANSACTION_END, - XSD_TRANSACTION_START, XSD_WRITE, + XSD_DIRECTORY, XSD_GET_DOMAIN_PATH, XSD_INTRODUCE, XSD_MKDIR, XSD_READ, XSD_RM, + XSD_TRANSACTION_END, XSD_TRANSACTION_START, XSD_WRITE, }; use std::ffi::CString; @@ -12,7 +12,9 @@ pub struct XsdClient { pub trait XsdInterface { fn list(&mut self, path: &str) -> Result, XsdBusError>; fn read(&mut self, path: &str) -> Result, XsdBusError>; + fn read_string(&mut self, path: &str) -> Result; fn write(&mut self, path: &str, data: Vec) -> Result; + fn write_string(&mut self, path: &str, data: String) -> Result; fn mkdir(&mut self, path: &str) -> Result; fn rm(&mut self, path: &str) -> Result; } @@ -62,6 +64,24 @@ impl XsdClient { .send_single(0, XSD_GET_DOMAIN_PATH, domid.to_string().as_str())?; response.parse_string() } + + pub fn introduce_domain( + &mut self, + domid: u32, + mfn: u32, + eventchn: u32, + ) -> Result { + let response = self.socket.send_multiple( + 0, + XSD_INTRODUCE, + &[ + domid.to_string().as_str(), + mfn.to_string().as_str(), + eventchn.to_string().as_str(), + ], + )?; + response.parse_string() + } } pub struct XsdTransaction<'a> { @@ -78,10 +98,18 @@ impl XsdInterface for XsdClient { self.read(0, path) } + fn read_string(&mut self, path: &str) -> Result { + Ok(String::from_utf8(self.read(0, path)?)?) + } + fn write(&mut self, path: &str, data: Vec) -> Result { self.write(0, path, data) } + fn write_string(&mut self, path: &str, data: String) -> Result { + self.write(0, path, data.into_bytes()) + } + fn mkdir(&mut self, path: &str) -> Result { self.mkdir(0, path) } @@ -100,10 +128,18 @@ impl XsdInterface for XsdTransaction<'_> { self.client.read(self.tx, path) } + fn read_string(&mut self, path: &str) -> Result { + Ok(String::from_utf8(self.client.read(self.tx, path)?)?) + } + fn write(&mut self, path: &str, data: Vec) -> Result { self.client.write(self.tx, path, data) } + fn write_string(&mut self, path: &str, data: String) -> Result { + self.client.write(self.tx, path, data.into_bytes()) + } + fn mkdir(&mut self, path: &str) -> Result { self.client.mkdir(self.tx, path) }