mirror of
				https://github.com/edera-dev/krata.git
				synced 2025-11-03 23:29:39 +00:00 
			
		
		
		
	rearrange code to move most x86 specifics to the x86 module
This commit is contained in:
		@ -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<VmemRange> = 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<DomainSegment> = 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<DomainSegment> = 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::<SharedInfo>();
 | 
			
		||||
            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<DomainSegment, XenClientError> {
 | 
			
		||||
        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<usize, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
    fn alloc_page(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        arch: &mut dyn ArchBootSetup,
 | 
			
		||||
    ) -> Result<DomainSegment, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
        let segment = self.alloc_segment(0, buffer.len() as u64)?;
 | 
			
		||||
    fn alloc_module(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        arch: &mut dyn ArchBootSetup,
 | 
			
		||||
        buffer: &[u8],
 | 
			
		||||
    ) -> Result<DomainSegment, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError>;
 | 
			
		||||
 | 
			
		||||
    fn alloc_page_tables(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        setup: &mut BootSetup,
 | 
			
		||||
        image_info: &BootImageInfo,
 | 
			
		||||
    ) -> Result<DomainSegment, XenClientError>;
 | 
			
		||||
 | 
			
		||||
    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>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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(),
 | 
			
		||||
 | 
			
		||||
@ -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<usize, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
        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<DomainSegment, XenClientError> {
 | 
			
		||||
        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::<SharedInfo>();
 | 
			
		||||
            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<VmemRange> = 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(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user