From d46d0cf0c37e022a5a821eec77ff135bb14257b3 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Wed, 10 Jan 2024 10:08:39 -0800 Subject: [PATCH] cleanup elf loader and more work towards boot support --- xencall/examples/domain_create.rs | 4 +- xencall/src/domctl.rs | 46 ++++++++++----- xencall/src/sys.rs | 8 +++ xenclient/examples/boot.rs | 22 ++++++- xenclient/src/boot.rs | 3 + xenclient/src/elfloader.rs | 98 +++++++++++++++++++++---------- xenclient/src/lib.rs | 6 +- 7 files changed, 134 insertions(+), 53 deletions(-) diff --git a/xencall/examples/domain_create.rs b/xencall/examples/domain_create.rs index f2b2a26..c531363 100644 --- a/xencall/examples/domain_create.rs +++ b/xencall/examples/domain_create.rs @@ -5,7 +5,7 @@ use xencall::{XenCall, XenCallError}; fn main() -> Result<(), XenCallError> { let call = XenCall::open()?; let domctl: DomainControl = DomainControl::new(&call); - let info = domctl.create_domain(CreateDomain::default())?; - println!("created domain {}", info.domid); + let domid = domctl.create_domain(CreateDomain::default())?; + println!("created domain {}", domid); Ok(()) } diff --git a/xencall/src/domctl.rs b/xencall/src/domctl.rs index 4a94523..092051e 100644 --- a/xencall/src/domctl.rs +++ b/xencall/src/domctl.rs @@ -1,7 +1,8 @@ use crate::sys::{ - ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, GetDomainInfo, MaxMem, MaxVcpus, - HYPERVISOR_DOMCTL, XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_GETDOMAININFO, - XEN_DOMCTL_INTERFACE_VERSION, XEN_DOMCTL_MAX_MEM, XEN_DOMCTL_MAX_VCPUS, + ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, GetDomainInfo, HypercallInit, MaxMem, + MaxVcpus, HYPERVISOR_DOMCTL, XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN, + XEN_DOMCTL_GETDOMAININFO, XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_INTERFACE_VERSION, + XEN_DOMCTL_MAX_MEM, XEN_DOMCTL_MAX_VCPUS, }; use crate::{XenCall, XenCallError}; use std::ffi::c_ulong; @@ -11,10 +12,6 @@ pub struct DomainControl<'a> { call: &'a XenCall, } -pub struct CreatedDomain { - pub domid: u32, -} - impl DomainControl<'_> { pub fn new(call: &XenCall) -> DomainControl { DomainControl { call } @@ -55,10 +52,7 @@ impl DomainControl<'_> { Ok(unsafe { domctl.value.get_domain_info }) } - pub fn create_domain( - &self, - create_domain: CreateDomain, - ) -> Result { + pub fn create_domain(&self, create_domain: CreateDomain) -> Result { let domctl = DomCtl { cmd: XEN_DOMCTL_CREATEDOMAIN, interface_version: XEN_DOMCTL_INTERFACE_VERSION, @@ -67,9 +61,7 @@ impl DomainControl<'_> { }; self.call .hypercall1(HYPERVISOR_DOMCTL, addr_of!(domctl) as c_ulong)?; - Ok(CreatedDomain { - domid: domctl.domid, - }) + Ok(domctl.domid) } pub fn set_max_mem(&mut self, domid: u32, memkb: u64) -> Result<(), XenCallError> { @@ -99,4 +91,30 @@ impl DomainControl<'_> { .hypercall1(HYPERVISOR_DOMCTL, addr_of!(domctl) as c_ulong)?; Ok(()) } + + pub fn hypercall_init(&self, domid: u32, gmfn: u64) -> Result<(), XenCallError> { + let 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!(domctl) as c_ulong)?; + Ok(()) + } + + pub fn destroy_domain(&self, domid: u32) -> Result<(), XenCallError> { + let 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!(domctl) as c_ulong)?; + Ok(()) + } } diff --git a/xencall/src/sys.rs b/xencall/src/sys.rs index 1514247..ab057e1 100644 --- a/xencall/src/sys.rs +++ b/xencall/src/sys.rs @@ -195,6 +195,8 @@ pub union DomCtlValue { pub get_domain_info: GetDomainInfo, pub max_mem: MaxMem, pub max_cpus: MaxVcpus, + pub hypercall_init: HypercallInit, + pub pad: [u8; 128], } #[repr(C)] @@ -277,6 +279,12 @@ pub struct MaxVcpus { pub max_vcpus: u32, } +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct HypercallInit { + pub gmfn: u64, +} + pub const XEN_DOMCTL_INTERFACE_VERSION: u32 = 0x00000015; pub const SECINITSID_DOMU: u32 = 13; diff --git a/xenclient/examples/boot.rs b/xenclient/examples/boot.rs index 0304e22..25b8b1d 100644 --- a/xenclient/examples/boot.rs +++ b/xenclient/examples/boot.rs @@ -1,4 +1,5 @@ use std::alloc::Layout; +use std::{env, process}; use xencall::domctl::DomainControl; use xencall::sys::CreateDomain; use xencall::XenCall; @@ -7,12 +8,27 @@ use xenclient::elfloader::ElfImageLoader; use xenclient::XenClientError; fn main() -> Result<(), XenClientError> { + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("usage: boot "); + process::exit(1); + } + let kernel_image_path = args.get(1).expect("argument not specified"); let call = XenCall::open()?; let domctl = DomainControl::new(&call); - let _domain = domctl.create_domain(CreateDomain::default())?; - let boot = ElfImageLoader::load_file_kernel("/boot/vmlinuz-6.1.0-17-amd64")?; + let domid = domctl.create_domain(CreateDomain::default())?; + let domain = domctl.get_domain_info(domid)?; + println!("domain created: {:?}", domain); + let boot = ElfImageLoader::load_file_kernel(kernel_image_path.as_str())?; let ptr = unsafe { std::alloc::alloc(Layout::from_size_align(128 * 1024 * 1024, 16).unwrap()) }; let info = boot.load(ptr)?; - println!("{:?}", info); + println!("loaded kernel image into memory: {:?}", info); + // The address calculations don't make sense here and I am certain something + // is wrong up the stack. + // if info.virt_hypercall != XEN_UNSET_ADDR { + // domctl.hypercall_init(domid, info.virt_hypercall)?; + // } + domctl.destroy_domain(domid)?; + println!("domain destroyed: {}", domid); Ok(()) } diff --git a/xenclient/src/boot.rs b/xenclient/src/boot.rs index d52d029..1cc01fe 100644 --- a/xenclient/src/boot.rs +++ b/xenclient/src/boot.rs @@ -4,10 +4,13 @@ pub trait BootImageLoader { fn load(&self, dst: *mut u8) -> Result; } +pub const XEN_UNSET_ADDR: u64 = -1i64 as u64; + #[derive(Debug)] pub struct BootImageInfo { pub virt_kstart: u64, pub virt_kend: u64, + pub virt_hypercall: u64, pub entry: u64, pub hv_start_low: u64, } diff --git a/xenclient/src/elfloader.rs b/xenclient/src/elfloader.rs index 01eace1..1285eb0 100644 --- a/xenclient/src/elfloader.rs +++ b/xenclient/src/elfloader.rs @@ -1,5 +1,7 @@ -use crate::boot::{BootImageInfo, BootImageLoader}; -use crate::sys::{XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HV_START_LOW, XEN_ELFNOTE_VIRT_BASE}; +use crate::boot::{BootImageInfo, BootImageLoader, XEN_UNSET_ADDR}; +use crate::sys::{ + XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HV_START_LOW, XEN_ELFNOTE_HYPERCALL_PAGE, XEN_ELFNOTE_VIRT_BASE, +}; use crate::XenClientError; use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE}; use elf::endian::AnyEndian; @@ -22,17 +24,43 @@ pub struct ElfImageLoader { data: Vec, } -fn xen_note_value_u64(endian: AnyEndian, notes: &HashMap>, key: u64) -> Option { - let value = notes.get(&key); - value?; - let value = value.unwrap(); - let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); - bytes?; - - Some(match endian { - AnyEndian::Little => u64::from_le_bytes(bytes.unwrap()), - AnyEndian::Big => u64::from_be_bytes(bytes.unwrap()), - }) +fn xen_note_value_as_u64( + endian: AnyEndian, + notes: &HashMap>, + key: u64, +) -> Option { + let value = notes.get(&key)?; + match value.len() { + 1 => { + let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); + Some(match endian { + AnyEndian::Little => u8::from_le_bytes(bytes?), + AnyEndian::Big => u8::from_be_bytes(bytes?), + } as u64) + } + 2 => { + let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); + Some(match endian { + AnyEndian::Little => u16::from_le_bytes(bytes?), + AnyEndian::Big => u16::from_be_bytes(bytes?), + } as u64) + } + 4 => { + let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); + Some(match endian { + AnyEndian::Little => u32::from_le_bytes(bytes?), + AnyEndian::Big => u32::from_be_bytes(bytes?), + } as u64) + } + 8 => { + let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); + Some(match endian { + AnyEndian::Little => u64::from_le_bytes(bytes?), + AnyEndian::Big => u64::from_be_bytes(bytes?), + }) + } + _ => None, + } } impl ElfImageLoader { @@ -100,11 +128,8 @@ impl ElfImageLoader { } for start in find_iter(file.as_slice(), &[0xfd, 0x37, 0x7a, 0x58]) { - match ElfImageLoader::load_xz(&file[start..]) { - Ok(elf) => return Ok(elf), - Err(error) => { - println!("{}", error); - } + if let Ok(elf) = ElfImageLoader::load_xz(&file[start..]) { + return Ok(elf); } } @@ -155,16 +180,23 @@ impl BootImageLoader for ElfImageLoader { )); } - let virt_base = xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_VIRT_BASE) - .ok_or(XenClientError::new( - "Unable to find virt_base note in kernel.", - ))?; - let entry = xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_ENTRY) + let virt_base = + xen_note_value_as_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_VIRT_BASE).ok_or( + XenClientError::new("Unable to find virt_base note in kernel."), + )?; + let entry = xen_note_value_as_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_ENTRY) .ok_or(XenClientError::new("Unable to find entry note in kernel."))?; let hv_start_low = - xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_HV_START_LOW).ok_or( - XenClientError::new("Unable to find hv_start_low note in kernel."), - )?; + xen_note_value_as_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_HV_START_LOW) + .ok_or(XenClientError::new( + "Unable to find hv_start_low note in kernel.", + ))?; + + let hypercall_page = + xen_note_value_as_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_HYPERCALL_PAGE) + .ok_or(XenClientError::new( + "Unable to find hypercall_page note in kernel.", + ))?; let mut start: u64 = u64::MAX; let mut end: u64 = 0; @@ -200,15 +232,19 @@ impl BootImageLoader for ElfImageLoader { std::ptr::write_bytes((dest + filesz) as *mut u8, 0, (memsz - filesz) as usize); } } - - let virt_base = if virt_base == u64::MAX { 0 } else { virt_base }; - - let virt_kstart = start + virt_base; - let virt_kend = end + virt_base; + let virt_base = if virt_base == XEN_UNSET_ADDR { + 0 + } else { + virt_base + }; + let virt_kstart = start.wrapping_add(virt_base); + let virt_kend = end.wrapping_add(virt_base); + let virt_hypercall = hypercall_page.wrapping_add(virt_base); Ok(BootImageInfo { virt_kstart, virt_kend, + virt_hypercall, entry, hv_start_low, }) diff --git a/xenclient/src/lib.rs b/xenclient/src/lib.rs index df49974..fbcfe9c 100644 --- a/xenclient/src/lib.rs +++ b/xenclient/src/lib.rs @@ -76,8 +76,8 @@ impl XenClient { pub fn create(&mut self, config: DomainConfig) -> Result<(), XenClientError> { let domctl = DomainControl::new(&self.call); - let created = domctl.create_domain(CreateDomain::default())?; - let domain = self.store.get_domain_path(created.domid)?; + 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())?; let mut tx = self.store.transaction()?; @@ -88,7 +88,7 @@ impl XenClient { } let domid_path = format!("{}/domid", domain); - tx.write(domid_path.as_str(), created.domid.to_string().into_bytes())?; + tx.write(domid_path.as_str(), domid.to_string().into_bytes())?; for (key, value) in config.clone_domain_entries() { let path = format!("{}/{}", vm, key);