diff --git a/xencall/Cargo.toml b/xencall/Cargo.toml index c5328b5..f5b636c 100644 --- a/xencall/Cargo.toml +++ b/xencall/Cargo.toml @@ -7,6 +7,9 @@ resolver = "2" [lib] path = "src/lib.rs" +[dependencies] +libc = "0.2" + [dependencies.uuid] version = "1.6.1" features = ["v4"] diff --git a/xencall/src/lib.rs b/xencall/src/lib.rs index 06cb493..b7bb933 100644 --- a/xencall/src/lib.rs +++ b/xencall/src/lib.rs @@ -1,12 +1,14 @@ pub mod domctl; +pub mod memory; pub mod sys; use crate::sys::{ - Hypercall, Mmap, XenCapabilitiesInfo, HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, + Hypercall, MmapBatch, XenCapabilitiesInfo, HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, }; +use libc::{mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE}; use nix::errno::Errno; use std::error::Error; -use std::ffi::{c_long, c_ulong}; +use std::ffi::{c_long, c_ulong, c_void}; use std::fmt::{Display, Formatter}; use std::fs::{File, OpenOptions}; use std::os::fd::AsRawFd; @@ -62,6 +64,24 @@ impl XenCall { Ok(XenCall { handle: file }) } + pub fn mmap(&self, addr: u64, len: u64) -> Option { + unsafe { + let ptr = mmap( + addr as *mut c_void, + len as usize, + PROT_READ | PROT_WRITE, + MAP_SHARED, + self.handle.as_raw_fd(), + 0, + ); + if ptr == MAP_FAILED { + None + } else { + Some(ptr as u64) + } + } + } + pub fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result { unsafe { let mut call = Hypercall { op, arg }; @@ -120,10 +140,24 @@ impl XenCall { self.hypercall(op, [arg1, arg2, arg3, arg4, arg5]) } - pub fn mmap(&self, mmap: Mmap) -> Result { + pub fn mmap_batch( + &self, + domid: u32, + count: u64, + addr: u64, + mfns: Vec, + ) -> Result { unsafe { - let mut mmap = mmap.clone(); - let result = sys::mmap(self.handle.as_raw_fd(), &mut mmap)?; + let mut mfns = mfns.clone(); + let mut errors = vec![0i32; mfns.len()]; + let mut batch = MmapBatch { + num: count as u32, + domid: domid as u16, + addr, + mfns: mfns.as_mut_ptr(), + errors: errors.as_mut_ptr(), + }; + let result = sys::mmapbatch(self.handle.as_raw_fd(), &mut batch)?; Ok(result as c_long) } } diff --git a/xencall/src/memory.rs b/xencall/src/memory.rs new file mode 100644 index 0000000..e41cd35 --- /dev/null +++ b/xencall/src/memory.rs @@ -0,0 +1,40 @@ +use crate::sys::{MemoryReservation, HYPERVISOR_MEMORY_OP, XEN_MEM_POPULATE_PHYSMAP}; +use crate::{XenCall, XenCallError}; + +use std::ffi::c_ulong; + +use std::ptr::addr_of; + +pub struct MemoryControl<'a> { + call: &'a XenCall, +} + +impl MemoryControl<'_> { + pub fn new(call: &XenCall) -> MemoryControl { + MemoryControl { call } + } + + pub fn populate_physmap( + &self, + domid: u32, + nr_extents: u64, + extent_order: u32, + mem_flags: u32, + extent_starts: &[u64], + ) -> Result, XenCallError> { + let extent_starts = extent_starts.to_vec(); + let reservation = MemoryReservation { + extent_start: addr_of!(extent_starts) as c_ulong, + nr_extents, + extent_order, + mem_flags, + domid: domid as u16, + }; + self.call.hypercall2( + HYPERVISOR_MEMORY_OP, + XEN_MEM_POPULATE_PHYSMAP as c_ulong, + addr_of!(reservation) as c_ulong, + )?; + Ok(extent_starts) + } +} diff --git a/xencall/src/sys.rs b/xencall/src/sys.rs index ab057e1..4aab3ca 100644 --- a/xencall/src/sys.rs +++ b/xencall/src/sys.rs @@ -1,6 +1,6 @@ /// Handwritten hypercall bindings. use nix::ioctl_readwrite_bad; -use std::ffi::{c_char, c_int, c_ulong}; +use std::ffi::{c_char, c_int, c_uint, c_ulong}; use uuid::Uuid; #[repr(C)] @@ -11,13 +11,23 @@ pub struct Hypercall { } #[repr(C)] -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] pub struct MmapEntry { pub va: u64, pub mfn: u64, pub npages: u64, } +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MmapBatch { + pub num: u32, + pub domid: u16, + pub addr: u64, + pub mfns: *mut u64, + pub errors: *mut c_int, +} + #[repr(C)] #[derive(Clone, Debug)] pub struct Mmap { @@ -28,9 +38,11 @@ pub struct Mmap { const IOCTL_PRIVCMD_HYPERCALL: u64 = 0x305000; const IOCTL_PRIVCMD_MMAP: u64 = 0x105002; +const IOCTL_PRIVCMD_MMAPBATCH_V2: u64 = 0x205004; ioctl_readwrite_bad!(hypercall, IOCTL_PRIVCMD_HYPERCALL, Hypercall); ioctl_readwrite_bad!(mmap, IOCTL_PRIVCMD_MMAP, Mmap); +ioctl_readwrite_bad!(mmapbatch, IOCTL_PRIVCMD_MMAPBATCH_V2, MmapBatch); pub const HYPERVISOR_SET_TRAP_TABLE: c_ulong = 0; pub const HYPERVISOR_MMU_UPDATE: c_ulong = 1; @@ -295,3 +307,15 @@ pub struct XenCapabilitiesInfo { } pub const XENVER_CAPABILITIES: u64 = 3; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct MemoryReservation { + pub extent_start: c_ulong, + pub nr_extents: c_ulong, + pub extent_order: c_uint, + pub mem_flags: c_uint, + pub domid: u16, +} + +pub const XEN_MEM_POPULATE_PHYSMAP: u32 = 6; diff --git a/xenclient/Cargo.toml b/xenclient/Cargo.toml index 3b51b54..c7b4027 100644 --- a/xenclient/Cargo.toml +++ b/xenclient/Cargo.toml @@ -5,10 +5,12 @@ edition = "2021" resolver = "2" [dependencies] +libc = "0.2" elf = "0.7.4" flate2 = "1.0" xz2 = "0.1" memchr = "2" +slice-copy = "0.3.0" [dependencies.xencall] path = "../xencall" diff --git a/xenclient/examples/boot.rs b/xenclient/examples/boot.rs index 25b8b1d..5f60cb8 100644 --- a/xenclient/examples/boot.rs +++ b/xenclient/examples/boot.rs @@ -1,9 +1,9 @@ -use std::alloc::Layout; use std::{env, process}; use xencall::domctl::DomainControl; +use xencall::memory::MemoryControl; use xencall::sys::CreateDomain; use xencall::XenCall; -use xenclient::boot::BootImageLoader; +use xenclient::boot::{BootImageLoader, BootSetup}; use xenclient::elfloader::ElfImageLoader; use xenclient::XenClientError; @@ -19,15 +19,12 @@ fn main() -> Result<(), XenClientError> { 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!("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)?; - // } + let image_loader = ElfImageLoader::load_file_kernel(kernel_image_path.as_str())?; + let image_info = image_loader.parse()?; + println!("loaded kernel image into memory: {:?}", image_info); + let memctl = MemoryControl::new(&call); + let mut boot = BootSetup::new(&call, &domctl, &memctl, domid, 512 * 1024); + boot.initialize(image_info)?; domctl.destroy_domain(domid)?; println!("domain destroyed: {}", domid); Ok(()) diff --git a/xenclient/src/boot.rs b/xenclient/src/boot.rs index 1cc01fe..cbe6a50 100644 --- a/xenclient/src/boot.rs +++ b/xenclient/src/boot.rs @@ -1,7 +1,17 @@ +use crate::mem::PhysicalPages; +use crate::sys::{ + SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, XEN_PAGE_SHIFT, +}; use crate::XenClientError; +use libc::memset; +use std::ffi::c_void; +use xencall::domctl::DomainControl; +use xencall::memory::MemoryControl; +use xencall::XenCall; pub trait BootImageLoader { - fn load(&self, dst: *mut u8) -> Result; + fn parse(&self) -> Result; + fn load(&self, image_info: BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError>; } pub const XEN_UNSET_ADDR: u64 = -1i64 as u64; @@ -11,6 +21,174 @@ pub struct BootImageInfo { pub virt_kstart: u64, pub virt_kend: u64, pub virt_hypercall: u64, - pub entry: u64, - pub hv_start_low: u64, + pub virt_entry: u64, + pub init_p2m: u64, +} + +pub struct BootSetup<'a> { + domctl: &'a DomainControl<'a>, + memctl: &'a MemoryControl<'a>, + phys: PhysicalPages<'a>, + domid: u32, + memkb: u64, + virt_alloc_end: u64, + pfn_alloc_end: u64, +} + +struct DomainSegment { + _vstart: u64, + _vend: u64, + pfn: u64, + _pages: u64, +} + +struct VmemRange { + start: u64, + end: u64, + _flags: u32, + _nid: u32, +} + +impl BootSetup<'_> { + pub fn new<'a>( + call: &'a XenCall, + domctl: &'a DomainControl<'a>, + memctl: &'a MemoryControl<'a>, + domid: u32, + memkb: u64, + ) -> BootSetup<'a> { + BootSetup { + domctl, + memctl, + phys: PhysicalPages::new(call, domid), + domid, + memkb, + virt_alloc_end: 0, + pfn_alloc_end: 0, + } + } + + fn initialize_memory(&mut self) -> Result<(), XenClientError> { + let mem_mb: u64 = self.memkb / 1024; + let page_count: u64 = mem_mb << (20 - XEN_PAGE_SHIFT); + let mut pfn_base_idx: u64 = 0; + let mut vmemranges: Vec = Vec::new(); + let stub = VmemRange { + start: 0, + end: page_count << XEN_PAGE_SHIFT, + _flags: 0, + _nid: 0, + }; + vmemranges.push(stub); + + let mut p2m_size: u64 = 0; + let mut total: u64 = 0; + for range in &vmemranges { + total += (range.end - range.start) >> XEN_PAGE_SHIFT; + p2m_size = p2m_size.max(range.end >> XEN_PAGE_SHIFT); + } + + if total != page_count { + return Err(XenClientError::new( + "Page count mismatch while calculating pages.", + )); + } + + let mut p2m = vec![-1i64 as u64; p2m_size as usize]; + for range in &vmemranges { + let mut extents = vec![0u64; SUPERPAGE_BATCH_SIZE as usize]; + let pages = (range.end - range.start) >> XEN_PAGE_SHIFT; + let pfn_base = range.start >> XEN_PAGE_SHIFT; + + for pfn in pfn_base..pfn_base + pages { + p2m[pfn as usize] = pfn; + } + + let mut super_pages = pages >> SUPERPAGE_2MB_SHIFT; + while super_pages > 0 { + let count = super_pages.min(SUPERPAGE_BATCH_SIZE); + super_pages -= count; + + for (i, pfn) in (pfn_base_idx..(count << SUPERPAGE_2MB_SHIFT)) + .step_by(SUPERPAGE_2MB_NR_PFNS as usize) + .enumerate() + { + extents[i] = p2m[pfn as usize]; + } + + let starts = self.memctl.populate_physmap( + self.domid, + count, + SUPERPAGE_2MB_SHIFT as u32, + 0, + extents.as_slice(), + )?; + + let pfn = pfn_base; + for mfn in starts { + for k in 0..SUPERPAGE_2MB_NR_PFNS { + p2m[pfn as usize] = mfn + k; + } + } + pfn_base_idx = pfn; + } + + let mut j = pfn_base_idx - pfn_base; + + loop { + if j >= pages { + break; + } + + let allocsz = (pages - j).min(1024 * 1024); + let result = self.memctl.populate_physmap( + self.domid, + allocsz, + 0, + 0, + &[p2m[(pfn_base + j) as usize]], + )?; + p2m[(pfn_base + j) as usize] = result[0]; + j += allocsz; + } + } + + self.phys.load_p2m(p2m); + Ok(()) + } + + fn initialize_hypercall(&mut self, image_info: BootImageInfo) -> Result<(), XenClientError> { + if image_info.virt_hypercall != XEN_UNSET_ADDR { + self.domctl + .hypercall_init(self.domid, image_info.virt_hypercall)?; + } + Ok(()) + } + + pub fn initialize(&mut self, image_info: BootImageInfo) -> Result<(), XenClientError> { + self.initialize_memory()?; + let _kernel_segment = self.alloc_segment(image_info.virt_kend - image_info.virt_kstart)?; + self.initialize_hypercall(image_info)?; + Ok(()) + } + + fn alloc_segment(&mut self, size: u64) -> Result { + let page_size = 1u64 << XEN_PAGE_SHIFT; + let pages = (size + page_size - 1) / page_size; + let start = self.virt_alloc_end; + let mut segment = DomainSegment { + _vstart: start, + _vend: 0, + pfn: self.pfn_alloc_end, + _pages: pages, + }; + let ptr = self.phys.pfn_to_ptr(segment.pfn, pages)?; + unsafe { + memset(ptr as *mut c_void, 0, (pages * page_size) as usize); + } + self.virt_alloc_end += pages * page_size; + segment._vend = self.virt_alloc_end; + self.pfn_alloc_end += 1; + Ok(segment) + } } diff --git a/xenclient/src/elfloader.rs b/xenclient/src/elfloader.rs index 1285eb0..adbd69b 100644 --- a/xenclient/src/elfloader.rs +++ b/xenclient/src/elfloader.rs @@ -1,6 +1,7 @@ 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, + XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HYPERCALL_PAGE, XEN_ELFNOTE_INIT_P2M, XEN_ELFNOTE_PADDR_OFFSET, + XEN_ELFNOTE_TYPES, XEN_ELFNOTE_VIRT_BASE, }; use crate::XenClientError; use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE}; @@ -9,7 +10,9 @@ use elf::note::Note; use elf::{ElfBytes, ParseError}; use flate2::bufread::GzDecoder; use memchr::memmem::find_iter; +use slice_copy::copy; use std::collections::HashMap; +use std::ffi::{FromVecWithNulError, IntoStringError}; use std::io::{BufReader, Read}; use std::mem::size_of; use xz2::bufread::XzDecoder; @@ -20,40 +23,48 @@ impl From for XenClientError { } } +impl From for XenClientError { + fn from(value: FromVecWithNulError) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + +impl From for XenClientError { + fn from(value: IntoStringError) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + pub struct ElfImageLoader { data: Vec, } -fn xen_note_value_as_u64( - endian: AnyEndian, - notes: &HashMap>, - key: u64, -) -> Option { - let value = notes.get(&key)?; +fn xen_note_value_as_u64(endian: AnyEndian, value: &[u8]) -> Option { + let bytes = value.to_vec(); match value.len() { 1 => { - let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); + let bytes: Option<[u8; size_of::()]> = bytes.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(); + let bytes: Option<[u8; size_of::()]> = bytes.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(); + let bytes: Option<[u8; size_of::()]> = bytes.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(); + let bytes: Option<[u8; size_of::()]> = bytes.try_into().ok(); Some(match endian { AnyEndian::Little => u64::from_le_bytes(bytes?), AnyEndian::Big => u64::from_be_bytes(bytes?), @@ -139,14 +150,18 @@ impl ElfImageLoader { } } +struct ElfNoteValue { + value: u64, +} + impl BootImageLoader for ElfImageLoader { - fn load(&self, dst: *mut u8) -> Result { + fn parse(&self) -> Result { let elf = ElfBytes::::minimal_parse(self.data.as_slice())?; let headers = elf.section_headers().ok_or(XenClientError::new( "Unable to parse kernel image: section headers not found.", ))?; let mut linux_notes: HashMap> = HashMap::new(); - let mut xen_notes: HashMap> = HashMap::new(); + let mut xen_notes: HashMap = HashMap::new(); for header in headers { if header.sh_type != SHT_NOTE { @@ -161,7 +176,19 @@ impl BootImageLoader for ElfImageLoader { } if note.name == "Xen" { - xen_notes.insert(note.n_type, note.desc.to_vec()); + for typ in XEN_ELFNOTE_TYPES { + if typ.id != note.n_type { + continue; + } + + let value = if !typ.is_string { + xen_note_value_as_u64(elf.ehdr.endianness, note.desc).unwrap_or(0) + } else { + 0 + }; + + xen_notes.insert(typ.id, ElfNoteValue { value }); + } continue; } } @@ -180,23 +207,34 @@ impl BootImageLoader for ElfImageLoader { )); } - 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_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 paddr_offset = xen_notes + .get(&XEN_ELFNOTE_PADDR_OFFSET) + .ok_or(XenClientError::new( + "Unable to find paddr_offset note in kernel.", + ))? + .value; + let virt_base = xen_notes + .get(&XEN_ELFNOTE_VIRT_BASE) + .ok_or(XenClientError::new( + "Unable to find virt_base note in kernel.", + ))? + .value; + let entry = xen_notes + .get(&XEN_ELFNOTE_ENTRY) + .ok_or(XenClientError::new("Unable to find entry note in kernel."))? + .value; + let virt_hypercall = xen_notes + .get(&XEN_ELFNOTE_HYPERCALL_PAGE) + .ok_or(XenClientError::new( + "Unable to find hypercall_page note in kernel.", + ))? + .value; + let init_p2m = xen_notes + .get(&XEN_ELFNOTE_INIT_P2M) + .ok_or(XenClientError::new( + "Unable to find init_p2m note in kernel.", + ))? + .value; let mut start: u64 = u64::MAX; let mut end: u64 = 0; @@ -204,12 +242,13 @@ impl BootImageLoader for ElfImageLoader { let segments = elf.segments().ok_or(XenClientError::new( "Unable to parse kernel image: segments not found.", ))?; - for segment in segments { - if (segment.p_type != PT_LOAD) || (segment.p_flags & (PF_R | PF_W | PF_X)) == 0 { + + for header in segments { + if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 { continue; } - let paddr = segment.p_paddr; - let memsz = segment.p_memsz; + let paddr = header.p_paddr; + let memsz = header.p_memsz; if start > paddr { start = paddr; } @@ -219,34 +258,61 @@ impl BootImageLoader for ElfImageLoader { } } - let base_dst_addr = dst as u64; - for header in segments { - let paddr = header.p_paddr; - let filesz = header.p_filesz; - let memsz = header.p_memsz; - let dest = base_dst_addr + paddr - start; - let data = elf.segment_data(&header)?; - - unsafe { - std::ptr::copy(data.as_ptr(), dest as *mut u8, filesz as usize); - std::ptr::write_bytes((dest + filesz) as *mut u8, 0, (memsz - filesz) as usize); - } + if paddr_offset != XEN_UNSET_ADDR && virt_base == XEN_UNSET_ADDR { + return Err(XenClientError::new( + "Unable to load kernel image: paddr_offset set but virt_base is unset.", + )); } + 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); + + let paddr_offset = if paddr_offset == XEN_UNSET_ADDR { + 0 + } else { + paddr_offset + }; + + let virt_offset = virt_base - paddr_offset; + let virt_kstart = start + virt_offset; + let virt_kend = end + virt_offset; + let virt_entry = if entry == XEN_UNSET_ADDR { + elf.ehdr.e_entry + } else { + entry + }; Ok(BootImageInfo { virt_kstart, virt_kend, virt_hypercall, - entry, - hv_start_low, + virt_entry, + init_p2m, }) } + + fn load(&self, image_info: BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError> { + let elf = ElfBytes::::minimal_parse(self.data.as_slice())?; + let segments = elf.segments().ok_or(XenClientError::new( + "Unable to parse kernel image: segments not found.", + ))?; + + for header in segments { + let paddr = header.p_paddr; + let filesz = header.p_filesz; + let memsz = header.p_memsz; + let base_offset = paddr - image_info.virt_kstart; + let data = elf.segment_data(&header)?; + let segment_dst = &mut dst[base_offset as usize..]; + copy(segment_dst, &data[0..filesz as usize]); + if memsz - filesz > 0 { + let remaining = &mut segment_dst[filesz as usize..(memsz - filesz) as usize]; + remaining.fill(0); + } + } + Ok(()) + } } diff --git a/xenclient/src/lib.rs b/xenclient/src/lib.rs index fbcfe9c..b7345ff 100644 --- a/xenclient/src/lib.rs +++ b/xenclient/src/lib.rs @@ -1,6 +1,7 @@ pub mod boot; pub mod create; pub mod elfloader; +pub mod mem; pub mod sys; use crate::create::DomainConfig; diff --git a/xenclient/src/mem.rs b/xenclient/src/mem.rs new file mode 100644 index 0000000..a81e06d --- /dev/null +++ b/xenclient/src/mem.rs @@ -0,0 +1,99 @@ +use crate::sys::XEN_PAGE_SHIFT; +use crate::XenClientError; + +use xencall::sys::MmapEntry; +use xencall::XenCall; + +pub struct PhysicalPage { + pfn: u64, + ptr: u64, + size: u64, +} + +pub struct PhysicalPages<'a> { + domid: u32, + p2m: Vec, + call: &'a XenCall, + pages: Vec, +} + +impl PhysicalPages<'_> { + pub fn new(call: &XenCall, domid: u32) -> PhysicalPages { + PhysicalPages { + domid, + p2m: Vec::new(), + call, + pages: Vec::new(), + } + } + + pub fn load_p2m(&mut self, p2m: Vec) { + self.p2m = p2m; + } + + pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result { + for page in &self.pages { + if pfn >= page.pfn + page.size { + continue; + } + + if count > 0 { + if (pfn + count) <= page.pfn { + continue; + } + + if pfn < page.pfn || (pfn + count) > page.pfn + page.size { + return Err(XenClientError::new("request overlaps allocated block")); + } + } else { + if pfn < page.pfn { + continue; + } + + if pfn >= page.pfn + page.size { + continue; + } + } + + return Ok(page.ptr + ((pfn - page.pfn) << XEN_PAGE_SHIFT)); + } + + if count == 0 { + return Err(XenClientError::new( + "allocation is only allowed when a size is given", + )); + } + + self.pfn_alloc(pfn, count) + } + + fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result { + let mut entries = vec![MmapEntry::default(); count as usize]; + for (i, entry) in (0_u64..).zip(entries.iter_mut()) { + entry.mfn = self.p2m[(pfn + i) as usize]; + } + let chunk_size = 1 << XEN_PAGE_SHIFT; + let num_per_entry = chunk_size >> XEN_PAGE_SHIFT; + let num = num_per_entry * entries.len(); + let mut pfns = vec![0u64; num]; + for i in 0..entries.len() { + for j in 0..num_per_entry { + pfns[i * num_per_entry + j] = entries[i].mfn + j as u64; + } + } + + let size = (num as u64) << XEN_PAGE_SHIFT; + let addr = self + .call + .mmap(0, size) + .ok_or(XenClientError::new("failed to mmap address"))?; + self.call.mmap_batch(self.domid, num as u64, addr, pfns)?; + let page = PhysicalPage { + pfn, + ptr: addr, + size, + }; + self.pages.push(page); + Ok(addr) + } +} diff --git a/xenclient/src/sys.rs b/xenclient/src/sys.rs index 58c8b7e..a9e6a84 100644 --- a/xenclient/src/sys.rs +++ b/xenclient/src/sys.rs @@ -17,3 +17,105 @@ pub const XEN_ELFNOTE_INIT_P2M: u64 = 15; pub const XEN_ELFNOTE_MOD_START_PFN: u64 = 16; pub const XEN_ELFNOTE_SUPPORTED_FEATURES: u64 = 17; pub const XEN_ELFNOTE_PHYS32_ENTRY: u64 = 18; + +#[derive(Copy, Clone)] +pub struct ElfNoteXenType { + pub id: u64, + pub name: &'static str, + pub is_string: bool, +} + +pub const XEN_ELFNOTE_TYPES: &[ElfNoteXenType] = &[ + ElfNoteXenType { + id: XEN_ELFNOTE_ENTRY, + name: "ENTRY", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_HYPERCALL_PAGE, + name: "HYPERCALL_PAGE", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_VIRT_BASE, + name: "VIRT_BASE", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_INIT_P2M, + name: "INIT_P2M", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_PADDR_OFFSET, + name: "PADDR_OFFSET", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_HV_START_LOW, + name: "HV_START_LOW", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_XEN_VERSION, + name: "XEN_VERSION", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_GUEST_OS, + name: "GUEST_OS", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_GUEST_VERSION, + name: "GUEST_VERSION", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_LOADER, + name: "LOADER", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_PAE_MODE, + name: "PAE_MODE", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_FEATURES, + name: "FEATURES", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_SUPPORTED_FEATURES, + name: "SUPPORTED_FEATURES", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_BSD_SYMTAB, + name: "BSD_SYMTAB", + is_string: true, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_SUSPEND_CANCEL, + name: "SUSPEND_CANCEL", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_MOD_START_PFN, + name: "MOD_START_PFN", + is_string: false, + }, + ElfNoteXenType { + id: XEN_ELFNOTE_PHYS32_ENTRY, + name: "PHYS32_ENTRY", + is_string: false, + }, +]; + +pub const XEN_PAGE_SHIFT: u64 = 12; +pub const XEN_PAGE_SIZE: u64 = 1 << XEN_PAGE_SHIFT; +pub const XEN_PAGE_MASK: u64 = !(XEN_PAGE_SIZE - 1); +pub const SUPERPAGE_BATCH_SIZE: u64 = 512; +pub const SUPERPAGE_2MB_SHIFT: u64 = 9; +pub const SUPERPAGE_2MB_NR_PFNS: u64 = 1u64 << SUPERPAGE_2MB_SHIFT;