mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-02 12:50:54 +00:00
implement memory allocation in boot setup
This commit is contained in:
parent
d46d0cf0c3
commit
153619a02c
@ -7,6 +7,9 @@ resolver = "2"
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "1.6.1"
|
||||
features = ["v4"]
|
||||
|
@ -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
40
xencall/src/memory.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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(())
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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
99
xenclient/src/mem.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user