oh my god, we have a console

This commit is contained in:
Alex Zenla 2024-01-17 05:22:47 -08:00
parent 6ca61410ad
commit 15ba27e573
No known key found for this signature in database
GPG Key ID: 067B238899B51269
14 changed files with 643 additions and 559 deletions

View File

@ -1,4 +1,3 @@
use xencall::domctl::DomainControl;
use xencall::sys::CreateDomain; use xencall::sys::CreateDomain;
use xencall::{XenCall, XenCallError}; use xencall::{XenCall, XenCallError};
@ -6,8 +5,7 @@ fn main() -> Result<(), XenCallError> {
env_logger::init(); env_logger::init();
let call = XenCall::open()?; let call = XenCall::open()?;
let domctl: DomainControl = DomainControl::new(&call); let domid = call.create_domain(CreateDomain::default())?;
let domid = domctl.create_domain(CreateDomain::default())?;
println!("created domain {}", domid); println!("created domain {}", domid);
Ok(()) Ok(())
} }

View File

@ -1,12 +1,10 @@
use xencall::domctl::DomainControl;
use xencall::{XenCall, XenCallError}; use xencall::{XenCall, XenCallError};
fn main() -> Result<(), XenCallError> { fn main() -> Result<(), XenCallError> {
env_logger::init(); env_logger::init();
let call = XenCall::open()?; let call = XenCall::open()?;
let domctl: DomainControl = DomainControl::new(&call); let info = call.get_domain_info(1)?;
let info = domctl.get_domain_info(1)?;
println!("{:?}", info); println!("{:?}", info);
Ok(()) Ok(())
} }

View File

@ -1,12 +1,10 @@
use xencall::domctl::DomainControl;
use xencall::{XenCall, XenCallError}; use xencall::{XenCall, XenCallError};
fn main() -> Result<(), XenCallError> { fn main() -> Result<(), XenCallError> {
env_logger::init(); env_logger::init();
let call = XenCall::open()?; let call = XenCall::open()?;
let domctl: DomainControl = DomainControl::new(&call); let context = call.get_vcpu_context(224, 0)?;
let context = domctl.get_vcpu_context(224, 0)?;
println!("{:?}", context); println!("{:?}", context);
Ok(()) Ok(())
} }

View File

@ -1,301 +0,0 @@
use crate::sys::{
AddressSize, ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, DomCtlVcpuContext,
GetDomainInfo, GetPageFrameInfo3, HypercallInit, MaxMem, MaxVcpus, VcpuGuestContext,
VcpuGuestContextAny, HYPERVISOR_DOMCTL, 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,
};
use crate::{XenCall, XenCallError};
use log::trace;
use std::ffi::c_ulong;
use std::os::fd::AsRawFd;
use std::ptr::addr_of_mut;
use std::slice;
pub struct DomainControl<'a> {
pub call: &'a XenCall,
}
impl DomainControl<'_> {
pub fn new(call: &XenCall) -> DomainControl {
DomainControl { call }
}
pub fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo, XenCallError> {
trace!(
"domctl fd={} get_domain_info domid={}",
self.call.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.call
.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, XenCallError> {
trace!(
"domctl fd={} create_domain create_domain={:?}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(domctl.domid)
}
pub fn pause_domain(&self, domid: u32) -> Result<(), XenCallError> {
trace!(
"domctl fd={} pause_domain domid={:?}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
pub fn unpause_domain(&self, domid: u32) -> Result<(), XenCallError> {
trace!(
"domctl fd={} unpause_domain domid={:?}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
pub fn set_max_mem(&self, domid: u32, memkb: u64) -> Result<(), XenCallError> {
trace!(
"domctl fd={} set_max_mem domid={} memkb={}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
pub fn set_max_vcpus(&self, domid: u32, max_vcpus: u32) -> Result<(), XenCallError> {
trace!(
"domctl fd={} set_max_vcpus domid={} max_vcpus={}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
pub fn set_address_size(&self, domid: u32, size: u32) -> Result<(), XenCallError> {
trace!(
"domctl fd={} set_address_size domid={} size={}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
pub fn get_vcpu_context(
&self,
domid: u32,
vcpu: u32,
) -> Result<VcpuGuestContext, XenCallError> {
trace!(
"domctl fd={} get_vcpu_context domid={}",
self.call.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.call
.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<(), XenCallError> {
trace!(
"domctl fd={} set_vcpu_context domid={} context={:?}",
self.call.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.call
.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>, XenCallError> {
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.call
.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<(), XenCallError> {
trace!(
"domctl fd={} hypercall_init domid={} gmfn={}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
pub fn destroy_domain(&self, domid: u32) -> Result<(), XenCallError> {
trace!(
"domctl fd={} destroy_domain domid={}",
self.call.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.call
.hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?;
Ok(())
}
}

View File

@ -1,20 +1,28 @@
pub mod domctl;
pub mod memory;
pub mod sys; pub mod sys;
use crate::sys::{ use crate::sys::{
EvtChnAllocUnbound, Hypercall, MmapBatch, MmapResource, MultiCallEntry, XenCapabilitiesInfo, AddressSize, ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, DomCtlVcpuContext,
HYPERVISOR_EVENT_CHANNEL_OP, HYPERVISOR_MULTICALL, HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, 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_MEMORY_MAP,
XEN_MEM_POPULATE_PHYSMAP,
}; };
use libc::{c_int, mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE}; use libc::{c_int, mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
use log::trace; use log::trace;
use nix::errno::Errno; use nix::errno::Errno;
use std::error::Error; use std::error::Error;
use std::ffi::{c_long, c_ulong, c_void}; use std::ffi::{c_long, c_uint, c_ulong, c_void};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
use std::ptr::addr_of_mut; use std::ptr::addr_of_mut;
use std::slice;
pub struct XenCall { pub struct XenCall {
pub handle: File, pub handle: File,
@ -251,4 +259,353 @@ impl XenCall {
self.evtchn_op(6, addr_of_mut!(alloc_unbound) as c_ulong)?; self.evtchn_op(6, addr_of_mut!(alloc_unbound) as c_ulong)?;
Ok(alloc_unbound.port) Ok(alloc_unbound.port)
} }
pub fn get_domain_info(&self, domid: u32) -> Result<GetDomainInfo, XenCallError> {
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, XenCallError> {
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<(), XenCallError> {
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<(), XenCallError> {
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<(), XenCallError> {
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<(), XenCallError> {
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<(), XenCallError> {
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, XenCallError> {
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<(), XenCallError> {
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>, XenCallError> {
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<(), XenCallError> {
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<(), XenCallError> {
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>, XenCallError> {
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>, XenCallError> {
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(XenCallError::new(
format!("failed to populate physmap: {:#x}", code).as_str(),
));
}
if code as usize > extent_starts.len() {
return Err(XenCallError::new("failed to populate physmap"));
}
let extents = extent_starts[0..code as usize].to_vec();
Ok(extents)
}
pub fn mmuext(
&self,
domid: u32,
cmd: c_uint,
arg1: u64,
arg2: u64,
) -> Result<(), XenCallError> {
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(|_| ())
}
} }

View File

@ -1,106 +0,0 @@
use crate::sys::{
MemoryMap, MemoryReservation, MmuExtOp, MultiCallEntry, HYPERVISOR_MEMORY_OP,
HYPERVISOR_MMUEXT_OP, XEN_MEM_MEMORY_MAP, XEN_MEM_POPULATE_PHYSMAP,
};
use crate::{XenCall, XenCallError};
use log::trace;
use std::ffi::{c_uint, c_ulong};
use std::os::fd::AsRawFd;
use std::ptr::addr_of_mut;
pub struct MemoryControl<'a> {
call: &'a XenCall,
}
impl MemoryControl<'_> {
pub fn new(call: &XenCall) -> MemoryControl {
MemoryControl { call }
}
pub fn get_memory_map(&self, size_of_entry: usize) -> Result<Vec<u8>, XenCallError> {
let mut memory_map = MemoryMap {
count: 0,
buffer: 0,
};
self.call.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.call.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>, XenCallError> {
trace!("memory fd={} populate_physmap domid={} nr_extents={} extent_order={} mem_flags={} extent_starts={:?}", self.call.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.call.multicall(calls)?;
let code = calls[0].result;
if code > !0xfff {
return Err(XenCallError::new(
format!("failed to populate physmap: {:#x}", code).as_str(),
));
}
if code as usize > extent_starts.len() {
return Err(XenCallError::new("failed to populate physmap"));
}
let extents = extent_starts[0..code as usize].to_vec();
Ok(extents)
}
pub fn mmuext(
&self,
domid: u32,
cmd: c_uint,
arg1: u64,
arg2: u64,
) -> Result<(), XenCallError> {
let mut ops = MmuExtOp { cmd, arg1, arg2 };
self.call
.hypercall4(
HYPERVISOR_MMUEXT_OP,
addr_of_mut!(ops) as c_ulong,
1,
0,
domid as c_ulong,
)
.map(|_| ())
}
}

View File

@ -32,10 +32,6 @@ env_logger = "0.10.1"
[lib] [lib]
path = "src/lib.rs" path = "src/lib.rs"
[[example]]
name = "xenclient-simple"
path = "examples/simple.rs"
[[example]] [[example]]
name = "xenclient-boot" name = "xenclient-boot"
path = "examples/boot.rs" path = "examples/boot.rs"

View File

@ -1,13 +1,6 @@
use std::fs::read;
use std::{env, process}; use std::{env, process};
use xencall::domctl::DomainControl; use xenclient::create::DomainConfig;
use xencall::memory::MemoryControl; use xenclient::{XenClient, XenClientError};
use xencall::sys::CreateDomain;
use xencall::XenCall;
use xenclient::boot::BootSetup;
use xenclient::elfloader::ElfImageLoader;
use xenclient::XenClientError;
use xenevtchn::EventChannel;
fn main() -> Result<(), XenClientError> { fn main() -> Result<(), XenClientError> {
env_logger::init(); env_logger::init();
@ -19,40 +12,15 @@ fn main() -> Result<(), XenClientError> {
} }
let kernel_image_path = args.get(1).expect("argument not specified"); let kernel_image_path = args.get(1).expect("argument not specified");
let initrd_path = args.get(2).expect("argument not specified"); let initrd_path = args.get(2).expect("argument not specified");
let call = XenCall::open()?; let mut client = XenClient::open()?;
let domctl = DomainControl::new(&call); let config = DomainConfig {
let domain = CreateDomain {
max_vcpus: 1, max_vcpus: 1,
..Default::default() mem_mb: 512,
kernel_path: kernel_image_path.to_string(),
initrd_path: initrd_path.to_string(),
cmdline: "debug elevator=noop".to_string(),
}; };
let domid = domctl.create_domain(domain)?; let domid = client.create(config)?;
boot( println!("created domain {}", domid);
domid,
kernel_image_path.as_str(),
initrd_path.as_str(),
&call,
&domctl,
)?;
Ok(())
}
fn boot(
domid: u32,
kernel_image_path: &str,
initrd_path: &str,
call: &XenCall,
domctl: &DomainControl,
) -> Result<(), XenClientError> {
println!("domain created: {:?}", domid);
let image_loader = ElfImageLoader::load_file_kernel(kernel_image_path)?;
let memctl = MemoryControl::new(call);
let mut boot = BootSetup::new(call, domctl, &memctl, domid);
let initrd = read(initrd_path)?;
let mut state = boot.initialize(&image_loader, initrd.as_slice(), 1, 512)?;
boot.boot(&mut state, "debug elevator=noop")?;
domctl.unpause_domain(domid)?;
let _evtchn = EventChannel::open()?;
Ok(()) Ok(())
} }

View File

@ -1,18 +0,0 @@
use xenclient::create::{DomainConfig, PvDomainConfig};
use xenclient::{XenClient, XenClientError};
fn main() -> Result<(), XenClientError> {
env_logger::init();
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(config)?;
Ok(())
}

View File

@ -16,8 +16,6 @@ use std::cmp::{max, min};
use std::ffi::c_void; use std::ffi::c_void;
use std::mem::size_of; use std::mem::size_of;
use std::slice; use std::slice;
use xencall::domctl::DomainControl;
use xencall::memory::MemoryControl;
use xencall::sys::{VcpuGuestContext, MMUEXT_PIN_L4_TABLE}; use xencall::sys::{VcpuGuestContext, MMUEXT_PIN_L4_TABLE};
use xencall::XenCall; use xencall::XenCall;
@ -41,9 +39,8 @@ pub struct BootImageInfo {
} }
pub struct BootSetup<'a> { pub struct BootSetup<'a> {
domctl: &'a DomainControl<'a>, call: &'a XenCall,
memctl: &'a MemoryControl<'a>, pub phys: PhysicalPages<'a>,
phys: PhysicalPages<'a>,
domid: u32, domid: u32,
virt_alloc_end: u64, virt_alloc_end: u64,
pfn_alloc_end: u64, pfn_alloc_end: u64,
@ -55,7 +52,7 @@ pub struct BootSetup<'a> {
pub struct DomainSegment { pub struct DomainSegment {
vstart: u64, vstart: u64,
_vend: u64, _vend: u64,
pfn: u64, pub pfn: u64,
addr: u64, addr: u64,
size: u64, size: u64,
pages: u64, pages: u64,
@ -87,15 +84,9 @@ pub struct BootState {
} }
impl BootSetup<'_> { impl BootSetup<'_> {
pub fn new<'a>( pub fn new(call: &XenCall, domid: u32) -> BootSetup {
call: &'a XenCall,
domctl: &'a DomainControl<'a>,
memctl: &'a MemoryControl<'a>,
domid: u32,
) -> BootSetup<'a> {
BootSetup { BootSetup {
domctl, call,
memctl,
phys: PhysicalPages::new(call, domid), phys: PhysicalPages::new(call, domid),
domid, domid,
virt_alloc_end: 0, virt_alloc_end: 0,
@ -106,7 +97,7 @@ impl BootSetup<'_> {
} }
fn initialize_memory(&mut self, total_pages: u64) -> Result<(), XenClientError> { fn initialize_memory(&mut self, total_pages: u64) -> Result<(), XenClientError> {
self.domctl.set_address_size(self.domid, 64)?; self.call.set_address_size(self.domid, 64)?;
let mut vmemranges: Vec<VmemRange> = Vec::new(); let mut vmemranges: Vec<VmemRange> = Vec::new();
let stub = VmemRange { let stub = VmemRange {
@ -162,7 +153,7 @@ impl BootSetup<'_> {
} }
let extents_init_slice = extents_init.as_slice(); let extents_init_slice = extents_init.as_slice();
let extents = self.memctl.populate_physmap( let extents = self.call.populate_physmap(
self.domid, self.domid,
count, count,
SUPERPAGE_2MB_SHIFT as u32, SUPERPAGE_2MB_SHIFT as u32,
@ -191,7 +182,7 @@ impl BootSetup<'_> {
let p2m_end_idx = p2m_idx + allocsz as usize; let p2m_end_idx = p2m_idx + allocsz as usize;
let input_extent_starts = &p2m[p2m_idx..p2m_end_idx]; let input_extent_starts = &p2m[p2m_idx..p2m_end_idx];
let result = let result =
self.memctl self.call
.populate_physmap(self.domid, allocsz, 0, 0, input_extent_starts)?; .populate_physmap(self.domid, allocsz, 0, 0, input_extent_starts)?;
if result.len() != allocsz as usize { if result.len() != allocsz as usize {
@ -226,7 +217,7 @@ impl BootSetup<'_> {
let pfn = (image_info.virt_hypercall - image_info.virt_base) >> X86_PAGE_SHIFT; let pfn = (image_info.virt_hypercall - image_info.virt_base) >> X86_PAGE_SHIFT;
let mfn = self.phys.p2m[pfn as usize]; let mfn = self.phys.p2m[pfn as usize];
self.domctl.hypercall_init(self.domid, mfn)?; self.call.hypercall_init(self.domid, mfn)?;
Ok(()) Ok(())
} }
@ -241,8 +232,6 @@ impl BootSetup<'_> {
"BootSetup initialize max_vcpus={:?} mem_mb={:?}", "BootSetup initialize max_vcpus={:?} mem_mb={:?}",
max_vcpus, mem_mb max_vcpus, mem_mb
); );
self.domctl.set_max_vcpus(self.domid, max_vcpus)?;
self.domctl.set_max_mem(self.domid, mem_mb * 1024)?;
let total_pages = mem_mb << (20 - X86_PAGE_SHIFT); let total_pages = mem_mb << (20 - X86_PAGE_SHIFT);
self.initialize_memory(total_pages)?; self.initialize_memory(total_pages)?;
@ -284,8 +273,8 @@ impl BootSetup<'_> {
} }
let initrd_segment = initrd_segment.unwrap(); let initrd_segment = initrd_segment.unwrap();
let store_evtchn = self.domctl.call.evtchn_alloc_unbound(self.domid, 0)?; let store_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?;
let console_evtchn = self.domctl.call.evtchn_alloc_unbound(self.domid, 0)?; let console_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0)?;
let state = BootState { let state = BootState {
kernel_segment, kernel_segment,
start_info_segment, start_info_segment,
@ -306,7 +295,7 @@ impl BootSetup<'_> {
} }
pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> { pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> {
let domain_info = self.domctl.get_domain_info(self.domid)?; let domain_info = self.call.get_domain_info(self.domid)?;
let shared_info_frame = domain_info.shared_info_frame; let shared_info_frame = domain_info.shared_info_frame;
state.shared_info_frame = shared_info_frame; state.shared_info_frame = shared_info_frame;
self.setup_page_tables(state)?; self.setup_page_tables(state)?;
@ -317,7 +306,7 @@ impl BootSetup<'_> {
self.phys.unmap(pg_pfn)?; self.phys.unmap(pg_pfn)?;
self.phys.unmap(state.p2m_segment.pfn)?; self.phys.unmap(state.p2m_segment.pfn)?;
let pg_mfn = self.phys.p2m[pg_pfn as usize]; let pg_mfn = self.phys.p2m[pg_pfn as usize];
self.memctl self.call
.mmuext(self.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?; .mmuext(self.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?;
self.setup_shared_info(state.shared_info_frame)?; self.setup_shared_info(state.shared_info_frame)?;
@ -346,7 +335,7 @@ impl BootSetup<'_> {
vcpu.kernel_ss = vcpu.user_regs.ss as u64; vcpu.kernel_ss = vcpu.user_regs.ss as u64;
vcpu.kernel_sp = vcpu.user_regs.rsp; vcpu.kernel_sp = vcpu.user_regs.rsp;
debug!("vcpu context: {:?}", vcpu); debug!("vcpu context: {:?}", vcpu);
self.domctl.set_vcpu_context(self.domid, 0, &vcpu)?; self.call.set_vcpu_context(self.domid, 0, &vcpu)?;
self.phys.unmap_all()?; self.phys.unmap_all()?;
self.gnttab_seed(state)?; self.gnttab_seed(state)?;
Ok(()) Ok(())
@ -356,13 +345,10 @@ impl BootSetup<'_> {
let console_gfn = self.phys.p2m[state.console_segment.pfn as usize]; 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 xenstore_gfn = self.phys.p2m[state.xenstore_segment.pfn as usize];
let addr = self let addr = self
.domctl
.call .call
.mmap(0, 1 << XEN_PAGE_SHIFT) .mmap(0, 1 << XEN_PAGE_SHIFT)
.ok_or(XenClientError::new("failed to mmap for resource"))?; .ok_or(XenClientError::new("failed to mmap for resource"))?;
self.domctl self.call.map_resource(self.domid, 1, 0, 0, 1, addr)?;
.call
.map_resource(self.domid, 1, 0, 0, 1, addr)?;
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) }; let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
entries[0].flags = 1 << 0; entries[0].flags = 1 << 0;
entries[0].domid = 0; entries[0].domid = 0;

View File

@ -1,19 +1,27 @@
use std::collections::HashMap; use std::collections::HashMap;
pub struct DomainConfig { pub struct DomainConfig {
vm_entries: HashMap<String, String>, pub max_vcpus: u32,
domain_entries: HashMap<String, String>, pub mem_mb: u64,
pub kernel_path: String,
pub initrd_path: String,
pub cmdline: String,
} }
pub struct PvDomainConfig { pub struct PvDomainStore {
kernel: String, kernel: String,
ramdisk: Option<String>, ramdisk: Option<String>,
cmdline: Option<String>, cmdline: Option<String>,
} }
impl DomainConfig { pub struct DomainStore {
pub fn new() -> DomainConfig { vm_entries: HashMap<String, String>,
DomainConfig { domain_entries: HashMap<String, String>,
}
impl DomainStore {
pub fn new() -> DomainStore {
DomainStore {
vm_entries: HashMap::new(), vm_entries: HashMap::new(),
domain_entries: HashMap::new(), domain_entries: HashMap::new(),
} }
@ -43,7 +51,7 @@ impl DomainConfig {
pub fn configure_cpus(&mut self, _maxvcpus: u32) {} pub fn configure_cpus(&mut self, _maxvcpus: u32) {}
pub fn configure_pv(&mut self, pv: PvDomainConfig) { pub fn configure_pv(&mut self, pv: PvDomainStore) {
self.put_vm_str("image/ostype", "linux"); self.put_vm_str("image/ostype", "linux");
self.put_vm("image/kernel", pv.kernel); self.put_vm("image/kernel", pv.kernel);
@ -67,15 +75,15 @@ impl DomainConfig {
} }
} }
impl Default for DomainConfig { impl Default for DomainStore {
fn default() -> Self { fn default() -> Self {
DomainConfig::new() DomainStore::new()
} }
} }
impl PvDomainConfig { impl PvDomainStore {
pub fn new(kernel: String, ramdisk: Option<String>, cmdline: Option<String>) -> PvDomainConfig { pub fn new(kernel: String, ramdisk: Option<String>, cmdline: Option<String>) -> PvDomainStore {
PvDomainConfig { PvDomainStore {
kernel, kernel,
ramdisk, ramdisk,
cmdline, cmdline,

View File

@ -5,16 +5,19 @@ pub mod mem;
pub mod sys; pub mod sys;
mod x86; mod x86;
use crate::boot::BootSetup;
use crate::create::DomainConfig; use crate::create::DomainConfig;
use crate::elfloader::ElfImageLoader;
use std::error::Error; use std::error::Error;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fs::read;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use xencall::domctl::DomainControl; use uuid::Uuid;
use xencall::sys::CreateDomain; use xencall::sys::CreateDomain;
use xencall::{XenCall, XenCallError}; use xencall::{XenCall, XenCallError};
use xenevtchn::EventChannelError; use xenevtchn::EventChannelError;
use xenstore::bus::XsdBusError; use xenstore::bus::XsdBusError;
use xenstore::client::{XsdClient, XsdInterface}; use xenstore::client::{XsPermissions, XsdClient, XsdInterface};
pub struct XenClient { pub struct XenClient {
store: XsdClient, store: XsdClient,
@ -83,29 +86,208 @@ impl XenClient {
Ok(XenClient { store, call }) Ok(XenClient { store, call })
} }
pub fn create(&mut self, config: DomainConfig) -> Result<(), XenClientError> { pub fn create(&mut self, config: DomainConfig) -> Result<u32, XenClientError> {
let domctl = DomainControl::new(&self.call); let domain = CreateDomain {
let domid = domctl.create_domain(CreateDomain::default())?; max_vcpus: config.max_vcpus,
let domain = self.store.get_domain_path(domid)?; ..Default::default()
let vm = self.store.read_string(format!("{}/vm", domain).as_str())?; };
let domid = self.call.create_domain(domain)?;
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 libxl_path = format!("/libxl/{}", domid);
let ro_perm = XsPermissions { id: 0, perms: 0 };
let rw_perm = XsPermissions { id: 0, perms: 0 };
let no_perm = XsPermissions { id: 0, perms: 0 };
{
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.rm(libxl_path.as_str())?;
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", vm_path).as_str(),
"mycelium",
)?;
tx.write_string(format!("{}/type", libxl_path).as_str(), "pv")?;
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.as_str())?;
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 initrd = read(config.initrd_path.as_str())?;
let mut state = boot.initialize(
&image_loader,
initrd.as_slice(),
config.max_vcpus,
config.mem_mb,
)?;
boot.boot(&mut state, config.cmdline.as_str())?;
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 {
tx.write_string(
format!("{}/cpu/{}/availability", dom_path, i).as_str(),
"online",
)?;
}
tx.commit()?;
}
self.console_device_add(&dom_path.to_string(), domid, console_evtchn, console_mfn)?;
self.store
.introduce_domain(domid, xenstore_mfn, xenstore_evtchn)?;
self.call.unpause_domain(domid)?;
Ok(domid)
}
fn console_device_add(
&mut self,
dom_path: &String,
domid: u32,
port: u32,
mfn: u64,
) -> Result<(), XenClientError> {
let frontend_path = format!("{}/console", dom_path);
let backend_path = format!("{}/backend/console/{}/{}", dom_path, domid, 0);
let mut tx = self.store.transaction()?; let mut tx = self.store.transaction()?;
tx.write_string(
format!("{}/frontend-id", backend_path).as_str(),
&domid.to_string(),
)?;
tx.write_string(
format!("{}/online", backend_path).as_str(),
"1",
)?;
tx.write_string(format!("{}/state", backend_path).as_str(), "1")?;
tx.write_string(
format!("{}/protocol", backend_path).as_str(),
"vt100",
)?;
for (key, value) in config.clone_domain_entries() { tx.write_string(
let path = format!("{}/{}", domain, key); format!("{}/backend-id", frontend_path).as_str(),
tx.write(path.as_str(), value.into_bytes())?; "0",
} )?;
tx.write_string(
let domid_path = format!("{}/domid", domain); format!("{}/limit", frontend_path).as_str(),
tx.write(domid_path.as_str(), domid.to_string().into_bytes())?; "1048576",
)?;
for (key, value) in config.clone_domain_entries() { tx.write_string(
let path = format!("{}/{}", vm, key); format!("{}/type", frontend_path).as_str(),
tx.write(path.as_str(), value.into_bytes())?; "xenconsoled",
} )?;
tx.write_string(
format!("{}/output", frontend_path).as_str(),
"pty",
)?;
tx.write_string(format!("{}/tty", frontend_path).as_str(), "")?;
tx.write_string(
format!("{}/port", frontend_path).as_str(),
&port.to_string(),
)?;
tx.write_string(
format!("{}/ring-ref", frontend_path).as_str(),
&mfn.to_string(),
)?;
tx.commit()?; tx.commit()?;
Ok(()) Ok(())
} }
} }

View File

@ -1,6 +1,6 @@
use crate::sys::{XsdMessageHeader, XSD_ERROR}; use crate::sys::{XsdMessageHeader, XSD_ERROR};
use std::error::Error; use std::error::Error;
use std::ffi::{CString, FromVecWithNulError, NulError}; use std::ffi::{CString, FromVecWithNulError, IntoStringError, NulError};
use std::fs::metadata; use std::fs::metadata;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::mem::size_of; use std::mem::size_of;
@ -83,6 +83,12 @@ impl From<ParseIntError> for XsdBusError {
} }
} }
impl From<IntoStringError> for XsdBusError {
fn from(_: IntoStringError) -> Self {
XsdBusError::new("Unable to coerce data into a string.")
}
}
pub struct XsdSocket { pub struct XsdSocket {
handle: UnixStream, handle: UnixStream,
} }
@ -95,7 +101,7 @@ pub struct XsdResponse {
impl XsdResponse { impl XsdResponse {
pub fn parse_string(&self) -> Result<String, XsdBusError> { pub fn parse_string(&self) -> Result<String, XsdBusError> {
Ok(String::from_utf8(self.payload.clone())?) Ok(CString::from_vec_with_nul(self.payload.clone())?.into_string()?)
} }
pub fn parse_string_vec(&self) -> Result<Vec<String>, XsdBusError> { pub fn parse_string_vec(&self) -> Result<Vec<String>, XsdBusError> {
@ -114,8 +120,10 @@ impl XsdResponse {
} }
pub fn parse_bool(&self) -> Result<bool, XsdBusError> { pub fn parse_bool(&self) -> Result<bool, XsdBusError> {
if self.payload.len() != 1 { if self.payload.is_empty() {
Err(XsdBusError::new("Expected payload to be a single byte.")) Err(XsdBusError::new(
"Expected bool payload to be at least one byte.",
))
} else { } else {
Ok(self.payload[0] == 0) Ok(self.payload[0] == 0)
} }

View File

@ -9,14 +9,23 @@ pub struct XsdClient {
pub socket: XsdSocket, pub socket: XsdSocket,
} }
pub struct XsPermissions {
pub id: u32,
pub perms: u32,
}
pub trait XsdInterface { pub trait XsdInterface {
fn list(&mut self, path: &str) -> Result<Vec<String>, XsdBusError>; fn list(&mut self, path: &str) -> Result<Vec<String>, XsdBusError>;
fn read(&mut self, path: &str) -> Result<Vec<u8>, XsdBusError>; fn read(&mut self, path: &str) -> Result<Vec<u8>, XsdBusError>;
fn read_string(&mut self, path: &str) -> Result<String, XsdBusError>; fn read_string(&mut self, path: &str) -> Result<String, XsdBusError>;
fn write(&mut self, path: &str, data: Vec<u8>) -> Result<bool, XsdBusError>; fn write(&mut self, path: &str, data: Vec<u8>) -> Result<bool, XsdBusError>;
fn write_string(&mut self, path: &str, data: String) -> Result<bool, XsdBusError>; fn write_string(&mut self, path: &str, data: &str) -> Result<bool, XsdBusError>;
fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError>; fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError>;
fn rm(&mut self, path: &str) -> Result<bool, XsdBusError>; fn rm(&mut self, path: &str) -> Result<bool, XsdBusError>;
fn mknod(&mut self, path: &str, _perm: &XsPermissions) -> Result<bool, XsdBusError> {
self.write_string(path, "")
}
} }
impl XsdClient { impl XsdClient {
@ -53,8 +62,9 @@ impl XsdClient {
} }
pub fn transaction(&mut self) -> Result<XsdTransaction, XsdBusError> { pub fn transaction(&mut self) -> Result<XsdTransaction, XsdBusError> {
let response = self.socket.send(0, XSD_TRANSACTION_START, &[])?; let response = self.socket.send_single(0, XSD_TRANSACTION_START, "")?;
let tx = response.parse_string()?.parse::<u32>()?; let str = response.parse_string()?;
let tx = str.parse::<u32>()?;
Ok(XsdTransaction { client: self, tx }) Ok(XsdTransaction { client: self, tx })
} }
@ -68,7 +78,7 @@ impl XsdClient {
pub fn introduce_domain( pub fn introduce_domain(
&mut self, &mut self,
domid: u32, domid: u32,
mfn: u32, mfn: u64,
eventchn: u32, eventchn: u32,
) -> Result<String, XsdBusError> { ) -> Result<String, XsdBusError> {
let response = self.socket.send_multiple( let response = self.socket.send_multiple(
@ -106,8 +116,8 @@ impl XsdInterface for XsdClient {
self.write(0, path, data) self.write(0, path, data)
} }
fn write_string(&mut self, path: &str, data: String) -> Result<bool, XsdBusError> { fn write_string(&mut self, path: &str, data: &str) -> Result<bool, XsdBusError> {
self.write(0, path, data.into_bytes()) self.write(0, path, data.as_bytes().to_vec())
} }
fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError> { fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError> {
@ -136,8 +146,8 @@ impl XsdInterface for XsdTransaction<'_> {
self.client.write(self.tx, path, data) self.client.write(self.tx, path, data)
} }
fn write_string(&mut self, path: &str, data: String) -> Result<bool, XsdBusError> { fn write_string(&mut self, path: &str, data: &str) -> Result<bool, XsdBusError> {
self.client.write(self.tx, path, data.into_bytes()) self.client.write(self.tx, path, data.as_bytes().to_vec())
} }
fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError> { fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError> {