From ee32cbee16edb3766730c2130a1893091d1e89d5 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Wed, 17 Jan 2024 06:27:12 -0800 Subject: [PATCH] rearrange code to move most x86 specifics to the x86 module --- xenclient/src/boot.rs | 657 +++++++++--------------------------------- xenclient/src/lib.rs | 50 +--- xenclient/src/x86.rs | 522 +++++++++++++++++++++++++++++++++ 3 files changed, 672 insertions(+), 557 deletions(-) diff --git a/xenclient/src/boot.rs b/xenclient/src/boot.rs index 3caea19..8dd9bf2 100644 --- a/xenclient/src/boot.rs +++ b/xenclient/src/boot.rs @@ -1,22 +1,12 @@ use crate::mem::PhysicalPages; -use crate::sys::{ - GrantEntry, SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, VGCF_IN_KERNEL, - VGCF_ONLINE, XEN_PAGE_SHIFT, -}; -use crate::x86::{ - PageTable, PageTableMapping, SharedInfo, 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::sys::{GrantEntry, XEN_PAGE_SHIFT}; use crate::XenClientError; -use libc::{c_char, munmap}; -use log::{debug, trace}; +use libc::munmap; +use log::debug; use slice_copy::copy; -use std::cmp::{max, min}; + use std::ffi::c_void; -use std::mem::size_of; use std::slice; -use xencall::sys::{VcpuGuestContext, MMUEXT_PIN_L4_TABLE}; use xencall::XenCall; pub trait BootImageLoader { @@ -39,31 +29,23 @@ pub struct BootImageInfo { } pub struct BootSetup<'a> { - call: &'a XenCall, + pub(crate) call: &'a XenCall, pub phys: PhysicalPages<'a>, - domid: u32, - virt_alloc_end: u64, - pfn_alloc_end: u64, - virt_pgtab_end: u64, - total_pages: u64, + pub(crate) domid: u32, + pub(crate) virt_alloc_end: u64, + pub(crate) pfn_alloc_end: u64, + pub(crate) virt_pgtab_end: u64, + pub(crate) total_pages: u64, } #[derive(Debug)] pub struct DomainSegment { - vstart: u64, - _vend: u64, + pub(crate) vstart: u64, + vend: u64, pub pfn: u64, - addr: u64, - size: u64, - pages: u64, -} - -#[derive(Debug)] -struct VmemRange { - start: u64, - end: u64, - _flags: u32, - _nid: u32, + pub(crate) addr: u64, + pub(crate) size: u64, + pub(crate) pages: u64, } #[derive(Debug)] @@ -75,7 +57,6 @@ pub struct BootState { pub boot_stack_segment: DomainSegment, pub p2m_segment: DomainSegment, pub page_table_segment: DomainSegment, - pub page_table: PageTable, pub image_info: BootImageInfo, pub shared_info_frame: u64, pub initrd_segment: DomainSegment, @@ -96,133 +77,19 @@ impl BootSetup<'_> { } } - fn initialize_memory(&mut self, total_pages: u64) -> Result<(), XenClientError> { + fn initialize_memory( + &mut self, + arch: &mut dyn ArchBootSetup, + total_pages: u64, + ) -> Result<(), XenClientError> { self.call.set_address_size(self.domid, 64)?; - - let mut vmemranges: Vec = Vec::new(); - let stub = VmemRange { - start: 0, - end: total_pages << XEN_PAGE_SHIFT, - _flags: 0, - _nid: 0, - }; - vmemranges.push(stub); - debug!("BootSetup initialize_memory vmemranges: {:?}", vmemranges); - - 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 != total_pages { - return Err(XenClientError::new( - "page count mismatch while calculating pages", - )); - } - - debug!("BootSetup initialize_memory total_pages={}", total); - self.total_pages = total; - - let mut p2m = vec![u64::MAX; p2m_size as usize]; - for range in &vmemranges { - let mut extents_init = 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; - let mut pfn_base_idx: u64 = pfn_base; - while super_pages > 0 { - let count = super_pages.min(SUPERPAGE_BATCH_SIZE); - super_pages -= count; - - let mut j: usize = 0; - let mut pfn: u64 = pfn_base_idx; - loop { - if pfn >= pfn_base_idx + (count << SUPERPAGE_2MB_SHIFT) { - break; - } - extents_init[j] = p2m[pfn as usize]; - pfn += SUPERPAGE_2MB_NR_PFNS; - j += 1; - } - - let extents_init_slice = extents_init.as_slice(); - let extents = self.call.populate_physmap( - self.domid, - count, - SUPERPAGE_2MB_SHIFT as u32, - 0, - &extents_init_slice[0usize..count as usize], - )?; - - pfn = pfn_base_idx; - for mfn in extents { - for k in 0..SUPERPAGE_2MB_NR_PFNS { - p2m[pfn as usize] = mfn + k; - pfn += 1; - } - } - pfn_base_idx = pfn; - } - - let mut j = pfn_base_idx - pfn_base; - loop { - if j >= pages { - break; - } - - let allocsz = (1024 * 1024).min(pages - j); - let p2m_idx = (pfn_base + j) as usize; - let p2m_end_idx = p2m_idx + allocsz as usize; - let input_extent_starts = &p2m[p2m_idx..p2m_end_idx]; - let result = - self.call - .populate_physmap(self.domid, allocsz, 0, 0, input_extent_starts)?; - - if result.len() != allocsz as usize { - return Err(XenClientError::new( - format!( - "failed to populate physmap: wanted={} received={} input_extents={}", - allocsz, - result.len(), - input_extent_starts.len() - ) - .as_str(), - )); - } - - for (i, item) in result.iter().enumerate() { - let p = (pfn_base + j + i as u64) as usize; - let m = *item; - p2m[p] = m; - } - j += allocsz; - } - } - - self.phys.load_p2m(p2m); - Ok(()) - } - - fn setup_hypercall_page(&mut self, image_info: &BootImageInfo) -> Result<(), XenClientError> { - if image_info.virt_hypercall == XEN_UNSET_ADDR { - return Ok(()); - } - - let pfn = (image_info.virt_hypercall - image_info.virt_base) >> X86_PAGE_SHIFT; - let mfn = self.phys.p2m[pfn as usize]; - self.call.hypercall_init(self.domid, mfn)?; + arch.meminit(self, total_pages)?; Ok(()) } pub fn initialize( &mut self, + arch: &mut dyn ArchBootSetup, image_loader: &dyn BootImageLoader, initrd: &[u8], max_vcpus: u32, @@ -233,43 +100,42 @@ impl BootSetup<'_> { max_vcpus, mem_mb ); - let total_pages = mem_mb << (20 - X86_PAGE_SHIFT); - self.initialize_memory(total_pages)?; + let total_pages = mem_mb << (20 - arch.page_shift()); + self.initialize_memory(arch, total_pages)?; let image_info = image_loader.parse()?; debug!("BootSetup initialize image_info={:?}", image_info); self.virt_alloc_end = image_info.virt_base; - let kernel_segment = self.load_kernel_segment(image_loader, &image_info)?; + let kernel_segment = self.load_kernel_segment(arch, image_loader, &image_info)?; let mut p2m_segment: Option = None; - let mut page_table = PageTable::default(); if image_info.virt_p2m_base >= image_info.virt_base - || (image_info.virt_p2m_base & ((1 << X86_PAGE_SHIFT) - 1)) != 0 + || (image_info.virt_p2m_base & ((1 << arch.page_shift()) - 1)) != 0 { - p2m_segment = Some(self.alloc_p2m_segment(&mut page_table, &image_info)?); + p2m_segment = Some(arch.alloc_p2m_segment(self, &image_info)?); } - let start_info_segment = self.alloc_page()?; - let xenstore_segment = self.alloc_page()?; - let console_segment = self.alloc_page()?; - let page_table_segment = self.alloc_page_tables(&mut page_table, &image_info)?; - let boot_stack_segment = self.alloc_page()?; + let start_info_segment = self.alloc_page(arch)?; + let xenstore_segment = self.alloc_page(arch)?; + let console_segment = self.alloc_page(arch)?; + let page_table_segment = arch.alloc_page_tables(self, &image_info)?; + let boot_stack_segment = self.alloc_page(arch)?; if self.virt_pgtab_end > 0 { - self.alloc_padding_pages(self.virt_pgtab_end)?; + self.alloc_padding_pages(arch, self.virt_pgtab_end)?; } let mut initrd_segment: Option = None; if !image_info.unmapped_initrd { - initrd_segment = Some(self.alloc_module(initrd)?); + initrd_segment = Some(self.alloc_module(arch, initrd)?); } if p2m_segment.is_none() { - let mut segment = self.alloc_p2m_segment(&mut page_table, &image_info)?; + let mut segment = arch.alloc_p2m_segment(self, &image_info)?; segment.vstart = image_info.virt_p2m_base; p2m_segment = Some(segment); } let p2m_segment = p2m_segment.unwrap(); if image_info.unmapped_initrd { - initrd_segment = Some(self.alloc_module(initrd)?); + initrd_segment = Some(self.alloc_module(arch, initrd)?); } let initrd_segment = initrd_segment.unwrap(); @@ -283,7 +149,6 @@ impl BootSetup<'_> { boot_stack_segment, p2m_segment, page_table_segment, - page_table, image_info, initrd_segment, store_evtchn, @@ -294,48 +159,21 @@ impl BootSetup<'_> { Ok(state) } - pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> { + pub fn boot( + &mut self, + arch: &mut dyn ArchBootSetup, + state: &mut BootState, + cmdline: &str, + ) -> Result<(), XenClientError> { let domain_info = self.call.get_domain_info(self.domid)?; let shared_info_frame = domain_info.shared_info_frame; state.shared_info_frame = shared_info_frame; - self.setup_page_tables(state)?; - self.setup_start_info(state, cmdline)?; - self.setup_hypercall_page(&state.image_info)?; - - let pg_pfn = state.page_table_segment.pfn; - self.phys.unmap(pg_pfn)?; - self.phys.unmap(state.p2m_segment.pfn)?; - let pg_mfn = self.phys.p2m[pg_pfn as usize]; - self.call - .mmuext(self.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?; - self.setup_shared_info(state.shared_info_frame)?; - - let mut vcpu = VcpuGuestContext::default(); - vcpu.user_regs.rip = state.image_info.virt_entry; - vcpu.user_regs.rsp = - state.image_info.virt_base + (state.boot_stack_segment.pfn + 1) * X86_PAGE_SIZE; - vcpu.user_regs.rsi = - state.image_info.virt_base + (state.start_info_segment.pfn) * X86_PAGE_SIZE; - vcpu.user_regs.rflags = 1 << 9; - vcpu.debugreg[6] = 0xffff0ff0; - vcpu.debugreg[7] = 0x00000400; - vcpu.flags = VGCF_IN_KERNEL | VGCF_ONLINE; - let cr3_pfn = pg_mfn; - debug!( - "cr3: pfn {:#x} mfn {:#x}", - state.page_table_segment.pfn, cr3_pfn - ); - vcpu.ctrlreg[3] = cr3_pfn << 12; - vcpu.user_regs.ds = 0x0; - vcpu.user_regs.es = 0x0; - vcpu.user_regs.fs = 0x0; - vcpu.user_regs.gs = 0x0; - vcpu.user_regs.ss = 0xe02b; - vcpu.user_regs.cs = 0xe033; - vcpu.kernel_ss = vcpu.user_regs.ss as u64; - vcpu.kernel_sp = vcpu.user_regs.rsp; - debug!("vcpu context: {:?}", vcpu); - self.call.set_vcpu_context(self.domid, 0, &vcpu)?; + arch.setup_page_tables(self, state)?; + arch.setup_start_info(self, state, cmdline)?; + arch.setup_hypercall_page(self, &state.image_info)?; + arch.bootlate(self, state)?; + arch.setup_shared_info(self, state.shared_info_frame)?; + arch.vcpu(self, state)?; self.phys.unmap_all()?; self.gnttab_seed(state)?; Ok(()) @@ -365,161 +203,14 @@ impl BootSetup<'_> { Ok(()) } - fn setup_page_tables(&mut self, state: &mut BootState) -> Result<(), XenClientError> { - let p2m_guest = unsafe { - slice::from_raw_parts_mut( - state.p2m_segment.addr as *mut u64, - self.phys.p2m_size() as usize, - ) - }; - copy(p2m_guest, &self.phys.p2m); - - for l in (0usize..X86_PGTABLE_LEVELS as usize).rev() { - for m1 in 0usize..state.page_table.mappings_count { - let map1 = &state.page_table.mappings[m1]; - let from = map1.levels[l].from; - let to = map1.levels[l].to; - let pg_ptr = self.phys.pfn_to_ptr(map1.levels[l].pfn, 0)? as *mut u64; - for m2 in 0usize..state.page_table.mappings_count { - let map2 = &state.page_table.mappings[m2]; - let lvl = if l > 0 { - &map2.levels[l - 1] - } else { - &map2.area - }; - - if l > 0 && lvl.pgtables == 0 { - continue; - } - - if lvl.from >= to || lvl.to <= from { - continue; - } - - let p_s = (max(from, lvl.from) - from) - >> (X86_PAGE_SHIFT + l as u64 * X86_PGTABLE_LEVEL_SHIFT); - let p_e = (min(to, lvl.to) - from) - >> (X86_PAGE_SHIFT + l as u64 * X86_PGTABLE_LEVEL_SHIFT); - let rhs = X86_PAGE_SHIFT as usize + l * X86_PGTABLE_LEVEL_SHIFT as usize; - let mut pfn = ((max(from, lvl.from) - lvl.from) >> rhs) + lvl.pfn; - - debug!( - "BootSetup setup_page_tables lvl={} map_1={} map_2={} pfn={:#x} p_s={:#x} p_e={:#x}", - l, m1, m2, pfn, p_s, p_e - ); - - let pg = unsafe { slice::from_raw_parts_mut(pg_ptr, (p_e + 1) as usize) }; - for p in p_s..p_e + 1 { - let prot = self.get_pg_prot(l, pfn, &state.page_table); - let pfn_paddr = self.phys.p2m[pfn as usize] << X86_PAGE_SHIFT; - let value = pfn_paddr | prot; - pg[p as usize] = value; - pfn += 1; - } - } - } - } - Ok(()) - } - - const PAGE_PRESENT: u64 = 0x001; - const PAGE_RW: u64 = 0x002; - const PAGE_USER: u64 = 0x004; - const PAGE_ACCESSED: u64 = 0x020; - const PAGE_DIRTY: u64 = 0x040; - fn get_pg_prot(&mut self, l: usize, pfn: u64, table: &PageTable) -> u64 { - let prot = [ - BootSetup::PAGE_PRESENT | BootSetup::PAGE_RW | BootSetup::PAGE_ACCESSED, - BootSetup::PAGE_PRESENT - | BootSetup::PAGE_RW - | BootSetup::PAGE_ACCESSED - | BootSetup::PAGE_DIRTY - | BootSetup::PAGE_USER, - BootSetup::PAGE_PRESENT - | BootSetup::PAGE_RW - | BootSetup::PAGE_ACCESSED - | BootSetup::PAGE_DIRTY - | BootSetup::PAGE_USER, - BootSetup::PAGE_PRESENT - | BootSetup::PAGE_RW - | BootSetup::PAGE_ACCESSED - | BootSetup::PAGE_DIRTY - | BootSetup::PAGE_USER, - ]; - - let prot = prot[l]; - if l > 0 { - return prot; - } - - for m in 0..table.mappings_count { - let map = &table.mappings[m]; - let pfn_s = map.levels[(X86_PGTABLE_LEVELS - 1) as usize].pfn; - let pfn_e = map.area.pgtables as u64 + pfn_s; - if pfn >= pfn_s && pfn < pfn_e { - return prot & !BootSetup::PAGE_RW; - } - } - prot - } - - fn setup_start_info(&mut self, state: &BootState, cmdline: &str) -> Result<(), XenClientError> { - let ptr = self.phys.pfn_to_ptr(state.start_info_segment.pfn, 1)?; - let byte_slice = - unsafe { slice::from_raw_parts_mut(ptr as *mut u8, X86_PAGE_SIZE as usize) }; - byte_slice.fill(0); - let info = ptr as *mut StartInfo; - unsafe { - for (i, c) in X86_GUEST_MAGIC.chars().enumerate() { - (*info).magic[i] = c as c_char; - } - (*info).magic[X86_GUEST_MAGIC.len()] = 0 as c_char; - (*info).nr_pages = self.total_pages; - (*info).shared_info = state.shared_info_frame << X86_PAGE_SHIFT; - (*info).pt_base = state.page_table_segment.vstart; - (*info).nr_pt_frames = state.page_table.mappings[0].area.pgtables as u64; - (*info).mfn_list = state.p2m_segment.vstart; - (*info).first_p2m_pfn = state.p2m_segment.pfn; - (*info).nr_p2m_frames = state.p2m_segment.pages; - (*info).flags = 0; - (*info).store_evtchn = state.store_evtchn; - (*info).store_mfn = self.phys.p2m[state.xenstore_segment.pfn as usize]; - (*info).console.mfn = self.phys.p2m[state.console_segment.pfn as usize]; - (*info).console.evtchn = state.console_evtchn; - (*info).mod_start = state.initrd_segment.vstart; - (*info).mod_len = state.initrd_segment.size; - for (i, c) in cmdline.chars().enumerate() { - (*info).cmdline[i] = c as c_char; - } - (*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0; - trace!("BootSetup setup_start_info start_info={:?}", *info); - } - Ok(()) - } - - fn setup_shared_info(&mut self, shared_info_frame: u64) -> Result<(), XenClientError> { - let info = self - .phys - .map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)? - as *mut SharedInfo; - unsafe { - let size = size_of::(); - let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size); - info_as_buff.fill(0); - for i in 0..32 { - (*info).vcpu_info[i].evtchn_upcall_mask = 1; - } - trace!("BootSetup setup_shared_info shared_info={:?}", *info); - } - Ok(()) - } - fn load_kernel_segment( &mut self, + arch: &mut dyn ArchBootSetup, image_loader: &dyn BootImageLoader, image_info: &BootImageInfo, ) -> Result { let kernel_segment = self.alloc_segment( + arch, image_info.virt_kstart, image_info.virt_kend - image_info.virt_kstart, )?; @@ -530,161 +221,22 @@ impl BootSetup<'_> { Ok(kernel_segment) } - fn count_page_tables( - &mut self, - table: &mut PageTable, - from: u64, - to: u64, - pfn: u64, - ) -> Result { - debug!("counting pgtables from={} to={} pfn={}", from, to, pfn); - if table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS { - return Err(XenClientError::new("too many mappings")); - } - - let m = table.mappings_count; - - 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 idx in 0..table.mappings_count { - if from < table.mappings[idx].area.to && to > table.mappings[idx].area.from { - return Err(XenClientError::new("overlapping mappings")); - } - } - let mut map = PageTableMapping::default(); - map.area.from = from & X86_VIRT_MASK; - map.area.to = to & X86_VIRT_MASK; - - for l in (0usize..X86_PGTABLE_LEVELS as usize).rev() { - map.levels[l].pfn = self.pfn_alloc_end + map.area.pgtables as u64; - if l as u64 == X86_PGTABLE_LEVELS - 1 { - if table.mappings_count == 0 { - map.levels[l].from = 0; - map.levels[l].to = X86_VIRT_MASK; - map.levels[l].pgtables = 1; - map.area.pgtables += 1; - } - continue; - } - - let bits = X86_PAGE_SHIFT + (l + 1) as u64 * X86_PGTABLE_LEVEL_SHIFT; - let mask = BootSetup::bits_to_mask(bits); - map.levels[l].from = map.area.from & !mask; - map.levels[l].to = map.area.to | mask; - - for cmp in &mut table.mappings[0..table.mappings_count] { - if cmp.levels[l].from == cmp.levels[l].to { - continue; - } - - if map.levels[l].from >= cmp.levels[l].from && map.levels[l].to <= cmp.levels[l].to - { - map.levels[l].from = 0; - map.levels[l].to = 0; - break; - } - - if map.levels[l].from >= cmp.levels[l].from - && map.levels[l].from <= cmp.levels[l].to - { - map.levels[l].from = cmp.levels[l].to + 1; - } - - if map.levels[l].to >= cmp.levels[l].from && map.levels[l].to <= cmp.levels[l].to { - map.levels[l].to = cmp.levels[l].from - 1; - } - } - - if map.levels[l].from < map.levels[l].to { - map.levels[l].pgtables = - (((map.levels[l].to - map.levels[l].from) >> bits) + 1) as usize; - } - - debug!( - "BootSetup count_pgtables {:#x}/{}: {:#x} -> {:#x}, {} tables", - mask, bits, map.levels[l].from, map.levels[l].to, map.levels[l].pgtables - ); - map.area.pgtables += map.levels[l].pgtables; - } - table.mappings[m] = map; - Ok(m) - } - - fn alloc_p2m_segment( - &mut self, - page_table: &mut PageTable, - image_info: &BootImageInfo, - ) -> Result { - let mut p2m_alloc_size = - ((self.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1); - let from = image_info.virt_p2m_base; - let to = from + p2m_alloc_size - 1; - let m = self.count_page_tables(page_table, from, to, self.pfn_alloc_end)?; - - let pgtables: usize; - { - let map = &mut page_table.mappings[m]; - map.area.pfn = self.pfn_alloc_end; - for lvl_idx in 0..4 { - map.levels[lvl_idx].pfn += p2m_alloc_size >> X86_PAGE_SHIFT; - } - pgtables = map.area.pgtables; - } - page_table.mappings_count += 1; - p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64; - let p2m_segment = self.alloc_segment(0, p2m_alloc_size)?; - Ok(p2m_segment) - } - - fn round_up(addr: u64, mask: u64) -> u64 { + pub(crate) fn round_up(addr: u64, mask: u64) -> u64 { addr | mask } - fn bits_to_mask(bits: u64) -> u64 { + pub(crate) fn bits_to_mask(bits: u64) -> u64 { (1 << bits) - 1 } - fn alloc_page_tables( + pub(crate) fn alloc_segment( &mut self, - table: &mut PageTable, - image_info: &BootImageInfo, + arch: &mut dyn ArchBootSetup, + start: u64, + size: u64, ) -> Result { - let mut extra_pages = 1; - extra_pages += (512 * 1024) / X86_PAGE_SIZE; - let mut pages = extra_pages; - - let mut try_virt_end: u64; - let mut m: usize; - loop { - try_virt_end = BootSetup::round_up( - self.virt_alloc_end + pages * X86_PAGE_SIZE, - BootSetup::bits_to_mask(22), - ); - m = self.count_page_tables(table, image_info.virt_base, try_virt_end, 0)?; - pages = table.mappings[m].area.pgtables as u64 + extra_pages; - if self.virt_alloc_end + pages * X86_PAGE_SIZE <= try_virt_end + 1 { - break; - } - } - - table.mappings[m].area.pfn = 0; - table.mappings_count += 1; - self.virt_pgtab_end = try_virt_end + 1; - let segment = - self.alloc_segment(0, table.mappings[m].area.pgtables as u64 * X86_PAGE_SIZE)?; - debug!( - "BootSetup alloc_page_tables table={:?} segment={:?}", - table, segment - ); - Ok(segment) - } - - fn alloc_segment(&mut self, start: u64, size: u64) -> Result { if start > 0 { - self.alloc_padding_pages(start)?; + self.alloc_padding_pages(arch, start)?; } let page_size: u32 = (1i64 << XEN_PAGE_SHIFT) as u32; @@ -693,14 +245,14 @@ impl BootSetup<'_> { let mut segment = DomainSegment { vstart: start, - _vend: 0, + vend: 0, pfn: self.pfn_alloc_end, addr: 0, size, pages, }; - self.chk_alloc_pages(pages)?; + self.chk_alloc_pages(arch, pages)?; let ptr = self.phys.pfn_to_ptr(segment.pfn, pages)?; segment.addr = ptr; @@ -708,23 +260,26 @@ impl BootSetup<'_> { slice::from_raw_parts_mut(ptr as *mut u8, (pages * page_size as u64) as usize) }; slice.fill(0); - segment._vend = self.virt_alloc_end; + segment.vend = self.virt_alloc_end; debug!( "BootSetup alloc_segment {:#x} -> {:#x} (pfn {:#x} + {:#x} pages)", - start, segment._vend, segment.pfn, pages + start, segment.vend, segment.pfn, pages ); Ok(segment) } - fn alloc_page(&mut self) -> Result { + fn alloc_page( + &mut self, + arch: &mut dyn ArchBootSetup, + ) -> Result { let start = self.virt_alloc_end; let pfn = self.pfn_alloc_end; - self.chk_alloc_pages(1)?; + self.chk_alloc_pages(arch, 1)?; debug!("BootSetup alloc_page {:#x} (pfn {:#x})", start, pfn); Ok(DomainSegment { vstart: start, - _vend: (start + X86_PAGE_SIZE) - 1, + vend: (start + arch.page_size()) - 1, pfn, addr: 0, size: 0, @@ -732,15 +287,23 @@ impl BootSetup<'_> { }) } - fn alloc_module(&mut self, buffer: &[u8]) -> Result { - let segment = self.alloc_segment(0, buffer.len() as u64)?; + fn alloc_module( + &mut self, + arch: &mut dyn ArchBootSetup, + buffer: &[u8], + ) -> Result { + let segment = self.alloc_segment(arch, 0, buffer.len() as u64)?; let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) }; copy(slice, buffer); Ok(segment) } - fn alloc_padding_pages(&mut self, boundary: u64) -> Result<(), XenClientError> { - if (boundary & (X86_PAGE_SIZE - 1)) != 0 { + fn alloc_padding_pages( + &mut self, + arch: &mut dyn ArchBootSetup, + boundary: u64, + ) -> Result<(), XenClientError> { + if (boundary & (arch.page_size() - 1)) != 0 { return Err(XenClientError::new( format!("segment boundary isn't page aligned: {:#x}", boundary).as_str(), )); @@ -751,12 +314,16 @@ impl BootSetup<'_> { format!("segment boundary too low: {:#x})", boundary).as_str(), )); } - let pages = (boundary - self.virt_alloc_end) / X86_PAGE_SIZE; - self.chk_alloc_pages(pages)?; + let pages = (boundary - self.virt_alloc_end) / arch.page_size(); + self.chk_alloc_pages(arch, pages)?; Ok(()) } - fn chk_alloc_pages(&mut self, pages: u64) -> Result<(), XenClientError> { + fn chk_alloc_pages( + &mut self, + arch: &mut dyn ArchBootSetup, + pages: u64, + ) -> Result<(), XenClientError> { if pages > self.total_pages || self.pfn_alloc_end > self.total_pages || pages > self.total_pages - self.pfn_alloc_end @@ -771,7 +338,57 @@ impl BootSetup<'_> { } self.pfn_alloc_end += pages; - self.virt_alloc_end += pages * X86_PAGE_SIZE; + self.virt_alloc_end += pages * arch.page_size(); Ok(()) } } + +pub trait ArchBootSetup { + fn page_size(&mut self) -> u64; + fn page_shift(&mut self) -> u64; + + fn alloc_p2m_segment( + &mut self, + setup: &mut BootSetup, + image_info: &BootImageInfo, + ) -> Result; + + fn alloc_page_tables( + &mut self, + setup: &mut BootSetup, + image_info: &BootImageInfo, + ) -> Result; + + fn setup_page_tables( + &mut self, + setup: &mut BootSetup, + state: &mut BootState, + ) -> Result<(), XenClientError>; + + fn setup_start_info( + &mut self, + setup: &mut BootSetup, + state: &BootState, + cmdline: &str, + ) -> Result<(), XenClientError>; + + fn setup_shared_info( + &mut self, + setup: &mut BootSetup, + shared_info_frame: u64, + ) -> Result<(), XenClientError>; + + fn setup_hypercall_page( + &mut self, + setup: &mut BootSetup, + image_info: &BootImageInfo, + ) -> Result<(), XenClientError>; + + fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<(), XenClientError>; + fn bootlate( + &mut self, + setup: &mut BootSetup, + state: &mut BootState, + ) -> Result<(), XenClientError>; + fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<(), XenClientError>; +} diff --git a/xenclient/src/lib.rs b/xenclient/src/lib.rs index 3c19e1a..ae2db62 100644 --- a/xenclient/src/lib.rs +++ b/xenclient/src/lib.rs @@ -8,6 +8,7 @@ mod x86; use crate::boot::BootSetup; use crate::create::DomainConfig; use crate::elfloader::ElfImageLoader; +use crate::x86::X86BootSetup; use std::error::Error; use std::fmt::{Display, Formatter}; use std::fs::read; @@ -148,10 +149,7 @@ impl XenClient { format!("{}/uuid", vm_path).as_str(), &Uuid::from_bytes(domain.handle).to_string(), )?; - tx.write_string( - format!("{}/name", vm_path).as_str(), - "mycelium", - )?; + tx.write_string(format!("{}/name", vm_path).as_str(), "mycelium")?; tx.write_string(format!("{}/type", libxl_path).as_str(), "pv")?; tx.commit()?; } @@ -167,14 +165,16 @@ impl XenClient { { let mut boot = BootSetup::new(&self.call, domid); + let mut arch = X86BootSetup::new(); let initrd = read(config.initrd_path.as_str())?; let mut state = boot.initialize( + &mut arch, &image_loader, initrd.as_slice(), config.max_vcpus, config.mem_mb, )?; - boot.boot(&mut state, config.cmdline.as_str())?; + boot.boot(&mut arch, &mut state, config.cmdline.as_str())?; console_evtchn = state.console_evtchn; xenstore_evtchn = state.store_evtchn; console_mfn = boot.phys.p2m[state.console_segment.pfn as usize]; @@ -183,10 +183,7 @@ impl XenClient { { let mut tx = self.store.transaction()?; - tx.write_string( - format!("{}/image/os_type", vm_path).as_str(), - "linux", - )?; + tx.write_string(format!("{}/image/os_type", vm_path).as_str(), "linux")?; tx.write_string( format!("{}/image/kernel", vm_path).as_str(), &config.kernel_path, @@ -208,10 +205,7 @@ impl XenClient { format!("{}/memory/target", dom_path).as_str(), &(config.mem_mb * 1024).to_string(), )?; - tx.write_string( - format!("{}/memory/videoram", dom_path).as_str(), - "0", - )?; + tx.write_string(format!("{}/memory/videoram", dom_path).as_str(), "0")?; tx.write_string(format!("{}/domid", dom_path).as_str(), &domid.to_string())?; tx.write_string( format!("{}/store/port", dom_path).as_str(), @@ -252,32 +246,14 @@ impl XenClient { format!("{}/frontend-id", backend_path).as_str(), &domid.to_string(), )?; - tx.write_string( - format!("{}/online", backend_path).as_str(), - "1", - )?; + tx.write_string(format!("{}/online", backend_path).as_str(), "1")?; tx.write_string(format!("{}/state", backend_path).as_str(), "1")?; - tx.write_string( - format!("{}/protocol", backend_path).as_str(), - "vt100", - )?; + tx.write_string(format!("{}/protocol", backend_path).as_str(), "vt100")?; - tx.write_string( - format!("{}/backend-id", frontend_path).as_str(), - "0", - )?; - tx.write_string( - format!("{}/limit", frontend_path).as_str(), - "1048576", - )?; - tx.write_string( - format!("{}/type", frontend_path).as_str(), - "xenconsoled", - )?; - tx.write_string( - format!("{}/output", frontend_path).as_str(), - "pty", - )?; + tx.write_string(format!("{}/backend-id", frontend_path).as_str(), "0")?; + tx.write_string(format!("{}/limit", frontend_path).as_str(), "1048576")?; + tx.write_string(format!("{}/type", frontend_path).as_str(), "xenconsoled")?; + tx.write_string(format!("{}/output", frontend_path).as_str(), "pty")?; tx.write_string(format!("{}/tty", frontend_path).as_str(), "")?; tx.write_string( format!("{}/port", frontend_path).as_str(), diff --git a/xenclient/src/x86.rs b/xenclient/src/x86.rs index 20d3d07..f8a235b 100644 --- a/xenclient/src/x86.rs +++ b/xenclient/src/x86.rs @@ -1,4 +1,18 @@ +use crate::boot::{ + ArchBootSetup, BootImageInfo, BootSetup, BootState, DomainSegment, XEN_UNSET_ADDR, +}; +use crate::sys::{ + SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, VGCF_IN_KERNEL, VGCF_ONLINE, + XEN_PAGE_SHIFT, +}; +use crate::XenClientError; use libc::c_char; +use log::{debug, trace}; +use slice_copy::copy; +use std::cmp::{max, min}; +use std::mem::size_of; +use std::slice; +use xencall::sys::{VcpuGuestContext, MMUEXT_PIN_L4_TABLE}; pub const X86_PAGE_SHIFT: u64 = 12; pub const X86_PAGE_SIZE: u64 = 1 << X86_PAGE_SHIFT; @@ -112,3 +126,511 @@ pub struct SharedInfo { pub p2m_vaddr: u64, pub p2m_generation: u64, } + +pub struct X86BootSetup { + table: PageTable, +} + +#[derive(Debug)] +struct VmemRange { + start: u64, + end: u64, + _flags: u32, + _nid: u32, +} + +impl X86BootSetup { + pub fn new() -> X86BootSetup { + X86BootSetup { + table: PageTable::default(), + } + } + + const PAGE_PRESENT: u64 = 0x001; + const PAGE_RW: u64 = 0x002; + const PAGE_USER: u64 = 0x004; + const PAGE_ACCESSED: u64 = 0x020; + const PAGE_DIRTY: u64 = 0x040; + fn get_pg_prot(&mut self, l: usize, pfn: u64) -> u64 { + let prot = [ + X86BootSetup::PAGE_PRESENT | X86BootSetup::PAGE_RW | X86BootSetup::PAGE_ACCESSED, + X86BootSetup::PAGE_PRESENT + | X86BootSetup::PAGE_RW + | X86BootSetup::PAGE_ACCESSED + | X86BootSetup::PAGE_DIRTY + | X86BootSetup::PAGE_USER, + X86BootSetup::PAGE_PRESENT + | X86BootSetup::PAGE_RW + | X86BootSetup::PAGE_ACCESSED + | X86BootSetup::PAGE_DIRTY + | X86BootSetup::PAGE_USER, + X86BootSetup::PAGE_PRESENT + | X86BootSetup::PAGE_RW + | X86BootSetup::PAGE_ACCESSED + | X86BootSetup::PAGE_DIRTY + | X86BootSetup::PAGE_USER, + ]; + + let prot = prot[l]; + if l > 0 { + return prot; + } + + for m in 0..self.table.mappings_count { + let map = &self.table.mappings[m]; + let pfn_s = map.levels[(X86_PGTABLE_LEVELS - 1) as usize].pfn; + let pfn_e = map.area.pgtables as u64 + pfn_s; + if pfn >= pfn_s && pfn < pfn_e { + return prot & !X86BootSetup::PAGE_RW; + } + } + prot + } + + fn count_page_tables( + &mut self, + setup: &mut BootSetup, + from: u64, + to: u64, + pfn: u64, + ) -> Result { + debug!("counting pgtables from={} to={} pfn={}", from, to, pfn); + if self.table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS { + return Err(XenClientError::new("too many mappings")); + } + + let m = self.table.mappings_count; + + let pfn_end = pfn + ((to - from) >> X86_PAGE_SHIFT); + if pfn_end >= setup.phys.p2m_size() { + return Err(XenClientError::new("not enough memory for initial mapping")); + } + + for idx in 0..self.table.mappings_count { + if from < self.table.mappings[idx].area.to && to > self.table.mappings[idx].area.from { + return Err(XenClientError::new("overlapping mappings")); + } + } + let mut map = PageTableMapping::default(); + map.area.from = from & X86_VIRT_MASK; + map.area.to = to & X86_VIRT_MASK; + + for l in (0usize..X86_PGTABLE_LEVELS as usize).rev() { + map.levels[l].pfn = setup.pfn_alloc_end + map.area.pgtables as u64; + if l as u64 == X86_PGTABLE_LEVELS - 1 { + if self.table.mappings_count == 0 { + map.levels[l].from = 0; + map.levels[l].to = X86_VIRT_MASK; + map.levels[l].pgtables = 1; + map.area.pgtables += 1; + } + continue; + } + + let bits = X86_PAGE_SHIFT + (l + 1) as u64 * X86_PGTABLE_LEVEL_SHIFT; + let mask = BootSetup::bits_to_mask(bits); + map.levels[l].from = map.area.from & !mask; + map.levels[l].to = map.area.to | mask; + + for cmp in &mut self.table.mappings[0..self.table.mappings_count] { + if cmp.levels[l].from == cmp.levels[l].to { + continue; + } + + if map.levels[l].from >= cmp.levels[l].from && map.levels[l].to <= cmp.levels[l].to + { + map.levels[l].from = 0; + map.levels[l].to = 0; + break; + } + + if map.levels[l].from >= cmp.levels[l].from + && map.levels[l].from <= cmp.levels[l].to + { + map.levels[l].from = cmp.levels[l].to + 1; + } + + if map.levels[l].to >= cmp.levels[l].from && map.levels[l].to <= cmp.levels[l].to { + map.levels[l].to = cmp.levels[l].from - 1; + } + } + + if map.levels[l].from < map.levels[l].to { + map.levels[l].pgtables = + (((map.levels[l].to - map.levels[l].from) >> bits) + 1) as usize; + } + + debug!( + "BootSetup count_pgtables {:#x}/{}: {:#x} -> {:#x}, {} tables", + mask, bits, map.levels[l].from, map.levels[l].to, map.levels[l].pgtables + ); + map.area.pgtables += map.levels[l].pgtables; + } + self.table.mappings[m] = map; + Ok(m) + } +} + +impl ArchBootSetup for X86BootSetup { + fn page_size(&mut self) -> u64 { + X86_PAGE_SIZE + } + + fn alloc_p2m_segment( + &mut self, + setup: &mut BootSetup, + image_info: &BootImageInfo, + ) -> Result { + let mut p2m_alloc_size = + ((setup.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1); + let from = image_info.virt_p2m_base; + let to = from + p2m_alloc_size - 1; + let m = self.count_page_tables(setup, from, to, setup.pfn_alloc_end)?; + + let pgtables: usize; + { + let map = &mut self.table.mappings[m]; + map.area.pfn = setup.pfn_alloc_end; + for lvl_idx in 0..4 { + map.levels[lvl_idx].pfn += p2m_alloc_size >> X86_PAGE_SHIFT; + } + pgtables = map.area.pgtables; + } + self.table.mappings_count += 1; + p2m_alloc_size += (pgtables << X86_PAGE_SHIFT) as u64; + let p2m_segment = setup.alloc_segment(self, 0, p2m_alloc_size)?; + Ok(p2m_segment) + } + + fn alloc_page_tables( + &mut self, + setup: &mut BootSetup, + image_info: &BootImageInfo, + ) -> Result { + let mut extra_pages = 1; + extra_pages += (512 * 1024) / X86_PAGE_SIZE; + let mut pages = extra_pages; + + let mut try_virt_end: u64; + let mut m: usize; + loop { + try_virt_end = BootSetup::round_up( + setup.virt_alloc_end + pages * X86_PAGE_SIZE, + BootSetup::bits_to_mask(22), + ); + m = self.count_page_tables(setup, image_info.virt_base, try_virt_end, 0)?; + pages = self.table.mappings[m].area.pgtables as u64 + extra_pages; + if setup.virt_alloc_end + pages * X86_PAGE_SIZE <= try_virt_end + 1 { + break; + } + } + + self.table.mappings[m].area.pfn = 0; + self.table.mappings_count += 1; + setup.virt_pgtab_end = try_virt_end + 1; + let size = self.table.mappings[m].area.pgtables as u64 * X86_PAGE_SIZE; + let segment = setup.alloc_segment(self, 0, size)?; + debug!( + "BootSetup alloc_page_tables table={:?} segment={:?}", + self.table, segment + ); + Ok(segment) + } + + fn setup_page_tables( + &mut self, + setup: &mut BootSetup, + state: &mut BootState, + ) -> Result<(), XenClientError> { + let p2m_guest = unsafe { + slice::from_raw_parts_mut( + state.p2m_segment.addr as *mut u64, + setup.phys.p2m_size() as usize, + ) + }; + copy(p2m_guest, &setup.phys.p2m); + + for l in (0usize..X86_PGTABLE_LEVELS as usize).rev() { + for m1 in 0usize..self.table.mappings_count { + let map1 = &self.table.mappings[m1]; + let from = map1.levels[l].from; + let to = map1.levels[l].to; + let pg_ptr = setup.phys.pfn_to_ptr(map1.levels[l].pfn, 0)? as *mut u64; + for m2 in 0usize..self.table.mappings_count { + let map2 = &self.table.mappings[m2]; + let lvl = if l > 0 { + &map2.levels[l - 1] + } else { + &map2.area + }; + + if l > 0 && lvl.pgtables == 0 { + continue; + } + + if lvl.from >= to || lvl.to <= from { + continue; + } + + let p_s = (max(from, lvl.from) - from) + >> (X86_PAGE_SHIFT + l as u64 * X86_PGTABLE_LEVEL_SHIFT); + let p_e = (min(to, lvl.to) - from) + >> (X86_PAGE_SHIFT + l as u64 * X86_PGTABLE_LEVEL_SHIFT); + let rhs = X86_PAGE_SHIFT as usize + l * X86_PGTABLE_LEVEL_SHIFT as usize; + let mut pfn = ((max(from, lvl.from) - lvl.from) >> rhs) + lvl.pfn; + + debug!( + "BootSetup setup_page_tables lvl={} map_1={} map_2={} pfn={:#x} p_s={:#x} p_e={:#x}", + l, m1, m2, pfn, p_s, p_e + ); + + let pg = unsafe { slice::from_raw_parts_mut(pg_ptr, (p_e + 1) as usize) }; + for p in p_s..p_e + 1 { + let prot = self.get_pg_prot(l, pfn); + let pfn_paddr = setup.phys.p2m[pfn as usize] << X86_PAGE_SHIFT; + let value = pfn_paddr | prot; + pg[p as usize] = value; + pfn += 1; + } + } + } + } + Ok(()) + } + + fn setup_start_info( + &mut self, + setup: &mut BootSetup, + state: &BootState, + cmdline: &str, + ) -> Result<(), XenClientError> { + let ptr = setup.phys.pfn_to_ptr(state.start_info_segment.pfn, 1)?; + let byte_slice = + unsafe { slice::from_raw_parts_mut(ptr as *mut u8, X86_PAGE_SIZE as usize) }; + byte_slice.fill(0); + let info = ptr as *mut StartInfo; + unsafe { + for (i, c) in X86_GUEST_MAGIC.chars().enumerate() { + (*info).magic[i] = c as c_char; + } + (*info).magic[X86_GUEST_MAGIC.len()] = 0 as c_char; + (*info).nr_pages = setup.total_pages; + (*info).shared_info = state.shared_info_frame << X86_PAGE_SHIFT; + (*info).pt_base = state.page_table_segment.vstart; + (*info).nr_pt_frames = self.table.mappings[0].area.pgtables as u64; + (*info).mfn_list = state.p2m_segment.vstart; + (*info).first_p2m_pfn = state.p2m_segment.pfn; + (*info).nr_p2m_frames = state.p2m_segment.pages; + (*info).flags = 0; + (*info).store_evtchn = state.store_evtchn; + (*info).store_mfn = setup.phys.p2m[state.xenstore_segment.pfn as usize]; + (*info).console.mfn = setup.phys.p2m[state.console_segment.pfn as usize]; + (*info).console.evtchn = state.console_evtchn; + (*info).mod_start = state.initrd_segment.vstart; + (*info).mod_len = state.initrd_segment.size; + for (i, c) in cmdline.chars().enumerate() { + (*info).cmdline[i] = c as c_char; + } + (*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0; + trace!("BootSetup setup_start_info start_info={:?}", *info); + } + Ok(()) + } + + fn setup_shared_info( + &mut self, + setup: &mut BootSetup, + shared_info_frame: u64, + ) -> Result<(), XenClientError> { + let info = setup + .phys + .map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)? + as *mut SharedInfo; + unsafe { + let size = size_of::(); + let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size); + info_as_buff.fill(0); + for i in 0..32 { + (*info).vcpu_info[i].evtchn_upcall_mask = 1; + } + trace!("BootSetup setup_shared_info shared_info={:?}", *info); + } + Ok(()) + } + + fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<(), XenClientError> { + let mut vmemranges: Vec = Vec::new(); + let stub = VmemRange { + start: 0, + end: total_pages << 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 != total_pages { + return Err(XenClientError::new( + "page count mismatch while calculating pages", + )); + } + + setup.total_pages = total; + + let mut p2m = vec![u64::MAX; p2m_size as usize]; + for range in &vmemranges { + let mut extents_init = 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; + let mut pfn_base_idx: u64 = pfn_base; + while super_pages > 0 { + let count = super_pages.min(SUPERPAGE_BATCH_SIZE); + super_pages -= count; + + let mut j: usize = 0; + let mut pfn: u64 = pfn_base_idx; + loop { + if pfn >= pfn_base_idx + (count << SUPERPAGE_2MB_SHIFT) { + break; + } + extents_init[j] = p2m[pfn as usize]; + pfn += SUPERPAGE_2MB_NR_PFNS; + j += 1; + } + + let extents_init_slice = extents_init.as_slice(); + let extents = setup.call.populate_physmap( + setup.domid, + count, + SUPERPAGE_2MB_SHIFT as u32, + 0, + &extents_init_slice[0usize..count as usize], + )?; + + pfn = pfn_base_idx; + for mfn in extents { + for k in 0..SUPERPAGE_2MB_NR_PFNS { + p2m[pfn as usize] = mfn + k; + pfn += 1; + } + } + pfn_base_idx = pfn; + } + + let mut j = pfn_base_idx - pfn_base; + loop { + if j >= pages { + break; + } + + let allocsz = (1024 * 1024).min(pages - j); + let p2m_idx = (pfn_base + j) as usize; + let p2m_end_idx = p2m_idx + allocsz as usize; + let input_extent_starts = &p2m[p2m_idx..p2m_end_idx]; + let result = + setup + .call + .populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?; + + if result.len() != allocsz as usize { + return Err(XenClientError::new( + format!( + "failed to populate physmap: wanted={} received={} input_extents={}", + allocsz, + result.len(), + input_extent_starts.len() + ) + .as_str(), + )); + } + + for (i, item) in result.iter().enumerate() { + let p = (pfn_base + j + i as u64) as usize; + let m = *item; + p2m[p] = m; + } + j += allocsz; + } + } + + setup.phys.load_p2m(p2m); + Ok(()) + } + + fn setup_hypercall_page( + &mut self, + setup: &mut BootSetup, + image_info: &BootImageInfo, + ) -> Result<(), XenClientError> { + if image_info.virt_hypercall == XEN_UNSET_ADDR { + return Ok(()); + } + + let pfn = (image_info.virt_hypercall - image_info.virt_base) >> X86_PAGE_SHIFT; + let mfn = setup.phys.p2m[pfn as usize]; + setup.call.hypercall_init(setup.domid, mfn)?; + Ok(()) + } + + fn page_shift(&mut self) -> u64 { + X86_PAGE_SHIFT + } + + fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<(), XenClientError> { + let pg_pfn = state.page_table_segment.pfn; + let pg_mfn = setup.phys.p2m[pg_pfn as usize]; + let mut vcpu = VcpuGuestContext::default(); + vcpu.user_regs.rip = state.image_info.virt_entry; + vcpu.user_regs.rsp = + state.image_info.virt_base + (state.boot_stack_segment.pfn + 1) * self.page_size(); + vcpu.user_regs.rsi = + state.image_info.virt_base + (state.start_info_segment.pfn) * self.page_size(); + vcpu.user_regs.rflags = 1 << 9; + vcpu.debugreg[6] = 0xffff0ff0; + vcpu.debugreg[7] = 0x00000400; + vcpu.flags = VGCF_IN_KERNEL | VGCF_ONLINE; + let cr3_pfn = pg_mfn; + debug!( + "cr3: pfn {:#x} mfn {:#x}", + state.page_table_segment.pfn, cr3_pfn + ); + vcpu.ctrlreg[3] = cr3_pfn << 12; + vcpu.user_regs.ds = 0x0; + vcpu.user_regs.es = 0x0; + vcpu.user_regs.fs = 0x0; + vcpu.user_regs.gs = 0x0; + vcpu.user_regs.ss = 0xe02b; + vcpu.user_regs.cs = 0xe033; + vcpu.kernel_ss = vcpu.user_regs.ss as u64; + vcpu.kernel_sp = vcpu.user_regs.rsp; + debug!("vcpu context: {:?}", vcpu); + setup.call.set_vcpu_context(setup.domid, 0, &vcpu)?; + Ok(()) + } + + fn bootlate( + &mut self, + setup: &mut BootSetup, + state: &mut BootState, + ) -> Result<(), XenClientError> { + let pg_pfn = state.page_table_segment.pfn; + let pg_mfn = setup.phys.p2m[pg_pfn as usize]; + setup.phys.unmap(pg_pfn)?; + setup.phys.unmap(state.p2m_segment.pfn)?; + setup + .call + .mmuext(setup.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?; + Ok(()) + } +}