implement memory allocation in boot setup

This commit is contained in:
Alex Zenla 2024-01-10 16:07:57 -08:00
parent d46d0cf0c3
commit 153619a02c
No known key found for this signature in database
GPG Key ID: 067B238899B51269
11 changed files with 619 additions and 73 deletions

View File

@ -7,6 +7,9 @@ resolver = "2"
[lib]
path = "src/lib.rs"
[dependencies]
libc = "0.2"
[dependencies.uuid]
version = "1.6.1"
features = ["v4"]

View File

@ -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<u64> {
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<c_long, XenCallError> {
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<c_long, XenCallError> {
pub fn mmap_batch(
&self,
domid: u32,
count: u64,
addr: u64,
mfns: Vec<u64>,
) -> Result<c_long, XenCallError> {
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)
}
}

40
xencall/src/memory.rs Normal file
View File

@ -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<Vec<u64>, 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)
}
}

View File

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

View File

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

View File

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

View File

@ -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<BootImageInfo, XenClientError>;
fn parse(&self) -> Result<BootImageInfo, XenClientError>;
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<VmemRange> = 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<DomainSegment, XenClientError> {
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)
}
}

View File

@ -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<ParseError> for XenClientError {
}
}
impl From<FromVecWithNulError> for XenClientError {
fn from(value: FromVecWithNulError) -> Self {
XenClientError::new(value.to_string().as_str())
}
}
impl From<IntoStringError> for XenClientError {
fn from(value: IntoStringError) -> Self {
XenClientError::new(value.to_string().as_str())
}
}
pub struct ElfImageLoader {
data: Vec<u8>,
}
fn xen_note_value_as_u64(
endian: AnyEndian,
notes: &HashMap<u64, Vec<u8>>,
key: u64,
) -> Option<u64> {
let value = notes.get(&key)?;
fn xen_note_value_as_u64(endian: AnyEndian, value: &[u8]) -> Option<u64> {
let bytes = value.to_vec();
match value.len() {
1 => {
let bytes: Option<[u8; size_of::<u8>()]> = value.clone().try_into().ok();
let bytes: Option<[u8; size_of::<u8>()]> = 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::<u16>()]> = value.clone().try_into().ok();
let bytes: Option<[u8; size_of::<u16>()]> = 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::<u32>()]> = value.clone().try_into().ok();
let bytes: Option<[u8; size_of::<u32>()]> = 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::<u64>()]> = value.clone().try_into().ok();
let bytes: Option<[u8; size_of::<u64>()]> = 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<BootImageInfo, XenClientError> {
fn parse(&self) -> Result<BootImageInfo, XenClientError> {
let elf = ElfBytes::<AnyEndian>::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<u64, Vec<u8>> = HashMap::new();
let mut xen_notes: HashMap<u64, Vec<u8>> = HashMap::new();
let mut xen_notes: HashMap<u64, ElfNoteValue> = 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::<AnyEndian>::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(())
}
}

View File

@ -1,6 +1,7 @@
pub mod boot;
pub mod create;
pub mod elfloader;
pub mod mem;
pub mod sys;
use crate::create::DomainConfig;

99
xenclient/src/mem.rs Normal file
View File

@ -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<u64>,
call: &'a XenCall,
pages: Vec<PhysicalPage>,
}
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<u64>) {
self.p2m = p2m;
}
pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64, XenClientError> {
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<u64, XenClientError> {
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)
}
}

View File

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