mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-02 12:50:54 +00:00
implement initialization of start info for x86
This commit is contained in:
parent
684a7d1f62
commit
492a836213
@ -23,7 +23,8 @@ fn main() -> Result<(), XenClientError> {
|
||||
let image_loader = ElfImageLoader::load_file_kernel(kernel_image_path.as_str())?;
|
||||
let memctl = MemoryControl::new(&call);
|
||||
let mut boot = BootSetup::new(&call, &domctl, &memctl, domid);
|
||||
boot.initialize(&image_loader, 512 * 1024)?;
|
||||
let mut state = boot.initialize(&image_loader, 512 * 1024)?;
|
||||
boot.boot(&mut state, "debug")?;
|
||||
domctl.destroy_domain(domid)?;
|
||||
println!("domain destroyed: {}", domid);
|
||||
Ok(())
|
||||
|
@ -2,9 +2,15 @@ use crate::mem::PhysicalPages;
|
||||
use crate::sys::{
|
||||
SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, XEN_PAGE_SHIFT,
|
||||
};
|
||||
use crate::x86::{
|
||||
PageTable, PageTableMapping, StartInfo, MAX_GUEST_CMDLINE, X86_GUEST_MAGIC, X86_PAGE_SHIFT,
|
||||
X86_PAGE_SIZE, X86_PAGE_TABLE_MAX_MAPPINGS, X86_PGTABLE_LEVELS, X86_PGTABLE_LEVEL_SHIFT,
|
||||
X86_VIRT_MASK,
|
||||
};
|
||||
use crate::XenClientError;
|
||||
use libc::memset;
|
||||
use libc::{c_char, memset};
|
||||
use log::debug;
|
||||
use std::cmp::{max, min};
|
||||
use std::ffi::c_void;
|
||||
use std::slice;
|
||||
use xencall::domctl::DomainControl;
|
||||
@ -13,13 +19,14 @@ use xencall::XenCall;
|
||||
|
||||
pub trait BootImageLoader {
|
||||
fn parse(&self) -> Result<BootImageInfo, XenClientError>;
|
||||
fn load(&self, image_info: BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError>;
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError>;
|
||||
}
|
||||
|
||||
pub const XEN_UNSET_ADDR: u64 = -1i64 as u64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BootImageInfo {
|
||||
pub virt_base: u64,
|
||||
pub virt_kstart: u64,
|
||||
pub virt_kend: u64,
|
||||
pub virt_hypercall: u64,
|
||||
@ -34,11 +41,13 @@ pub struct BootSetup<'a> {
|
||||
domid: u32,
|
||||
virt_alloc_end: u64,
|
||||
pfn_alloc_end: u64,
|
||||
virt_pgtab_end: u64,
|
||||
total_pages: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DomainSegment {
|
||||
_vstart: u64,
|
||||
pub struct DomainSegment {
|
||||
vstart: u64,
|
||||
_vend: u64,
|
||||
pfn: u64,
|
||||
addr: u64,
|
||||
@ -53,6 +62,16 @@ struct VmemRange {
|
||||
_nid: u32,
|
||||
}
|
||||
|
||||
pub struct BootState {
|
||||
pub kernel_segment: DomainSegment,
|
||||
pub start_info_segment: DomainSegment,
|
||||
pub xenstore_segment: DomainSegment,
|
||||
pub console_segment: DomainSegment,
|
||||
pub boot_stack_segment: DomainSegment,
|
||||
pub page_table_segment: DomainSegment,
|
||||
pub page_table: PageTable,
|
||||
}
|
||||
|
||||
impl BootSetup<'_> {
|
||||
pub fn new<'a>(
|
||||
call: &'a XenCall,
|
||||
@ -67,6 +86,8 @@ impl BootSetup<'_> {
|
||||
domid,
|
||||
virt_alloc_end: 0,
|
||||
pfn_alloc_end: 0,
|
||||
virt_pgtab_end: 0,
|
||||
total_pages: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,6 +116,8 @@ impl BootSetup<'_> {
|
||||
));
|
||||
}
|
||||
|
||||
self.total_pages = total;
|
||||
|
||||
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];
|
||||
@ -184,30 +207,235 @@ impl BootSetup<'_> {
|
||||
&mut self,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
memkb: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<BootState, XenClientError> {
|
||||
debug!("BootSetup initialize memkb={:?}", memkb);
|
||||
let image_info = image_loader.parse()?;
|
||||
debug!("BootSetup initialize image_info={:?}", image_info);
|
||||
self.domctl.set_max_mem(self.domid, memkb)?;
|
||||
self.initialize_memory(memkb)?;
|
||||
|
||||
let image_info = image_loader.parse()?;
|
||||
let kernel_segment = self.load_kernel_segment(image_loader, &image_info)?;
|
||||
let start_info_segment = self.alloc_page()?;
|
||||
let xenstore_segment = self.alloc_page()?;
|
||||
let console_segment = self.alloc_page()?;
|
||||
let boot_stack_segment = self.alloc_page()?;
|
||||
let (page_table_segment, page_table) = self.alloc_page_tables(&image_info)?;
|
||||
Ok(BootState {
|
||||
kernel_segment,
|
||||
start_info_segment,
|
||||
xenstore_segment,
|
||||
console_segment,
|
||||
boot_stack_segment,
|
||||
page_table_segment,
|
||||
page_table,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> {
|
||||
self.setup_page_tables(state)?;
|
||||
self.setup_start_info(state, cmdline);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_page_tables(&mut self, state: &mut BootState) -> Result<(), XenClientError> {
|
||||
for lvl_idx in (0usize..3usize).rev() {
|
||||
for map_idx_1 in 0usize..state.page_table.mappings_count {
|
||||
let map1 = &state.page_table.mappings[map_idx_1];
|
||||
let from = map1.levels[lvl_idx].from;
|
||||
let to = map1.levels[lvl_idx].to;
|
||||
let pg = self.phys.pfn_to_ptr(map1.levels[lvl_idx].pfn, 0)? as *mut u64;
|
||||
for map_idx_2 in 0usize..state.page_table.mappings_count {
|
||||
let map2 = &state.page_table.mappings[map_idx_2];
|
||||
let lvl = if lvl_idx > 0 {
|
||||
&map2.levels[lvl_idx - 1]
|
||||
} else {
|
||||
&map2.area
|
||||
};
|
||||
|
||||
if lvl_idx > 0 && lvl.pgtables == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if lvl.from >= to || lvl.to <= from {
|
||||
continue;
|
||||
}
|
||||
|
||||
let p_s = (max(from, lvl.from) - from)
|
||||
>> (X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT);
|
||||
let p_e = (min(to, lvl.to) - from)
|
||||
>> (X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT);
|
||||
let mut pfn = (max(from, lvl.from) - from) >> ((X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT) + lvl.pfn);
|
||||
|
||||
for p in p_s..p_e + 1 {
|
||||
unsafe {
|
||||
*pg.add(p as usize) = self.phys.p2m[pfn as usize] << X86_PAGE_SHIFT;
|
||||
}
|
||||
pfn += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_start_info(&mut self, state: &BootState, cmdline: &str) {
|
||||
let info = state.start_info_segment.addr as *mut StartInfo;
|
||||
unsafe {
|
||||
for (i, c) in X86_GUEST_MAGIC.chars().enumerate() {
|
||||
(*info).magic[i] = c as c_char;
|
||||
}
|
||||
(*info).nr_pages = self.total_pages;
|
||||
(*info).shared_info = 0;
|
||||
(*info).pt_base = state.page_table_segment.vstart;
|
||||
(*info).nr_pt_frames = state.page_table.mappings[0].area.pgtables as u64;
|
||||
(*info).mfn_list = 0;
|
||||
(*info).first_p2m_pfn = 0;
|
||||
(*info).nr_p2m_frames = 0;
|
||||
(*info).flags = 0;
|
||||
(*info).store_evtchn = 0;
|
||||
(*info).store_mfn = 0;
|
||||
(*info).console.mfn = self.phys.p2m[state.console_segment.pfn as usize];
|
||||
(*info).console.evtchn = 0;
|
||||
(*info).mod_start = 0;
|
||||
(*info).mod_len = 0;
|
||||
for (i, c) in cmdline.chars().enumerate() {
|
||||
(*info).cmdline[i] = c as c_char;
|
||||
(*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0;
|
||||
}
|
||||
debug!("BootSetup setup_start_info={:?}", *info);
|
||||
}
|
||||
}
|
||||
|
||||
fn load_kernel_segment(
|
||||
&mut self,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
let kernel_segment = self.alloc_segment(image_info.virt_kend - image_info.virt_kstart)?;
|
||||
let kernel_segment_ptr = kernel_segment.addr as *mut u8;
|
||||
debug!(
|
||||
"BootSetup initialize kernel_segment ptr={:#x}",
|
||||
kernel_segment_ptr as u64
|
||||
);
|
||||
let slice =
|
||||
let kernel_segment_slice =
|
||||
unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) };
|
||||
image_loader.load(image_info, slice)?;
|
||||
image_loader.load(image_info, kernel_segment_slice)?;
|
||||
Ok(kernel_segment)
|
||||
}
|
||||
|
||||
fn count_page_tables(
|
||||
&mut self,
|
||||
table: &mut PageTable,
|
||||
from: u64,
|
||||
to: u64,
|
||||
pfn: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
if table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS {
|
||||
return Err(XenClientError::new("too many mappings"));
|
||||
}
|
||||
|
||||
let pfn_end = pfn + ((to - from) >> X86_PAGE_SHIFT);
|
||||
if pfn_end >= self.phys.p2m_size() {
|
||||
return Err(XenClientError::new("not enough memory for initial mapping"));
|
||||
}
|
||||
|
||||
for mapping in &table.mappings {
|
||||
if from < mapping.area.to && to > mapping.area.from {
|
||||
return Err(XenClientError::new("overlapping mappings"));
|
||||
}
|
||||
}
|
||||
|
||||
table.mappings[table.mappings_count] = PageTableMapping::default();
|
||||
let compare_table = table.clone();
|
||||
let map = &mut table.mappings[table.mappings_count];
|
||||
map.area.from = from & X86_VIRT_MASK;
|
||||
map.area.to = to & X86_VIRT_MASK;
|
||||
|
||||
for lvl_index in (0usize..3usize).rev() {
|
||||
let lvl = &mut map.levels[lvl_index];
|
||||
lvl.pfn = self.pfn_alloc_end + map.area.pgtables as u64;
|
||||
if lvl_index as u64 == X86_PGTABLE_LEVELS - 1 {
|
||||
if table.mappings_count == 0 {
|
||||
lvl.from = 0;
|
||||
lvl.to = X86_VIRT_MASK;
|
||||
lvl.pgtables = 1;
|
||||
map.area.pgtables += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let bits = X86_PAGE_SHIFT + (lvl_index + 1) as u64 * X86_PGTABLE_LEVEL_SHIFT;
|
||||
let mask = (1 << bits) - 1;
|
||||
lvl.from = map.area.from & !mask;
|
||||
lvl.to = map.area.to | mask;
|
||||
|
||||
for cmp in &compare_table.mappings {
|
||||
let cmp_lvl = &cmp.levels[lvl_index];
|
||||
if cmp_lvl.from == cmp_lvl.to {
|
||||
continue;
|
||||
}
|
||||
|
||||
if lvl.from >= cmp_lvl.from && lvl.to <= cmp_lvl.to {
|
||||
lvl.from = 0;
|
||||
lvl.to = 0;
|
||||
}
|
||||
|
||||
if lvl.from >= cmp_lvl.from && lvl.from <= cmp_lvl.to {
|
||||
lvl.from = cmp_lvl.to + 1;
|
||||
}
|
||||
|
||||
if lvl.to >= cmp_lvl.from && lvl.to <= cmp_lvl.to {
|
||||
lvl.to = cmp_lvl.from - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if lvl.from < lvl.to {
|
||||
lvl.pgtables = (((lvl.to - lvl.from) >> bits) + 1) as usize;
|
||||
}
|
||||
|
||||
map.area.pgtables += lvl.pgtables;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloc_page_tables(
|
||||
&mut self,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<(DomainSegment, PageTable), XenClientError> {
|
||||
let mut table = PageTable::default();
|
||||
let extra_pages = ((512 * 1024) / X86_PAGE_SIZE) + 1;
|
||||
let mut pages = extra_pages;
|
||||
|
||||
let mut try_virt_end: u64;
|
||||
loop {
|
||||
try_virt_end = (self.virt_alloc_end + pages * X86_PAGE_SIZE) | (1 << 22);
|
||||
self.count_page_tables(&mut table, image_info.virt_base, try_virt_end, 0)?;
|
||||
pages = table.mappings[0].area.pgtables as u64 + extra_pages;
|
||||
if self.virt_alloc_end + pages * X86_PAGE_SIZE <= try_virt_end + 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let segment: DomainSegment;
|
||||
{
|
||||
let map = &mut table.mappings[table.mappings_count];
|
||||
map.area.pfn = 0;
|
||||
table.mappings_count += 1;
|
||||
self.virt_pgtab_end = try_virt_end + 1;
|
||||
segment = self.alloc_segment(map.area.pgtables as u64 * X86_PAGE_SIZE)?;
|
||||
}
|
||||
debug!(
|
||||
"BootSetup alloc_page_tables table={:?} segment={:?}",
|
||||
table, segment
|
||||
);
|
||||
Ok((segment, table))
|
||||
}
|
||||
|
||||
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,
|
||||
vstart: start,
|
||||
_vend: 0,
|
||||
pfn: self.pfn_alloc_end,
|
||||
addr: 0,
|
||||
@ -225,4 +453,9 @@ impl BootSetup<'_> {
|
||||
debug!("BootSetup alloc_segment size={} ptr={:#x}", size, ptr);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_page(&mut self) -> Result<DomainSegment, XenClientError> {
|
||||
let page_size = 1u64 << XEN_PAGE_SHIFT;
|
||||
self.alloc_segment(page_size)
|
||||
}
|
||||
}
|
||||
|
@ -265,11 +265,7 @@ impl BootImageLoader for ElfImageLoader {
|
||||
));
|
||||
}
|
||||
|
||||
let _virt_base = if virt_base == XEN_UNSET_ADDR {
|
||||
0
|
||||
} else {
|
||||
virt_base
|
||||
};
|
||||
let virt_base = 0;
|
||||
|
||||
let _paddr_offset = if paddr_offset == XEN_UNSET_ADDR {
|
||||
0
|
||||
@ -287,6 +283,7 @@ impl BootImageLoader for ElfImageLoader {
|
||||
};
|
||||
|
||||
Ok(BootImageInfo {
|
||||
virt_base,
|
||||
virt_kstart,
|
||||
virt_kend,
|
||||
virt_hypercall,
|
||||
@ -295,7 +292,7 @@ impl BootImageLoader for ElfImageLoader {
|
||||
})
|
||||
}
|
||||
|
||||
fn load(&self, image_info: BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError> {
|
||||
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.",
|
||||
|
@ -3,6 +3,7 @@ pub mod create;
|
||||
pub mod elfloader;
|
||||
pub mod mem;
|
||||
pub mod sys;
|
||||
mod x86;
|
||||
|
||||
use crate::create::DomainConfig;
|
||||
use std::error::Error;
|
||||
|
@ -12,7 +12,7 @@ pub struct PhysicalPage {
|
||||
|
||||
pub struct PhysicalPages<'a> {
|
||||
domid: u32,
|
||||
p2m: Vec<u64>,
|
||||
pub(crate) p2m: Vec<u64>,
|
||||
call: &'a XenCall,
|
||||
pages: Vec<PhysicalPage>,
|
||||
}
|
||||
@ -31,6 +31,10 @@ impl PhysicalPages<'_> {
|
||||
self.p2m = p2m;
|
||||
}
|
||||
|
||||
pub fn p2m_size(&mut self) -> u64 {
|
||||
self.p2m.len() as u64
|
||||
}
|
||||
|
||||
pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64, XenClientError> {
|
||||
for page in &self.pages {
|
||||
if pfn >= page.pfn + page.count {
|
||||
|
73
xenclient/src/x86.rs
Normal file
73
xenclient/src/x86.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use libc::c_char;
|
||||
|
||||
pub const X86_PAGE_SHIFT: u64 = 12;
|
||||
pub const X86_PAGE_SIZE: u64 = 1 << X86_PAGE_SHIFT;
|
||||
pub const X86_VIRT_BITS: u64 = 48;
|
||||
pub const X86_VIRT_MASK: u64 = 1 << X86_VIRT_BITS;
|
||||
pub const X86_PGTABLE_LEVELS: u64 = 4;
|
||||
pub const X86_PGTABLE_LEVEL_SHIFT: u64 = 9;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default)]
|
||||
pub struct PageTableMappingLevel {
|
||||
pub from: u64,
|
||||
pub to: u64,
|
||||
pub pfn: u64,
|
||||
pub pgtables: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default)]
|
||||
pub struct PageTableMapping {
|
||||
pub area: PageTableMappingLevel,
|
||||
pub levels: [PageTableMappingLevel; X86_PGTABLE_LEVELS as usize],
|
||||
}
|
||||
|
||||
pub const X86_PAGE_TABLE_MAX_MAPPINGS: usize = 2;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default)]
|
||||
pub struct PageTable {
|
||||
pub mappings_count: usize,
|
||||
pub mappings: [PageTableMapping; X86_PAGE_TABLE_MAX_MAPPINGS],
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct StartInfoConsole {
|
||||
pub mfn: u64,
|
||||
pub evtchn: u32,
|
||||
}
|
||||
|
||||
pub const MAX_GUEST_CMDLINE: usize = 1024;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct StartInfo {
|
||||
pub magic: [c_char; 32],
|
||||
pub nr_pages: u64,
|
||||
pub shared_info: u64,
|
||||
pub flags: u32,
|
||||
pub store_mfn: u64,
|
||||
pub store_evtchn: u32,
|
||||
pub console: StartInfoConsole,
|
||||
pub pt_base: u64,
|
||||
pub nr_pt_frames: u64,
|
||||
pub mfn_list: u64,
|
||||
pub mod_start: u64,
|
||||
pub mod_len: u64,
|
||||
pub cmdline: [c_char; MAX_GUEST_CMDLINE],
|
||||
pub first_p2m_pfn: u64,
|
||||
pub nr_p2m_frames: u64,
|
||||
}
|
||||
|
||||
pub const X86_GUEST_MAGIC: &str = "xen-3.0-x86_64";
|
Loading…
Reference in New Issue
Block a user