mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-04 05:31:32 +00:00
feat(xen): update xenclient and xenplatform to the latest structure (#433)
This commit is contained in:
@ -8,16 +8,9 @@ use crate::{
|
||||
error::{Error, Result},
|
||||
mem::PhysicalPages,
|
||||
sys::XEN_PAGE_SHIFT,
|
||||
ImageLoader, PlatformKernelConfig, PlatformResourcesConfig,
|
||||
};
|
||||
|
||||
pub struct BootSetup<I: BootImageLoader, P: BootSetupPlatform> {
|
||||
pub call: XenCall,
|
||||
pub domid: u32,
|
||||
pub platform: P,
|
||||
pub image_loader: I,
|
||||
pub dtb: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct DomainSegment {
|
||||
pub vstart: u64,
|
||||
@ -42,7 +35,7 @@ pub struct BootDomain {
|
||||
pub phys: PhysicalPages,
|
||||
pub store_evtchn: u32,
|
||||
pub store_mfn: u64,
|
||||
pub initrd_segment: DomainSegment,
|
||||
pub initrd_segment: Option<DomainSegment>,
|
||||
pub console_evtchn: u32,
|
||||
pub console_mfn: u64,
|
||||
pub cmdline: String,
|
||||
@ -142,129 +135,8 @@ impl BootDomain {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: BootImageLoader, P: BootSetupPlatform> BootSetup<I, P> {
|
||||
pub fn new(
|
||||
call: XenCall,
|
||||
domid: u32,
|
||||
platform: P,
|
||||
image_loader: I,
|
||||
dtb: Option<Vec<u8>>,
|
||||
) -> BootSetup<I, P> {
|
||||
BootSetup {
|
||||
call,
|
||||
domid,
|
||||
platform,
|
||||
image_loader,
|
||||
dtb,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn initialize(
|
||||
&mut self,
|
||||
initrd: &[u8],
|
||||
target_mem_mb: u64,
|
||||
max_mem_mb: u64,
|
||||
max_vcpus: u32,
|
||||
cmdline: &str,
|
||||
) -> Result<BootDomain> {
|
||||
let target_pages = target_mem_mb << (20 - self.platform.page_shift());
|
||||
let total_pages = max_mem_mb << (20 - self.platform.page_shift());
|
||||
let image_info = self.image_loader.parse(self.platform.hvm()).await?;
|
||||
let mut domain = BootDomain {
|
||||
domid: self.domid,
|
||||
call: self.call.clone(),
|
||||
virt_alloc_end: 0,
|
||||
virt_pgtab_end: 0,
|
||||
pfn_alloc_end: 0,
|
||||
total_pages,
|
||||
target_pages,
|
||||
page_size: self.platform.page_size(),
|
||||
image_info,
|
||||
console_evtchn: 0,
|
||||
console_mfn: 0,
|
||||
max_vcpus,
|
||||
phys: PhysicalPages::new(self.call.clone(), self.domid, self.platform.page_shift()),
|
||||
initrd_segment: DomainSegment::default(),
|
||||
store_evtchn: 0,
|
||||
store_mfn: 0,
|
||||
cmdline: cmdline.to_string(),
|
||||
};
|
||||
|
||||
self.platform.initialize_early(&mut domain).await?;
|
||||
|
||||
let mut initrd_segment = if !domain.image_info.unmapped_initrd {
|
||||
Some(domain.alloc_module(initrd).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut kernel_segment = if self.platform.needs_early_kernel() {
|
||||
Some(self.load_kernel_segment(&mut domain).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.platform.initialize_memory(&mut domain).await?;
|
||||
domain.virt_alloc_end = domain.image_info.virt_base;
|
||||
|
||||
if kernel_segment.is_none() {
|
||||
kernel_segment = Some(self.load_kernel_segment(&mut domain).await?);
|
||||
}
|
||||
|
||||
if domain.image_info.unmapped_initrd {
|
||||
initrd_segment = Some(domain.alloc_module(initrd).await?);
|
||||
}
|
||||
|
||||
domain.initrd_segment =
|
||||
initrd_segment.ok_or(Error::MemorySetupFailed("initrd_segment missing"))?;
|
||||
|
||||
self.platform.alloc_magic_pages(&mut domain).await?;
|
||||
|
||||
domain.store_evtchn = self.call.evtchn_alloc_unbound(self.domid, 0).await?;
|
||||
|
||||
let _kernel_segment =
|
||||
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
||||
|
||||
Ok(domain)
|
||||
}
|
||||
|
||||
pub async fn boot(&mut self, domain: &mut BootDomain) -> Result<()> {
|
||||
let domain_info = self.call.get_domain_info(self.domid).await?;
|
||||
let shared_info_frame = domain_info.shared_info_frame;
|
||||
self.platform.setup_page_tables(domain).await?;
|
||||
self.platform
|
||||
.setup_start_info(domain, shared_info_frame)
|
||||
.await?;
|
||||
self.platform.setup_hypercall_page(domain).await?;
|
||||
self.platform.bootlate(domain).await?;
|
||||
self.platform
|
||||
.setup_shared_info(domain, shared_info_frame)
|
||||
.await?;
|
||||
self.platform.vcpu(domain).await?;
|
||||
domain.phys.unmap_all()?;
|
||||
self.platform.gnttab_seed(domain).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn load_kernel_segment(&mut self, domain: &mut BootDomain) -> Result<DomainSegment> {
|
||||
let kernel_segment = domain
|
||||
.alloc_segment(
|
||||
domain.image_info.virt_kstart,
|
||||
domain.image_info.virt_kend - domain.image_info.virt_kstart,
|
||||
)
|
||||
.await?;
|
||||
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
||||
let kernel_segment_slice =
|
||||
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
||||
self.image_loader
|
||||
.load(&domain.image_info, kernel_segment_slice)
|
||||
.await?;
|
||||
Ok(kernel_segment)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait BootSetupPlatform: Clone {
|
||||
pub trait BootSetupPlatform {
|
||||
fn create_domain(&self, enable_iommu: bool) -> CreateDomain;
|
||||
fn page_size(&self) -> u64;
|
||||
fn page_shift(&self) -> u64;
|
||||
@ -304,6 +176,135 @@ pub trait BootSetupPlatform: Clone {
|
||||
async fn vcpu(&mut self, domain: &mut BootDomain) -> Result<()>;
|
||||
|
||||
async fn setup_hypercall_page(&mut self, domain: &mut BootDomain) -> Result<()>;
|
||||
|
||||
async fn initialize_internal(
|
||||
&mut self,
|
||||
domid: u32,
|
||||
call: XenCall,
|
||||
image_loader: &ImageLoader,
|
||||
domain: &mut BootDomain,
|
||||
kernel: &PlatformKernelConfig,
|
||||
) -> Result<()> {
|
||||
self.initialize_early(domain).await?;
|
||||
|
||||
let mut initrd_segment = if !domain.image_info.unmapped_initrd && kernel.initrd.is_some() {
|
||||
Some(domain.alloc_module(kernel.initrd.as_ref().unwrap()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut kernel_segment = if self.needs_early_kernel() {
|
||||
Some(self.load_kernel_segment(image_loader, domain).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.initialize_memory(domain).await?;
|
||||
domain.virt_alloc_end = domain.image_info.virt_base;
|
||||
|
||||
if kernel_segment.is_none() {
|
||||
kernel_segment = Some(self.load_kernel_segment(image_loader, domain).await?);
|
||||
}
|
||||
|
||||
if domain.image_info.unmapped_initrd && kernel.initrd.is_some() {
|
||||
initrd_segment = Some(domain.alloc_module(kernel.initrd.as_ref().unwrap()).await?);
|
||||
}
|
||||
|
||||
domain.initrd_segment = initrd_segment;
|
||||
self.alloc_magic_pages(domain).await?;
|
||||
domain.store_evtchn = call.evtchn_alloc_unbound(domid, 0).await?;
|
||||
let _kernel_segment =
|
||||
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn initialize(
|
||||
&mut self,
|
||||
domid: u32,
|
||||
call: XenCall,
|
||||
image_loader: &ImageLoader,
|
||||
kernel: &PlatformKernelConfig,
|
||||
resources: &PlatformResourcesConfig,
|
||||
) -> Result<BootDomain> {
|
||||
let target_pages = resources.assigned_memory_mb << (20 - self.page_shift());
|
||||
let total_pages = resources.max_memory_mb << (20 - self.page_shift());
|
||||
let image_info = image_loader.parse(self.hvm()).await?;
|
||||
let mut domain = BootDomain {
|
||||
domid,
|
||||
call: call.clone(),
|
||||
virt_alloc_end: 0,
|
||||
virt_pgtab_end: 0,
|
||||
pfn_alloc_end: 0,
|
||||
total_pages,
|
||||
target_pages,
|
||||
page_size: self.page_size(),
|
||||
image_info,
|
||||
console_evtchn: 0,
|
||||
console_mfn: 0,
|
||||
max_vcpus: resources.max_vcpus,
|
||||
phys: PhysicalPages::new(call.clone(), domid, self.page_shift()),
|
||||
initrd_segment: None,
|
||||
store_evtchn: 0,
|
||||
store_mfn: 0,
|
||||
cmdline: kernel.cmdline.clone(),
|
||||
};
|
||||
match self
|
||||
.initialize_internal(domid, call, image_loader, &mut domain, kernel)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(domain),
|
||||
Err(error) => {
|
||||
domain.phys.unmap_all()?;
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn boot_internal(
|
||||
&mut self,
|
||||
call: XenCall,
|
||||
domid: u32,
|
||||
domain: &mut BootDomain,
|
||||
) -> Result<()> {
|
||||
let domain_info = call.get_domain_info(domid).await?;
|
||||
let shared_info_frame = domain_info.shared_info_frame;
|
||||
self.setup_page_tables(domain).await?;
|
||||
self.setup_start_info(domain, shared_info_frame).await?;
|
||||
self.setup_hypercall_page(domain).await?;
|
||||
self.bootlate(domain).await?;
|
||||
self.setup_shared_info(domain, shared_info_frame).await?;
|
||||
self.vcpu(domain).await?;
|
||||
self.gnttab_seed(domain).await?;
|
||||
domain.phys.unmap_all()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn boot(&mut self, domid: u32, call: XenCall, domain: &mut BootDomain) -> Result<()> {
|
||||
let result = self.boot_internal(call, domid, domain).await;
|
||||
domain.phys.unmap_all()?;
|
||||
result
|
||||
}
|
||||
|
||||
async fn load_kernel_segment(
|
||||
&mut self,
|
||||
image_loader: &ImageLoader,
|
||||
domain: &mut BootDomain,
|
||||
) -> Result<DomainSegment> {
|
||||
let kernel_segment = domain
|
||||
.alloc_segment(
|
||||
domain.image_info.virt_kstart,
|
||||
domain.image_info.virt_kend - domain.image_info.virt_kstart,
|
||||
)
|
||||
.await?;
|
||||
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
||||
let kernel_segment_slice =
|
||||
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
||||
image_loader
|
||||
.load(&domain.image_info, kernel_segment_slice)
|
||||
.await?;
|
||||
Ok(kernel_segment)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
boot::{BootSetup, BootSetupPlatform},
|
||||
elfloader::ElfImageLoader,
|
||||
boot::BootDomain, elfloader::ElfImageLoader, error::Error, ImageLoader, RuntimePlatform,
|
||||
RuntimePlatformType,
|
||||
};
|
||||
use log::warn;
|
||||
use uuid::Uuid;
|
||||
use xencall::XenCall;
|
||||
|
||||
@ -11,42 +12,92 @@ use crate::error::Result;
|
||||
|
||||
pub const XEN_EXTRA_MEMORY_KB: u64 = 2048;
|
||||
|
||||
pub struct BaseDomainManager<P: BootSetupPlatform> {
|
||||
pub struct PlatformDomainManager {
|
||||
call: XenCall,
|
||||
pub platform: Arc<P>,
|
||||
}
|
||||
|
||||
impl<P: BootSetupPlatform> BaseDomainManager<P> {
|
||||
pub async fn new(call: XenCall, platform: P) -> Result<BaseDomainManager<P>> {
|
||||
Ok(BaseDomainManager {
|
||||
call,
|
||||
platform: Arc::new(platform),
|
||||
})
|
||||
impl PlatformDomainManager {
|
||||
pub async fn new(call: XenCall) -> Result<PlatformDomainManager> {
|
||||
Ok(PlatformDomainManager { call })
|
||||
}
|
||||
|
||||
pub async fn create(&self, config: BaseDomainConfig) -> Result<CreatedDomain> {
|
||||
let mut domain = self.platform.create_domain(config.enable_iommu);
|
||||
fn max_memory_kb(resources: &PlatformResourcesConfig) -> u64 {
|
||||
(resources.max_memory_mb * 1024) + XEN_EXTRA_MEMORY_KB
|
||||
}
|
||||
|
||||
async fn create_base_domain(
|
||||
&self,
|
||||
config: &PlatformDomainConfig,
|
||||
platform: &RuntimePlatform,
|
||||
) -> Result<u32> {
|
||||
let mut domain = platform.create_domain(config.options.iommu);
|
||||
domain.handle = config.uuid.into_bytes();
|
||||
domain.max_vcpus = config.max_vcpus;
|
||||
domain.max_vcpus = config.resources.max_vcpus;
|
||||
let domid = self.call.create_domain(domain).await?;
|
||||
self.call.set_max_vcpus(domid, config.max_vcpus).await?;
|
||||
Ok(domid)
|
||||
}
|
||||
|
||||
async fn configure_domain_resources(
|
||||
&self,
|
||||
domid: u32,
|
||||
config: &PlatformDomainConfig,
|
||||
) -> Result<()> {
|
||||
self.call
|
||||
.set_max_mem(domid, (config.max_mem_mb * 1024) + XEN_EXTRA_MEMORY_KB)
|
||||
.set_max_vcpus(domid, config.resources.max_vcpus)
|
||||
.await?;
|
||||
let loader = ElfImageLoader::load_file_kernel(&config.kernel)?;
|
||||
let platform = (*self.platform).clone();
|
||||
let mut boot = BootSetup::new(self.call.clone(), domid, platform, loader, None);
|
||||
let mut domain = boot
|
||||
.initialize(
|
||||
&config.initrd,
|
||||
config.target_mem_mb,
|
||||
config.max_mem_mb,
|
||||
config.max_vcpus,
|
||||
&config.cmdline,
|
||||
self.call
|
||||
.set_max_mem(
|
||||
domid,
|
||||
PlatformDomainManager::max_memory_kb(&config.resources),
|
||||
)
|
||||
.await?;
|
||||
boot.boot(&mut domain).await?;
|
||||
Ok(CreatedDomain {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_internal(
|
||||
&self,
|
||||
domid: u32,
|
||||
config: &PlatformDomainConfig,
|
||||
mut platform: RuntimePlatform,
|
||||
) -> Result<BootDomain> {
|
||||
self.configure_domain_resources(domid, config).await?;
|
||||
let kernel = config.kernel.clone();
|
||||
let loader = tokio::task::spawn_blocking(move || match kernel.format {
|
||||
KernelFormat::ElfCompressed => ElfImageLoader::load(kernel.data),
|
||||
KernelFormat::ElfUncompressed => Ok(ElfImageLoader::new(kernel.data)),
|
||||
})
|
||||
.await
|
||||
.map_err(Error::AsyncJoinError)??;
|
||||
let loader = ImageLoader::Elf(loader);
|
||||
let mut domain = platform
|
||||
.initialize(
|
||||
domid,
|
||||
self.call.clone(),
|
||||
&loader,
|
||||
&config.kernel,
|
||||
&config.resources,
|
||||
)
|
||||
.await?;
|
||||
platform.boot(domid, self.call.clone(), &mut domain).await?;
|
||||
Ok(domain)
|
||||
}
|
||||
|
||||
pub async fn create(&self, config: PlatformDomainConfig) -> Result<PlatformDomainInfo> {
|
||||
let platform = config.platform.create();
|
||||
let domid = self.create_base_domain(&config, &platform).await?;
|
||||
let domain = match self.create_internal(domid, &config, platform).await {
|
||||
Ok(domain) => domain,
|
||||
Err(error) => {
|
||||
if let Err(destroy_fail) = self.call.destroy_domain(domid).await {
|
||||
warn!(
|
||||
"failed to destroy failed domain {}: {}",
|
||||
domid, destroy_fail
|
||||
);
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
Ok(PlatformDomainInfo {
|
||||
domid,
|
||||
store_evtchn: domain.store_evtchn,
|
||||
store_mfn: domain.store_mfn,
|
||||
@ -62,21 +113,43 @@ impl<P: BootSetupPlatform> BaseDomainManager<P> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BaseDomainConfig {
|
||||
pub struct PlatformDomainConfig {
|
||||
pub uuid: Uuid,
|
||||
pub owner_domid: u32,
|
||||
pub max_vcpus: u32,
|
||||
pub target_vcpus: u32,
|
||||
pub max_mem_mb: u64,
|
||||
pub target_mem_mb: u64,
|
||||
pub kernel: Vec<u8>,
|
||||
pub initrd: Vec<u8>,
|
||||
pub cmdline: String,
|
||||
pub enable_iommu: bool,
|
||||
pub platform: RuntimePlatformType,
|
||||
pub resources: PlatformResourcesConfig,
|
||||
pub kernel: PlatformKernelConfig,
|
||||
pub options: PlatformOptions,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CreatedDomain {
|
||||
pub struct PlatformKernelConfig {
|
||||
pub data: Arc<Vec<u8>>,
|
||||
pub format: KernelFormat,
|
||||
pub initrd: Option<Arc<Vec<u8>>>,
|
||||
pub cmdline: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlatformResourcesConfig {
|
||||
pub max_vcpus: u32,
|
||||
pub assigned_vcpus: u32,
|
||||
pub max_memory_mb: u64,
|
||||
pub assigned_memory_mb: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlatformOptions {
|
||||
pub iommu: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum KernelFormat {
|
||||
ElfUncompressed,
|
||||
ElfCompressed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlatformDomainInfo {
|
||||
pub domid: u32,
|
||||
pub store_evtchn: u32,
|
||||
pub store_mfn: u64,
|
||||
|
@ -19,6 +19,12 @@ use std::mem::size_of;
|
||||
use std::sync::Arc;
|
||||
use xz2::bufread::XzDecoder;
|
||||
|
||||
const ELF_MAGIC: &[u8] = &[
|
||||
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
const GZIP_MAGIC: &[u8] = &[0x1f, 0x8b];
|
||||
const XZ_MAGIC: &[u8] = &[0xfd, 0x37, 0x7a, 0x58];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ElfImageLoader {
|
||||
data: Arc<Vec<u8>>,
|
||||
@ -60,22 +66,40 @@ fn xen_note_value_as_u64(endian: AnyEndian, value: &[u8]) -> Option<u64> {
|
||||
}
|
||||
|
||||
impl ElfImageLoader {
|
||||
pub fn new(data: Vec<u8>) -> ElfImageLoader {
|
||||
ElfImageLoader {
|
||||
data: Arc::new(data),
|
||||
}
|
||||
pub fn new(data: Arc<Vec<u8>>) -> ElfImageLoader {
|
||||
ElfImageLoader { data }
|
||||
}
|
||||
|
||||
pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
let buff = BufReader::new(data);
|
||||
let image = ElfImageLoader::read_one_stream(&mut GzDecoder::new(buff))?;
|
||||
Ok(ElfImageLoader::new(image))
|
||||
Ok(ElfImageLoader::new(Arc::new(image)))
|
||||
}
|
||||
|
||||
pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
let buff = BufReader::new(data);
|
||||
let image = ElfImageLoader::read_one_stream(&mut XzDecoder::new(buff))?;
|
||||
Ok(ElfImageLoader::new(image))
|
||||
Ok(ElfImageLoader::new(Arc::new(image)))
|
||||
}
|
||||
|
||||
pub fn load(data: Arc<Vec<u8>>) -> Result<ElfImageLoader> {
|
||||
if data.len() >= 16 && find_iter(&data[0..15], ELF_MAGIC).next().is_some() {
|
||||
return Ok(ElfImageLoader::new(data));
|
||||
}
|
||||
|
||||
for start in find_iter(&data, GZIP_MAGIC) {
|
||||
if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
for start in find_iter(&data, XZ_MAGIC) {
|
||||
if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ElfCompressionUnknown)
|
||||
}
|
||||
|
||||
fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>> {
|
||||
@ -101,36 +125,11 @@ impl ElfImageLoader {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn load_file_gz(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
ElfImageLoader::load_gz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_xz(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
ElfImageLoader::load_xz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_kernel(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
for start in find_iter(data, &[0x1f, 0x8b]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
for start in find_iter(data, &[0xfd, 0x37, 0x7a, 0x58]) {
|
||||
if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
|
||||
return Ok(elf);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ElfCompressionUnknown)
|
||||
}
|
||||
|
||||
fn parse_sync(&self, hvm: bool) -> Result<BootImageInfo> {
|
||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||
let headers = elf.section_headers().ok_or(Error::ElfInvalidImage)?;
|
||||
let mut linux_notes: HashMap<u64, Vec<u8>> = HashMap::new();
|
||||
let headers = elf
|
||||
.section_headers()
|
||||
.ok_or(Error::ElfInvalidImage("section headers missing"))?;
|
||||
let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new();
|
||||
|
||||
for header in headers {
|
||||
@ -140,62 +139,55 @@ impl ElfImageLoader {
|
||||
|
||||
let notes = elf.section_data_as_notes(&header)?;
|
||||
for note in notes {
|
||||
if let Note::Unknown(note) = note {
|
||||
if note.name == "Linux" {
|
||||
linux_notes.insert(note.n_type, note.desc.to_vec());
|
||||
}
|
||||
let Note::Unknown(note) = note else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if note.name == "Xen" {
|
||||
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 });
|
||||
if note.name == "Xen" {
|
||||
for typ in XEN_ELFNOTE_TYPES {
|
||||
if typ.id != note.n_type {
|
||||
continue;
|
||||
}
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if linux_notes.is_empty() {
|
||||
return Err(Error::ElfInvalidImage);
|
||||
}
|
||||
|
||||
if xen_notes.is_empty() {
|
||||
return Err(Error::ElfXenSupportMissing);
|
||||
}
|
||||
|
||||
let paddr_offset = xen_notes
|
||||
.get(&XEN_ELFNOTE_PADDR_OFFSET)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.ok_or(Error::ElfXenNoteMissing("PADDR_OFFSET"))?
|
||||
.value;
|
||||
let virt_base = xen_notes
|
||||
.get(&XEN_ELFNOTE_VIRT_BASE)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.ok_or(Error::ElfXenNoteMissing("VIRT_BASE"))?
|
||||
.value;
|
||||
let entry = xen_notes
|
||||
.get(&XEN_ELFNOTE_ENTRY)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.ok_or(Error::ElfXenNoteMissing("ENTRY"))?
|
||||
.value;
|
||||
let virt_hypercall = xen_notes
|
||||
.get(&XEN_ELFNOTE_HYPERCALL_PAGE)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.ok_or(Error::ElfXenNoteMissing("HYPERCALL_PAGE"))?
|
||||
.value;
|
||||
let init_p2m = xen_notes
|
||||
.get(&XEN_ELFNOTE_INIT_P2M)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.ok_or(Error::ElfXenNoteMissing("INIT_P2M"))?
|
||||
.value;
|
||||
let mod_start_pfn = xen_notes
|
||||
.get(&XEN_ELFNOTE_MOD_START_PFN)
|
||||
.ok_or(Error::ElfInvalidImage)?
|
||||
.ok_or(Error::ElfXenNoteMissing("MOD_START_PFN"))?
|
||||
.value;
|
||||
|
||||
let phys32_entry = xen_notes.get(&XEN_ELFNOTE_PHYS32_ENTRY).map(|x| x.value);
|
||||
@ -203,7 +195,9 @@ impl ElfImageLoader {
|
||||
let mut start: u64 = u64::MAX;
|
||||
let mut end: u64 = 0;
|
||||
|
||||
let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
|
||||
let segments = elf
|
||||
.segments()
|
||||
.ok_or(Error::ElfInvalidImage("segments missing"))?;
|
||||
|
||||
for header in segments {
|
||||
if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 {
|
||||
@ -221,7 +215,9 @@ impl ElfImageLoader {
|
||||
}
|
||||
|
||||
if paddr_offset != u64::MAX && virt_base == u64::MAX {
|
||||
return Err(Error::ElfInvalidImage);
|
||||
return Err(Error::ElfInvalidImage(
|
||||
"paddr_offset specified, but virt_base is not specified",
|
||||
));
|
||||
}
|
||||
|
||||
let virt_offset = virt_base - paddr_offset;
|
||||
@ -247,8 +243,13 @@ impl ElfImageLoader {
|
||||
};
|
||||
Ok(image_info)
|
||||
}
|
||||
|
||||
pub fn into_elf_bytes(self) -> Arc<Vec<u8>> {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ElfNoteValue {
|
||||
value: u64,
|
||||
}
|
||||
@ -262,7 +263,9 @@ impl BootImageLoader for ElfImageLoader {
|
||||
|
||||
async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||
let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
|
||||
let segments = elf
|
||||
.segments()
|
||||
.ok_or(Error::ElfInvalidImage("segments missing"))?;
|
||||
|
||||
debug!(
|
||||
"load dst={:#x} segments={}",
|
||||
|
@ -28,10 +28,14 @@ pub enum Error {
|
||||
PopulatePhysmapFailed(usize, usize, usize),
|
||||
#[error("unknown elf compression method")]
|
||||
ElfCompressionUnknown,
|
||||
#[error("expected elf image format not found")]
|
||||
ElfInvalidImage,
|
||||
#[error("elf image format invalid: {0}")]
|
||||
ElfInvalidImage(&'static str),
|
||||
#[error("elf linux image not found")]
|
||||
ElfNotLinux,
|
||||
#[error("provided elf image does not contain xen support")]
|
||||
ElfXenSupportMissing,
|
||||
#[error("provided elf image does not contain xen note {0}")]
|
||||
ElfXenNoteMissing(&'static str),
|
||||
#[error("regex error: {0}")]
|
||||
RegexError(#[from] regex::Error),
|
||||
#[error("error: {0}")]
|
||||
|
@ -4,9 +4,104 @@ pub mod error;
|
||||
pub mod mem;
|
||||
pub mod sys;
|
||||
|
||||
use boot::{BootDomain, BootImageInfo, BootImageLoader, BootSetupPlatform};
|
||||
use domain::{PlatformKernelConfig, PlatformResourcesConfig};
|
||||
use elfloader::ElfImageLoader;
|
||||
use error::Result;
|
||||
use unsupported::UnsupportedPlatform;
|
||||
use xencall::{sys::CreateDomain, XenCall};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub mod domain;
|
||||
pub mod unsupported;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86pv;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ImageLoader {
|
||||
Elf(ElfImageLoader),
|
||||
}
|
||||
|
||||
impl ImageLoader {
|
||||
async fn parse(&self, hvm: bool) -> Result<BootImageInfo> {
|
||||
match self {
|
||||
ImageLoader::Elf(elf) => elf.parse(hvm).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
||||
match self {
|
||||
ImageLoader::Elf(elf) => elf.load(image_info, dst).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub enum RuntimePlatformType {
|
||||
Unsupported,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
Pv,
|
||||
}
|
||||
|
||||
impl RuntimePlatformType {
|
||||
pub fn create(&self) -> RuntimePlatform {
|
||||
match self {
|
||||
RuntimePlatformType::Unsupported => {
|
||||
RuntimePlatform::Unsupported(UnsupportedPlatform::new())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
RuntimePlatformType::Pv => RuntimePlatform::Pv(x86pv::X86PvPlatform::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum RuntimePlatform {
|
||||
Unsupported(UnsupportedPlatform),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
Pv(x86pv::X86PvPlatform),
|
||||
}
|
||||
|
||||
impl RuntimePlatform {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn initialize(
|
||||
&mut self,
|
||||
domid: u32,
|
||||
call: XenCall,
|
||||
image_loader: &ImageLoader,
|
||||
kernel: &PlatformKernelConfig,
|
||||
resources: &PlatformResourcesConfig,
|
||||
) -> Result<BootDomain> {
|
||||
match self {
|
||||
RuntimePlatform::Unsupported(unsupported) => {
|
||||
unsupported
|
||||
.initialize(domid, call, image_loader, kernel, resources)
|
||||
.await
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
RuntimePlatform::Pv(pv) => {
|
||||
pv.initialize(domid, call, image_loader, kernel, resources)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn boot(&mut self, domid: u32, call: XenCall, domain: &mut BootDomain) -> Result<()> {
|
||||
match self {
|
||||
RuntimePlatform::Unsupported(unsupported) => {
|
||||
unsupported.boot(domid, call, domain).await
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
RuntimePlatform::Pv(pv) => pv.boot(domid, call, domain).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_domain(&self, enable_iommu: bool) -> CreateDomain {
|
||||
match self {
|
||||
RuntimePlatform::Unsupported(unsupported) => unsupported.create_domain(enable_iommu),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
RuntimePlatform::Pv(pv) => pv.create_domain(enable_iommu),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ use log::{debug, trace};
|
||||
use nix::errno::Errno;
|
||||
use slice_copy::copy;
|
||||
use xencall::sys::{
|
||||
x8664VcpuGuestContext, CreateDomain, E820Entry, VcpuGuestContextAny, E820_MAX, E820_RAM,
|
||||
E820_UNUSABLE, MMUEXT_PIN_L4_TABLE, XEN_DOMCTL_CDF_IOMMU,
|
||||
x8664VcpuGuestContext, CreateDomain, VcpuGuestContextAny, MMUEXT_PIN_L4_TABLE,
|
||||
XEN_DOMCTL_CDF_IOMMU,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -282,52 +282,6 @@ impl X86PvPlatform {
|
||||
self.table.mappings[m] = map;
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
fn e820_sanitize(
|
||||
&self,
|
||||
mut source: Vec<E820Entry>,
|
||||
map_limit_kb: u64,
|
||||
) -> Result<Vec<E820Entry>> {
|
||||
let mut e820 = vec![E820Entry::default(); E820_MAX as usize];
|
||||
|
||||
for entry in &mut source {
|
||||
if entry.addr > 0x100000 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// entries under 1MB should be removed.
|
||||
entry.typ = 0;
|
||||
entry.size = 0;
|
||||
entry.addr = u64::MAX;
|
||||
}
|
||||
|
||||
let mut lowest = u64::MAX;
|
||||
let mut highest = 0;
|
||||
|
||||
for entry in &source {
|
||||
if entry.typ == E820_RAM || entry.typ == E820_UNUSABLE || entry.typ == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
lowest = if entry.addr < lowest {
|
||||
entry.addr
|
||||
} else {
|
||||
lowest
|
||||
};
|
||||
|
||||
highest = if entry.addr + entry.size > highest {
|
||||
entry.addr + entry.size
|
||||
} else {
|
||||
highest
|
||||
}
|
||||
}
|
||||
|
||||
e820[0].addr = 0;
|
||||
e820[0].size = map_limit_kb << 10;
|
||||
e820[0].typ = E820_RAM;
|
||||
|
||||
Ok(e820)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -690,8 +644,10 @@ impl BootSetupPlatform for X86PvPlatform {
|
||||
(*info).store_mfn = domain.phys.p2m[xenstore_segment.pfn as usize];
|
||||
(*info).console.mfn = domain.console_mfn;
|
||||
(*info).console.evtchn = domain.console_evtchn;
|
||||
(*info).mod_start = domain.initrd_segment.vstart;
|
||||
(*info).mod_len = domain.initrd_segment.size;
|
||||
if let Some(ref initrd_segment) = domain.initrd_segment {
|
||||
(*info).mod_start = initrd_segment.vstart;
|
||||
(*info).mod_len = initrd_segment.size;
|
||||
}
|
||||
for (i, c) in domain.cmdline.chars().enumerate() {
|
||||
(*info).cmdline[i] = c as c_char;
|
||||
}
|
||||
@ -715,12 +671,6 @@ impl BootSetupPlatform for X86PvPlatform {
|
||||
domain.phys.unmap(pg_pfn)?;
|
||||
domain.phys.unmap(p2m_segment.pfn)?;
|
||||
|
||||
let map = domain.call.get_memory_map(E820_MAX).await?;
|
||||
let mem_mb = domain.total_pages >> (20 - self.page_shift());
|
||||
let mem_kb = mem_mb * 1024;
|
||||
let e820 = self.e820_sanitize(map, mem_kb)?;
|
||||
domain.call.set_memory_map(domain.domid, e820).await?;
|
||||
|
||||
domain
|
||||
.call
|
||||
.mmuext(domain.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)
|
||||
|
Reference in New Issue
Block a user