cleanup elf loader and more work towards boot support

This commit is contained in:
Alex Zenla 2024-01-10 10:08:39 -08:00
parent 5ff0ea8a1b
commit d46d0cf0c3
No known key found for this signature in database
GPG Key ID: 067B238899B51269
7 changed files with 134 additions and 53 deletions

View File

@ -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(())
}

View File

@ -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<CreatedDomain, XenCallError> {
pub fn create_domain(&self, create_domain: CreateDomain) -> Result<u32, XenCallError> {
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(())
}
}

View File

@ -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;

View File

@ -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<String> = env::args().collect();
if args.len() != 2 {
println!("usage: boot <kernel-image>");
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(())
}

View File

@ -4,10 +4,13 @@ pub trait BootImageLoader {
fn load(&self, dst: *mut u8) -> Result<BootImageInfo, XenClientError>;
}
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,
}

View File

@ -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<u8>,
}
fn xen_note_value_u64(endian: AnyEndian, notes: &HashMap<u64, Vec<u8>>, key: u64) -> Option<u64> {
let value = notes.get(&key);
value?;
let value = value.unwrap();
let bytes: Option<[u8; size_of::<u64>()]> = 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<u64, Vec<u8>>,
key: u64,
) -> Option<u64> {
let value = notes.get(&key)?;
match value.len() {
1 => {
let bytes: Option<[u8; size_of::<u8>()]> = 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::<u16>()]> = 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::<u32>()]> = 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::<u64>()]> = 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,
})

View File

@ -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);