mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 13:11:31 +00:00
oh my god, we have a console
This commit is contained in:
@ -32,10 +32,6 @@ env_logger = "0.10.1"
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xenclient-simple"
|
||||
path = "examples/simple.rs"
|
||||
|
||||
[[example]]
|
||||
name = "xenclient-boot"
|
||||
path = "examples/boot.rs"
|
||||
|
@ -1,13 +1,6 @@
|
||||
use std::fs::read;
|
||||
use std::{env, process};
|
||||
use xencall::domctl::DomainControl;
|
||||
use xencall::memory::MemoryControl;
|
||||
use xencall::sys::CreateDomain;
|
||||
use xencall::XenCall;
|
||||
use xenclient::boot::BootSetup;
|
||||
use xenclient::elfloader::ElfImageLoader;
|
||||
use xenclient::XenClientError;
|
||||
use xenevtchn::EventChannel;
|
||||
use xenclient::create::DomainConfig;
|
||||
use xenclient::{XenClient, XenClientError};
|
||||
|
||||
fn main() -> Result<(), XenClientError> {
|
||||
env_logger::init();
|
||||
@ -19,40 +12,15 @@ fn main() -> Result<(), XenClientError> {
|
||||
}
|
||||
let kernel_image_path = args.get(1).expect("argument not specified");
|
||||
let initrd_path = args.get(2).expect("argument not specified");
|
||||
let call = XenCall::open()?;
|
||||
let domctl = DomainControl::new(&call);
|
||||
let domain = CreateDomain {
|
||||
let mut client = XenClient::open()?;
|
||||
let config = DomainConfig {
|
||||
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)?;
|
||||
boot(
|
||||
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()?;
|
||||
|
||||
let domid = client.create(config)?;
|
||||
println!("created domain {}", domid);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
@ -16,8 +16,6 @@ use std::cmp::{max, min};
|
||||
use std::ffi::c_void;
|
||||
use std::mem::size_of;
|
||||
use std::slice;
|
||||
use xencall::domctl::DomainControl;
|
||||
use xencall::memory::MemoryControl;
|
||||
use xencall::sys::{VcpuGuestContext, MMUEXT_PIN_L4_TABLE};
|
||||
use xencall::XenCall;
|
||||
|
||||
@ -41,9 +39,8 @@ pub struct BootImageInfo {
|
||||
}
|
||||
|
||||
pub struct BootSetup<'a> {
|
||||
domctl: &'a DomainControl<'a>,
|
||||
memctl: &'a MemoryControl<'a>,
|
||||
phys: PhysicalPages<'a>,
|
||||
call: &'a XenCall,
|
||||
pub phys: PhysicalPages<'a>,
|
||||
domid: u32,
|
||||
virt_alloc_end: u64,
|
||||
pfn_alloc_end: u64,
|
||||
@ -55,7 +52,7 @@ pub struct BootSetup<'a> {
|
||||
pub struct DomainSegment {
|
||||
vstart: u64,
|
||||
_vend: u64,
|
||||
pfn: u64,
|
||||
pub pfn: u64,
|
||||
addr: u64,
|
||||
size: u64,
|
||||
pages: u64,
|
||||
@ -87,15 +84,9 @@ pub struct BootState {
|
||||
}
|
||||
|
||||
impl BootSetup<'_> {
|
||||
pub fn new<'a>(
|
||||
call: &'a XenCall,
|
||||
domctl: &'a DomainControl<'a>,
|
||||
memctl: &'a MemoryControl<'a>,
|
||||
domid: u32,
|
||||
) -> BootSetup<'a> {
|
||||
pub fn new(call: &XenCall, domid: u32) -> BootSetup {
|
||||
BootSetup {
|
||||
domctl,
|
||||
memctl,
|
||||
call,
|
||||
phys: PhysicalPages::new(call, domid),
|
||||
domid,
|
||||
virt_alloc_end: 0,
|
||||
@ -106,7 +97,7 @@ impl BootSetup<'_> {
|
||||
}
|
||||
|
||||
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 stub = VmemRange {
|
||||
@ -162,7 +153,7 @@ impl BootSetup<'_> {
|
||||
}
|
||||
|
||||
let extents_init_slice = extents_init.as_slice();
|
||||
let extents = self.memctl.populate_physmap(
|
||||
let extents = self.call.populate_physmap(
|
||||
self.domid,
|
||||
count,
|
||||
SUPERPAGE_2MB_SHIFT as u32,
|
||||
@ -191,7 +182,7 @@ impl BootSetup<'_> {
|
||||
let p2m_end_idx = p2m_idx + allocsz as usize;
|
||||
let input_extent_starts = &p2m[p2m_idx..p2m_end_idx];
|
||||
let result =
|
||||
self.memctl
|
||||
self.call
|
||||
.populate_physmap(self.domid, allocsz, 0, 0, input_extent_starts)?;
|
||||
|
||||
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 mfn = self.phys.p2m[pfn as usize];
|
||||
self.domctl.hypercall_init(self.domid, mfn)?;
|
||||
self.call.hypercall_init(self.domid, mfn)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -241,8 +232,6 @@ impl BootSetup<'_> {
|
||||
"BootSetup initialize 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);
|
||||
self.initialize_memory(total_pages)?;
|
||||
@ -284,8 +273,8 @@ impl BootSetup<'_> {
|
||||
}
|
||||
|
||||
let initrd_segment = initrd_segment.unwrap();
|
||||
let store_evtchn = self.domctl.call.evtchn_alloc_unbound(self.domid, 0)?;
|
||||
let console_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.call.evtchn_alloc_unbound(self.domid, 0)?;
|
||||
let state = BootState {
|
||||
kernel_segment,
|
||||
start_info_segment,
|
||||
@ -306,7 +295,7 @@ impl BootSetup<'_> {
|
||||
}
|
||||
|
||||
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;
|
||||
state.shared_info_frame = shared_info_frame;
|
||||
self.setup_page_tables(state)?;
|
||||
@ -317,7 +306,7 @@ impl BootSetup<'_> {
|
||||
self.phys.unmap(pg_pfn)?;
|
||||
self.phys.unmap(state.p2m_segment.pfn)?;
|
||||
let pg_mfn = self.phys.p2m[pg_pfn as usize];
|
||||
self.memctl
|
||||
self.call
|
||||
.mmuext(self.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?;
|
||||
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_sp = vcpu.user_regs.rsp;
|
||||
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.gnttab_seed(state)?;
|
||||
Ok(())
|
||||
@ -356,13 +345,10 @@ impl BootSetup<'_> {
|
||||
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
|
||||
.domctl
|
||||
.call
|
||||
.mmap(0, 1 << XEN_PAGE_SHIFT)
|
||||
.ok_or(XenClientError::new("failed to mmap for resource"))?;
|
||||
self.domctl
|
||||
.call
|
||||
.map_resource(self.domid, 1, 0, 0, 1, addr)?;
|
||||
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;
|
||||
|
@ -1,19 +1,27 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct DomainConfig {
|
||||
vm_entries: HashMap<String, String>,
|
||||
domain_entries: HashMap<String, String>,
|
||||
pub max_vcpus: u32,
|
||||
pub mem_mb: u64,
|
||||
pub kernel_path: String,
|
||||
pub initrd_path: String,
|
||||
pub cmdline: String,
|
||||
}
|
||||
|
||||
pub struct PvDomainConfig {
|
||||
pub struct PvDomainStore {
|
||||
kernel: String,
|
||||
ramdisk: Option<String>,
|
||||
cmdline: Option<String>,
|
||||
}
|
||||
|
||||
impl DomainConfig {
|
||||
pub fn new() -> DomainConfig {
|
||||
DomainConfig {
|
||||
pub struct DomainStore {
|
||||
vm_entries: HashMap<String, String>,
|
||||
domain_entries: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl DomainStore {
|
||||
pub fn new() -> DomainStore {
|
||||
DomainStore {
|
||||
vm_entries: HashMap::new(),
|
||||
domain_entries: HashMap::new(),
|
||||
}
|
||||
@ -43,7 +51,7 @@ impl DomainConfig {
|
||||
|
||||
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("image/kernel", pv.kernel);
|
||||
|
||||
@ -67,15 +75,15 @@ impl DomainConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DomainConfig {
|
||||
impl Default for DomainStore {
|
||||
fn default() -> Self {
|
||||
DomainConfig::new()
|
||||
DomainStore::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PvDomainConfig {
|
||||
pub fn new(kernel: String, ramdisk: Option<String>, cmdline: Option<String>) -> PvDomainConfig {
|
||||
PvDomainConfig {
|
||||
impl PvDomainStore {
|
||||
pub fn new(kernel: String, ramdisk: Option<String>, cmdline: Option<String>) -> PvDomainStore {
|
||||
PvDomainStore {
|
||||
kernel,
|
||||
ramdisk,
|
||||
cmdline,
|
||||
|
@ -5,16 +5,19 @@ pub mod mem;
|
||||
pub mod sys;
|
||||
mod x86;
|
||||
|
||||
use crate::boot::BootSetup;
|
||||
use crate::create::DomainConfig;
|
||||
use crate::elfloader::ElfImageLoader;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::read;
|
||||
use std::string::FromUtf8Error;
|
||||
use xencall::domctl::DomainControl;
|
||||
use uuid::Uuid;
|
||||
use xencall::sys::CreateDomain;
|
||||
use xencall::{XenCall, XenCallError};
|
||||
use xenevtchn::EventChannelError;
|
||||
use xenstore::bus::XsdBusError;
|
||||
use xenstore::client::{XsdClient, XsdInterface};
|
||||
use xenstore::client::{XsPermissions, XsdClient, XsdInterface};
|
||||
|
||||
pub struct XenClient {
|
||||
store: XsdClient,
|
||||
@ -83,29 +86,208 @@ impl XenClient {
|
||||
Ok(XenClient { store, call })
|
||||
}
|
||||
|
||||
pub fn create(&mut self, config: DomainConfig) -> Result<(), XenClientError> {
|
||||
let domctl = DomainControl::new(&self.call);
|
||||
let domid = domctl.create_domain(CreateDomain::default())?;
|
||||
let domain = self.store.get_domain_path(domid)?;
|
||||
let vm = self.store.read_string(format!("{}/vm", domain).as_str())?;
|
||||
pub fn create(&mut self, config: DomainConfig) -> Result<u32, XenClientError> {
|
||||
let domain = CreateDomain {
|
||||
max_vcpus: config.max_vcpus,
|
||||
..Default::default()
|
||||
};
|
||||
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()?;
|
||||
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() {
|
||||
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(), 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.write_string(
|
||||
format!("{}/backend-id", frontend_path).as_str(),
|
||||
"0",
|
||||
)?;
|
||||
tx.write_string(
|
||||
format!("{}/limit", frontend_path).as_str(),
|
||||
"1048576",
|
||||
)?;
|
||||
tx.write_string(
|
||||
format!("{}/type", frontend_path).as_str(),
|
||||
"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()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user