2024-01-11 07:28:41 +00:00
|
|
|
use std::slice;
|
2024-01-11 00:07:57 +00:00
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
use log::debug;
|
|
|
|
use slice_copy::copy;
|
|
|
|
use xencall::{sys::CreateDomain, XenCall};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
error::{Error, Result},
|
|
|
|
mem::PhysicalPages,
|
|
|
|
sys::XEN_PAGE_SHIFT,
|
2024-12-14 23:16:10 +00:00
|
|
|
ImageLoader, PlatformKernelConfig, PlatformResourcesConfig,
|
2024-06-21 02:42:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone)]
|
2024-01-12 01:23:09 +00:00
|
|
|
pub struct DomainSegment {
|
2024-06-21 02:42:45 +00:00
|
|
|
pub vstart: u64,
|
|
|
|
pub vend: u64,
|
2024-01-17 13:22:47 +00:00
|
|
|
pub pfn: u64,
|
2024-06-21 02:42:45 +00:00
|
|
|
pub addr: u64,
|
|
|
|
pub size: u64,
|
|
|
|
pub pages: u64,
|
2024-01-11 00:07:57 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
pub struct BootDomain {
|
|
|
|
pub domid: u32,
|
|
|
|
pub call: XenCall,
|
|
|
|
pub page_size: u64,
|
|
|
|
pub virt_alloc_end: u64,
|
|
|
|
pub pfn_alloc_end: u64,
|
|
|
|
pub virt_pgtab_end: u64,
|
|
|
|
pub total_pages: u64,
|
|
|
|
pub target_pages: u64,
|
|
|
|
pub max_vcpus: u32,
|
2024-01-12 05:10:59 +00:00
|
|
|
pub image_info: BootImageInfo,
|
2024-06-21 02:42:45 +00:00
|
|
|
pub phys: PhysicalPages,
|
2024-01-17 09:41:17 +00:00
|
|
|
pub store_evtchn: u32,
|
2024-06-21 02:42:45 +00:00
|
|
|
pub store_mfn: u64,
|
2024-12-14 23:16:10 +00:00
|
|
|
pub initrd_segment: Option<DomainSegment>,
|
2024-06-21 17:38:19 +00:00
|
|
|
pub console_evtchn: u32,
|
|
|
|
pub console_mfn: u64,
|
2024-06-21 02:42:45 +00:00
|
|
|
pub cmdline: String,
|
2024-01-12 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
impl BootDomain {
|
|
|
|
pub async fn alloc_module(&mut self, buffer: &[u8]) -> Result<DomainSegment> {
|
|
|
|
let segment = self.alloc_segment(0, buffer.len() as u64).await?;
|
|
|
|
let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) };
|
|
|
|
copy(slice, buffer);
|
|
|
|
Ok(segment)
|
2024-01-17 01:57:19 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
pub async fn alloc_segment(&mut self, start: u64, size: u64) -> Result<DomainSegment> {
|
2024-04-02 00:56:18 +00:00
|
|
|
debug!("alloc_segment {:#x} {:#x}", start, size);
|
2024-01-12 07:54:44 +00:00
|
|
|
if start > 0 {
|
2024-06-21 02:42:45 +00:00
|
|
|
self.alloc_padding_pages(start)?;
|
2024-01-12 07:54:44 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 00:56:18 +00:00
|
|
|
let local_page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32;
|
2024-12-14 22:57:15 +00:00
|
|
|
let pages = size.div_ceil(local_page_size as u64);
|
2024-01-12 07:54:44 +00:00
|
|
|
let start = self.virt_alloc_end;
|
|
|
|
|
2024-01-11 00:07:57 +00:00
|
|
|
let mut segment = DomainSegment {
|
2024-01-12 01:23:09 +00:00
|
|
|
vstart: start,
|
2024-01-17 14:27:12 +00:00
|
|
|
vend: 0,
|
2024-01-11 00:07:57 +00:00
|
|
|
pfn: self.pfn_alloc_end,
|
2024-01-11 07:28:41 +00:00
|
|
|
addr: 0,
|
|
|
|
size,
|
2024-01-17 07:07:34 +00:00
|
|
|
pages,
|
2024-01-11 00:07:57 +00:00
|
|
|
};
|
2024-01-12 07:54:44 +00:00
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
self.chk_alloc_pages(pages)?;
|
2024-01-12 07:54:44 +00:00
|
|
|
|
2024-04-02 00:56:18 +00:00
|
|
|
let ptr = self.phys.pfn_to_ptr(segment.pfn, pages).await?;
|
2024-01-11 07:28:41 +00:00
|
|
|
segment.addr = ptr;
|
2024-01-17 01:57:19 +00:00
|
|
|
let slice = unsafe {
|
2024-04-02 00:56:18 +00:00
|
|
|
slice::from_raw_parts_mut(ptr as *mut u8, (pages * local_page_size as u64) as usize)
|
2024-01-17 01:57:19 +00:00
|
|
|
};
|
2024-01-15 00:00:44 +00:00
|
|
|
slice.fill(0);
|
2024-01-17 14:27:12 +00:00
|
|
|
segment.vend = self.virt_alloc_end;
|
2024-01-12 07:54:44 +00:00
|
|
|
debug!(
|
2024-02-25 05:38:23 +00:00
|
|
|
"alloc_segment {:#x} -> {:#x} (pfn {:#x} + {:#x} pages)",
|
2024-01-17 14:27:12 +00:00
|
|
|
start, segment.vend, segment.pfn, pages
|
2024-01-12 07:54:44 +00:00
|
|
|
);
|
2024-01-11 00:07:57 +00:00
|
|
|
Ok(segment)
|
|
|
|
}
|
2024-01-12 01:23:09 +00:00
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
pub fn alloc_padding_pages(&mut self, boundary: u64) -> Result<()> {
|
|
|
|
if (boundary & (self.page_size - 1)) != 0 {
|
|
|
|
return Err(Error::MemorySetupFailed("boundary is incorrect"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if boundary < self.virt_alloc_end {
|
|
|
|
return Err(Error::MemorySetupFailed("boundary is below allocation end"));
|
|
|
|
}
|
|
|
|
let pages = (boundary - self.virt_alloc_end) / self.page_size;
|
|
|
|
self.chk_alloc_pages(pages)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn chk_alloc_pages(&mut self, pages: u64) -> Result<()> {
|
|
|
|
if pages > self.total_pages
|
|
|
|
|| self.pfn_alloc_end > self.total_pages
|
|
|
|
|| pages > self.total_pages - self.pfn_alloc_end
|
|
|
|
{
|
|
|
|
return Err(Error::MemorySetupFailed("no more pages left"));
|
|
|
|
}
|
|
|
|
|
|
|
|
self.pfn_alloc_end += pages;
|
|
|
|
self.virt_alloc_end += pages * self.page_size;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn alloc_page(&mut self) -> Result<DomainSegment> {
|
2024-01-12 10:12:29 +00:00
|
|
|
let start = self.virt_alloc_end;
|
|
|
|
let pfn = self.pfn_alloc_end;
|
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
self.chk_alloc_pages(1)?;
|
2024-02-25 05:38:23 +00:00
|
|
|
debug!("alloc_page {:#x} (pfn {:#x})", start, pfn);
|
2024-01-12 10:12:29 +00:00
|
|
|
Ok(DomainSegment {
|
|
|
|
vstart: start,
|
2024-06-21 02:42:45 +00:00
|
|
|
vend: (start + self.page_size) - 1,
|
2024-01-12 10:12:29 +00:00
|
|
|
pfn,
|
|
|
|
addr: 0,
|
|
|
|
size: 0,
|
2024-01-17 07:07:34 +00:00
|
|
|
pages: 1,
|
2024-01-12 10:12:29 +00:00
|
|
|
})
|
2024-01-12 07:54:44 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
pub fn round_up(addr: u64, mask: u64) -> u64 {
|
|
|
|
addr | mask
|
2024-01-17 01:57:19 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
pub fn bits_to_mask(bits: u64) -> u64 {
|
|
|
|
(1 << bits) - 1
|
|
|
|
}
|
|
|
|
}
|
2024-01-12 07:54:44 +00:00
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
#[async_trait::async_trait]
|
|
|
|
pub trait BootSetupPlatform {
|
|
|
|
fn create_domain(&self, enable_iommu: bool) -> CreateDomain;
|
|
|
|
fn page_size(&self) -> u64;
|
|
|
|
fn page_shift(&self) -> u64;
|
|
|
|
fn needs_early_kernel(&self) -> bool;
|
|
|
|
fn hvm(&self) -> bool;
|
|
|
|
|
|
|
|
async fn initialize_early(&mut self, domain: &mut BootDomain) -> Result<()>;
|
|
|
|
|
|
|
|
async fn initialize_memory(&mut self, domain: &mut BootDomain) -> Result<()>;
|
|
|
|
|
|
|
|
async fn alloc_page_tables(&mut self, domain: &mut BootDomain)
|
|
|
|
-> Result<Option<DomainSegment>>;
|
|
|
|
|
|
|
|
async fn alloc_p2m_segment(&mut self, domain: &mut BootDomain)
|
|
|
|
-> Result<Option<DomainSegment>>;
|
|
|
|
|
|
|
|
async fn alloc_magic_pages(&mut self, domain: &mut BootDomain) -> Result<()>;
|
2024-01-12 07:54:44 +00:00
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
async fn setup_page_tables(&mut self, domain: &mut BootDomain) -> Result<()>;
|
|
|
|
|
|
|
|
async fn setup_shared_info(
|
2024-06-21 02:42:45 +00:00
|
|
|
&mut self,
|
2024-12-14 23:16:10 +00:00
|
|
|
domain: &mut BootDomain,
|
|
|
|
shared_info_frame: u64,
|
|
|
|
) -> Result<()>;
|
2024-06-21 02:42:45 +00:00
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
async fn setup_start_info(
|
|
|
|
&mut self,
|
|
|
|
domain: &mut BootDomain,
|
|
|
|
shared_info_frame: u64,
|
|
|
|
) -> Result<()>;
|
|
|
|
|
|
|
|
async fn bootlate(&mut self, domain: &mut BootDomain) -> Result<()>;
|
2024-06-21 02:42:45 +00:00
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
async fn gnttab_seed(&mut self, domain: &mut BootDomain) -> Result<()>;
|
|
|
|
|
|
|
|
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?)
|
2024-06-21 02:42:45 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
let mut kernel_segment = if self.needs_early_kernel() {
|
|
|
|
Some(self.load_kernel_segment(image_loader, domain).await?)
|
2024-06-21 02:42:45 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
self.initialize_memory(domain).await?;
|
2024-06-21 02:42:45 +00:00
|
|
|
domain.virt_alloc_end = domain.image_info.virt_base;
|
|
|
|
|
|
|
|
if kernel_segment.is_none() {
|
2024-12-14 23:16:10 +00:00
|
|
|
kernel_segment = Some(self.load_kernel_segment(image_loader, domain).await?);
|
2024-01-12 07:54:44 +00:00
|
|
|
}
|
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
if domain.image_info.unmapped_initrd && kernel.initrd.is_some() {
|
|
|
|
initrd_segment = Some(domain.alloc_module(kernel.initrd.as_ref().unwrap()).await?);
|
2024-06-21 02:42:45 +00:00
|
|
|
}
|
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
domain.initrd_segment = initrd_segment;
|
|
|
|
self.alloc_magic_pages(domain).await?;
|
|
|
|
domain.store_evtchn = call.evtchn_alloc_unbound(domid, 0).await?;
|
2024-06-21 02:42:45 +00:00
|
|
|
let _kernel_segment =
|
|
|
|
kernel_segment.ok_or(Error::MemorySetupFailed("kernel_segment missing"))?;
|
2024-12-14 23:16:10 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2024-06-21 02:42:45 +00:00
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
#[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)
|
|
|
|
}
|
|
|
|
}
|
2024-06-21 02:42:45 +00:00
|
|
|
}
|
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
async fn boot_internal(
|
|
|
|
&mut self,
|
|
|
|
call: XenCall,
|
|
|
|
domid: u32,
|
|
|
|
domain: &mut BootDomain,
|
|
|
|
) -> Result<()> {
|
|
|
|
let domain_info = call.get_domain_info(domid).await?;
|
2024-06-21 02:42:45 +00:00
|
|
|
let shared_info_frame = domain_info.shared_info_frame;
|
2024-12-14 23:16:10 +00:00
|
|
|
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?;
|
2024-06-21 02:42:45 +00:00
|
|
|
domain.phys.unmap_all()?;
|
2024-01-12 07:54:44 +00:00
|
|
|
Ok(())
|
2024-01-12 01:23:09 +00:00
|
|
|
}
|
2024-06-21 02:42:45 +00:00
|
|
|
|
2024-12-14 23:16:10 +00:00
|
|
|
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> {
|
2024-06-21 02:42:45 +00:00
|
|
|
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) };
|
2024-12-14 23:16:10 +00:00
|
|
|
image_loader
|
2024-06-21 02:42:45 +00:00
|
|
|
.load(&domain.image_info, kernel_segment_slice)
|
|
|
|
.await?;
|
|
|
|
Ok(kernel_segment)
|
|
|
|
}
|
2024-01-10 06:39:32 +00:00
|
|
|
}
|
2024-01-17 14:27:12 +00:00
|
|
|
|
2024-06-21 02:42:45 +00:00
|
|
|
#[async_trait::async_trait]
|
|
|
|
pub trait BootImageLoader {
|
|
|
|
async fn parse(&self, hvm: bool) -> Result<BootImageInfo>;
|
|
|
|
async fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct BootImageInfo {
|
|
|
|
pub start: u64,
|
|
|
|
pub virt_base: u64,
|
|
|
|
pub virt_kstart: u64,
|
|
|
|
pub virt_kend: u64,
|
|
|
|
pub virt_hypercall: u64,
|
|
|
|
pub virt_entry: u64,
|
|
|
|
pub virt_p2m_base: u64,
|
|
|
|
pub unmapped_initrd: bool,
|
2024-01-17 14:27:12 +00:00
|
|
|
}
|