we've done it, it boots!

This commit is contained in:
Alex Zenla 2024-01-16 23:07:34 -08:00
parent 0d68db8523
commit 5c1bb3d8fc
No known key found for this signature in database
GPG Key ID: 067B238899B51269
7 changed files with 161 additions and 40 deletions

View File

@ -15,7 +15,7 @@ use std::ptr::addr_of_mut;
use std::slice; use std::slice;
pub struct DomainControl<'a> { pub struct DomainControl<'a> {
call: &'a XenCall, pub call: &'a XenCall,
} }
impl DomainControl<'_> { impl DomainControl<'_> {

View File

@ -3,7 +3,7 @@ pub mod memory;
pub mod sys; pub mod sys;
use crate::sys::{ use crate::sys::{
Hypercall, MmapBatch, MultiCallEntry, XenCapabilitiesInfo, HYPERVISOR_MULTICALL, Hypercall, MmapBatch, MmapResource, MultiCallEntry, XenCapabilitiesInfo, HYPERVISOR_MULTICALL,
HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES, HYPERVISOR_XEN_VERSION, XENVER_CAPABILITIES,
}; };
use libc::{mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE}; use libc::{mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
@ -168,6 +168,29 @@ impl XenCall {
Ok(()) Ok(())
} }
pub fn map_resource(
&self,
domid: u32,
typ: u32,
id: u32,
idx: u32,
num: u64,
addr: u64,
) -> Result<(), XenCallError> {
let mut resource = MmapResource {
dom: domid as u16,
typ,
id,
idx,
num,
addr,
};
unsafe {
sys::mmap_resource(self.handle.as_raw_fd(), &mut resource)?;
}
Ok(())
}
pub fn mmap_batch( pub fn mmap_batch(
&self, &self,
domid: u32, domid: u32,

View File

@ -18,6 +18,17 @@ pub struct MmapEntry {
pub npages: u64, pub npages: u64,
} }
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
pub struct MmapResource {
pub dom: u16,
pub typ: u32,
pub id: u32,
pub idx: u32,
pub num: u64,
pub addr: u64,
}
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct MmapBatch { pub struct MmapBatch {
@ -39,10 +50,12 @@ pub struct Mmap {
const IOCTL_PRIVCMD_HYPERCALL: u64 = 0x305000; const IOCTL_PRIVCMD_HYPERCALL: u64 = 0x305000;
const IOCTL_PRIVCMD_MMAP: u64 = 0x105002; const IOCTL_PRIVCMD_MMAP: u64 = 0x105002;
const IOCTL_PRIVCMD_MMAPBATCH_V2: u64 = 0x205004; const IOCTL_PRIVCMD_MMAPBATCH_V2: u64 = 0x205004;
const IOCTL_PRIVCMD_MMAP_RESOURCE: u64 = 0x205007;
ioctl_readwrite_bad!(hypercall, IOCTL_PRIVCMD_HYPERCALL, Hypercall); ioctl_readwrite_bad!(hypercall, IOCTL_PRIVCMD_HYPERCALL, Hypercall);
ioctl_readwrite_bad!(mmap, IOCTL_PRIVCMD_MMAP, Mmap); ioctl_readwrite_bad!(mmap, IOCTL_PRIVCMD_MMAP, Mmap);
ioctl_readwrite_bad!(mmapbatch, IOCTL_PRIVCMD_MMAPBATCH_V2, MmapBatch); ioctl_readwrite_bad!(mmapbatch, IOCTL_PRIVCMD_MMAPBATCH_V2, MmapBatch);
ioctl_readwrite_bad!(mmap_resource, IOCTL_PRIVCMD_MMAP_RESOURCE, MmapResource);
pub const HYPERVISOR_SET_TRAP_TABLE: c_ulong = 0; pub const HYPERVISOR_SET_TRAP_TABLE: c_ulong = 0;
pub const HYPERVISOR_MMU_UPDATE: c_ulong = 1; pub const HYPERVISOR_MMU_UPDATE: c_ulong = 1;

View File

@ -25,16 +25,13 @@ fn main() -> Result<(), XenClientError> {
..Default::default() ..Default::default()
}; };
let domid = domctl.create_domain(domain)?; let domid = domctl.create_domain(domain)?;
let result = boot( boot(
domid, domid,
kernel_image_path.as_str(), kernel_image_path.as_str(),
initrd_path.as_str(), initrd_path.as_str(),
&call, &call,
&domctl, &domctl,
); )?;
domctl.destroy_domain(domid)?;
result?;
println!("domain destroyed: {}", domid);
Ok(()) Ok(())
} }
@ -50,7 +47,7 @@ fn boot(
let memctl = MemoryControl::new(call); let memctl = MemoryControl::new(call);
let mut boot = BootSetup::new(call, domctl, &memctl, domid); let mut boot = BootSetup::new(call, domctl, &memctl, domid);
let initrd = read(initrd_path)?; let initrd = read(initrd_path)?;
let mut state = boot.initialize(&image_loader, initrd.as_slice(), 512)?; let mut state = boot.initialize(&image_loader, initrd.as_slice(), 1, 512)?;
boot.boot(&mut state, "debug")?; boot.boot(&mut state, "debug")?;
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
use crate::mem::PhysicalPages; use crate::mem::PhysicalPages;
use crate::sys::{ use crate::sys::{
SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, VGCF_IN_KERNEL, VGCF_ONLINE, GrantEntry, SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, VGCF_IN_KERNEL,
XEN_PAGE_SHIFT, VGCF_ONLINE, XEN_PAGE_SHIFT,
}; };
use crate::x86::{ use crate::x86::{
PageTable, PageTableMapping, SharedInfo, StartInfo, MAX_GUEST_CMDLINE, X86_GUEST_MAGIC, PageTable, PageTableMapping, SharedInfo, StartInfo, MAX_GUEST_CMDLINE, X86_GUEST_MAGIC,
@ -9,10 +9,11 @@ use crate::x86::{
X86_PGTABLE_LEVEL_SHIFT, X86_VIRT_MASK, X86_PGTABLE_LEVEL_SHIFT, X86_VIRT_MASK,
}; };
use crate::XenClientError; use crate::XenClientError;
use libc::c_char; use libc::{c_char, munmap};
use log::{debug, trace}; use log::{debug, trace};
use slice_copy::copy; use slice_copy::copy;
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::ffi::c_void;
use std::mem::size_of; use std::mem::size_of;
use std::slice; use std::slice;
use xencall::domctl::DomainControl; use xencall::domctl::DomainControl;
@ -57,7 +58,7 @@ pub struct DomainSegment {
pfn: u64, pfn: u64,
addr: u64, addr: u64,
size: u64, size: u64,
_pages: u64, pages: u64,
} }
#[derive(Debug)] #[derive(Debug)]
@ -79,6 +80,8 @@ pub struct BootState {
pub page_table_segment: DomainSegment, pub page_table_segment: DomainSegment,
pub page_table: PageTable, pub page_table: PageTable,
pub image_info: BootImageInfo, pub image_info: BootImageInfo,
pub shared_info_frame: u64,
pub initrd_segment: DomainSegment,
} }
impl BootSetup<'_> { impl BootSetup<'_> {
@ -229,9 +232,14 @@ impl BootSetup<'_> {
&mut self, &mut self,
image_loader: &dyn BootImageLoader, image_loader: &dyn BootImageLoader,
initrd: &[u8], initrd: &[u8],
max_vcpus: u32,
mem_mb: u64, mem_mb: u64,
) -> Result<BootState, XenClientError> { ) -> Result<BootState, XenClientError> {
debug!("BootSetup initialize mem_mb={:?}", mem_mb); debug!(
"BootSetup initialize max_vcpus={:?} mem_mb={:?}",
max_vcpus, mem_mb
);
self.domctl.set_max_vcpus(self.domid, max_vcpus)?;
self.domctl.set_max_mem(self.domid, mem_mb * 1024)?; self.domctl.set_max_mem(self.domid, mem_mb * 1024)?;
let total_pages = mem_mb << (20 - X86_PAGE_SHIFT); let total_pages = mem_mb << (20 - X86_PAGE_SHIFT);
@ -253,6 +261,11 @@ impl BootSetup<'_> {
let console_segment = self.alloc_page()?; let console_segment = self.alloc_page()?;
let page_table_segment = self.alloc_page_tables(&mut page_table, &image_info)?; let page_table_segment = self.alloc_page_tables(&mut page_table, &image_info)?;
let boot_stack_segment = self.alloc_page()?; let boot_stack_segment = self.alloc_page()?;
if self.virt_pgtab_end > 0 {
self.alloc_padding_pages(self.virt_pgtab_end)?;
}
let mut initrd_segment: Option<DomainSegment> = None; let mut initrd_segment: Option<DomainSegment> = None;
if !image_info.unmapped_initrd { if !image_info.unmapped_initrd {
initrd_segment = Some(self.alloc_module(initrd)?); initrd_segment = Some(self.alloc_module(initrd)?);
@ -264,15 +277,11 @@ impl BootSetup<'_> {
} }
let p2m_segment = p2m_segment.unwrap(); let p2m_segment = p2m_segment.unwrap();
if self.virt_pgtab_end > 0 {
self.alloc_padding_pages(self.virt_pgtab_end)?;
}
if image_info.unmapped_initrd { if image_info.unmapped_initrd {
initrd_segment = Some(self.alloc_module(initrd)?); initrd_segment = Some(self.alloc_module(initrd)?);
} }
let _initrd_segment = initrd_segment.unwrap(); let initrd_segment = initrd_segment.unwrap();
let state = BootState { let state = BootState {
kernel_segment, kernel_segment,
@ -284,16 +293,17 @@ impl BootSetup<'_> {
page_table_segment, page_table_segment,
page_table, page_table,
image_info, image_info,
initrd_segment,
shared_info_frame: 0,
}; };
debug!("BootSetup initialize state={:?}", state); debug!("BootSetup initialize state={:?}", state);
Ok(state) Ok(state)
} }
pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> { pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> {
debug!( let domain_info = self.domctl.get_domain_info(self.domid)?;
"domain info: {:?}", let shared_info_frame = domain_info.shared_info_frame;
self.domctl.get_domain_info(self.domid)? state.shared_info_frame = shared_info_frame;
);
self.setup_page_tables(state)?; self.setup_page_tables(state)?;
self.setup_start_info(state, cmdline)?; self.setup_start_info(state, cmdline)?;
self.setup_hypercall_page(&state.image_info)?; self.setup_hypercall_page(&state.image_info)?;
@ -304,7 +314,7 @@ impl BootSetup<'_> {
let pg_mfn = self.phys.p2m[pg_pfn as usize]; let pg_mfn = self.phys.p2m[pg_pfn as usize];
self.memctl self.memctl
.mmuext(self.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?; .mmuext(self.domid, MMUEXT_PIN_L4_TABLE, pg_mfn, 0)?;
// self.setup_shared_info()?; self.setup_shared_info(state.shared_info_frame)?;
let mut vcpu = VcpuGuestContext::default(); let mut vcpu = VcpuGuestContext::default();
vcpu.user_regs.rip = state.image_info.virt_entry; vcpu.user_regs.rip = state.image_info.virt_entry;
@ -330,13 +340,46 @@ impl BootSetup<'_> {
vcpu.user_regs.cs = 0xe033; vcpu.user_regs.cs = 0xe033;
vcpu.kernel_ss = vcpu.user_regs.ss as u64; vcpu.kernel_ss = vcpu.user_regs.ss as u64;
vcpu.kernel_sp = vcpu.user_regs.rsp; vcpu.kernel_sp = vcpu.user_regs.rsp;
debug!("vcpu context: {:?}", vcpu);
self.domctl.set_vcpu_context(self.domid, 0, &vcpu)?; self.domctl.set_vcpu_context(self.domid, 0, &vcpu)?;
self.phys.unmap_all()?;
self.gnttab_seed(state)?;
Ok(())
}
fn gnttab_seed(&mut self, state: &mut BootState) -> Result<(), XenClientError> {
let console_gfn = self.phys.p2m[state.console_segment.pfn as usize];
let xenstore_gfn = self.phys.p2m[state.xenstore_segment.pfn as usize];
let addr = self
.domctl
.call
.mmap(0, 1 << XEN_PAGE_SHIFT)
.ok_or(XenClientError::new("failed to mmap for resource"))?;
self.domctl
.call
.map_resource(self.domid, 1, 0, 0, 1, addr)?;
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
entries[0].flags = 1 << 0;
entries[0].domid = 0;
entries[0].frame = console_gfn as u32;
entries[1].flags = 1 << 0;
entries[1].domid = 0;
entries[1].frame = xenstore_gfn as u32;
unsafe {
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
if result != 0 {
return Err(XenClientError::new("failed to unmap resource"));
}
}
Ok(()) Ok(())
} }
fn setup_page_tables(&mut self, state: &mut BootState) -> Result<(), XenClientError> { fn setup_page_tables(&mut self, state: &mut BootState) -> Result<(), XenClientError> {
let p2m_guest = unsafe { let p2m_guest = unsafe {
slice::from_raw_parts_mut(state.p2m_segment.addr as *mut u64, self.phys.p2m.len()) slice::from_raw_parts_mut(
state.p2m_segment.addr as *mut u64,
self.phys.p2m_size() as usize,
)
}; };
copy(p2m_guest, &self.phys.p2m); copy(p2m_guest, &self.phys.p2m);
@ -430,37 +473,44 @@ impl BootSetup<'_> {
} }
fn setup_start_info(&mut self, state: &BootState, cmdline: &str) -> Result<(), XenClientError> { fn setup_start_info(&mut self, state: &BootState, cmdline: &str) -> Result<(), XenClientError> {
let info = self.phys.pfn_to_ptr(state.start_info_segment.pfn, 1)? as *mut StartInfo; 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 { unsafe {
for (i, c) in X86_GUEST_MAGIC.chars().enumerate() { for (i, c) in X86_GUEST_MAGIC.chars().enumerate() {
(*info).magic[i] = c as c_char; (*info).magic[i] = c as c_char;
} }
(*info).magic[X86_GUEST_MAGIC.len()] = 0 as c_char;
(*info).nr_pages = self.total_pages; (*info).nr_pages = self.total_pages;
(*info).shared_info = 0; (*info).shared_info = state.shared_info_frame << X86_PAGE_SHIFT;
(*info).pt_base = state.page_table_segment.vstart; (*info).pt_base = state.page_table_segment.vstart;
(*info).nr_pt_frames = state.page_table.mappings[0].area.pgtables as u64; (*info).nr_pt_frames = state.page_table.mappings[0].area.pgtables as u64;
(*info).mfn_list = 0; (*info).mfn_list = state.p2m_segment.vstart;
(*info).first_p2m_pfn = 0; (*info).first_p2m_pfn = state.p2m_segment.pfn;
(*info).nr_p2m_frames = 0; (*info).nr_p2m_frames = state.p2m_segment.pages;
(*info).flags = 0; (*info).flags = 0;
(*info).store_evtchn = 0; (*info).store_evtchn = 0;
(*info).store_mfn = self.phys.p2m[state.xenstore_segment.pfn as usize]; (*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.mfn = self.phys.p2m[state.console_segment.pfn as usize];
(*info).console.evtchn = 0; (*info).console.evtchn = 0;
(*info).mod_start = 0; (*info).mod_start = state.initrd_segment.vstart;
(*info).mod_len = 0; (*info).mod_len = state.initrd_segment.size;
for (i, c) in cmdline.chars().enumerate() { for (i, c) in cmdline.chars().enumerate() {
(*info).cmdline[i] = c as c_char; (*info).cmdline[i] = c as c_char;
(*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0;
} }
(*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0;
trace!("BootSetup setup_start_info start_info={:?}", *info); trace!("BootSetup setup_start_info start_info={:?}", *info);
} }
Ok(()) Ok(())
} }
fn _setup_shared_info(&mut self) -> Result<(), XenClientError> { fn setup_shared_info(&mut self, shared_info_frame: u64) -> Result<(), XenClientError> {
let domain_info = self.domctl.get_domain_info(self.domid)?; let info = self
let info = self.phys.pfn_to_ptr(domain_info.shared_info_frame, 1)? as *mut SharedInfo; .phys
.map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)?
as *mut SharedInfo;
unsafe { unsafe {
let size = size_of::<SharedInfo>(); let size = size_of::<SharedInfo>();
let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size); let info_as_buff = slice::from_raw_parts_mut(info as *mut u8, size);
@ -656,7 +706,7 @@ impl BootSetup<'_> {
pfn: self.pfn_alloc_end, pfn: self.pfn_alloc_end,
addr: 0, addr: 0,
size, size,
_pages: pages, pages,
}; };
self.chk_alloc_pages(pages)?; self.chk_alloc_pages(pages)?;
@ -687,7 +737,7 @@ impl BootSetup<'_> {
pfn, pfn,
addr: 0, addr: 0,
size: 0, size: 0,
_pages: 1, pages: 1,
}) })
} }

View File

@ -1,4 +1,4 @@
use crate::sys::XEN_PAGE_SHIFT; use crate::sys::{XEN_PAGE_SHIFT, XEN_PAGE_SIZE};
use crate::XenClientError; use crate::XenClientError;
use libc::munmap; use libc::munmap;
use log::debug; use log::debug;
@ -114,6 +114,36 @@ impl PhysicalPages<'_> {
Ok(addr) Ok(addr)
} }
pub fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result<u64, XenClientError> {
let num = ((size + XEN_PAGE_SIZE - 1) >> XEN_PAGE_SHIFT) as usize;
let mut pfns = vec![u64::MAX; num];
for (i, item) in pfns.iter_mut().enumerate().take(num) {
*item = mfn + i as u64;
}
let actual_mmap_len = (num as u64) << XEN_PAGE_SHIFT;
let addr = self
.call
.mmap(0, actual_mmap_len)
.ok_or(XenClientError::new("failed to mmap address"))?;
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
if result != 0 {
return Err(XenClientError::new("mmap_batch call failed"));
}
let page = PhysicalPage {
pfn: u64::MAX,
ptr: addr,
count: num as u64,
};
debug!(
"alloc_mfn {:#x}+{:#x} at {:#x}",
page.pfn, page.count, page.ptr
);
self.pages.push(page);
Ok(addr)
}
pub fn unmap_all(&mut self) -> Result<(), XenClientError> { pub fn unmap_all(&mut self) -> Result<(), XenClientError> {
for page in &self.pages { for page in &self.pages {
unsafe { unsafe {
@ -131,11 +161,11 @@ impl PhysicalPages<'_> {
} }
pub fn unmap(&mut self, pfn: u64) -> Result<(), XenClientError> { pub fn unmap(&mut self, pfn: u64) -> Result<(), XenClientError> {
let page = self.pages.iter().find(|x| x.pfn == pfn); let page = self.pages.iter().enumerate().find(|(_, x)| x.pfn == pfn);
if page.is_none() { if page.is_none() {
return Err(XenClientError::new("unable to find page to unmap")); return Err(XenClientError::new("unable to find page to unmap"));
} }
let page = page.unwrap(); let (i, page) = page.unwrap();
unsafe { unsafe {
let err = munmap( let err = munmap(
@ -150,6 +180,7 @@ impl PhysicalPages<'_> {
if err != 0 { if err != 0 {
return Err(XenClientError::new("failed to munmap page")); return Err(XenClientError::new("failed to munmap page"));
} }
self.pages.remove(i);
} }
Ok(()) Ok(())
} }

View File

@ -121,3 +121,10 @@ pub const SUPERPAGE_2MB_SHIFT: u64 = 9;
pub const SUPERPAGE_2MB_NR_PFNS: u64 = 1u64 << SUPERPAGE_2MB_SHIFT; pub const SUPERPAGE_2MB_NR_PFNS: u64 = 1u64 << SUPERPAGE_2MB_SHIFT;
pub const VGCF_IN_KERNEL: u64 = 1 << 2; pub const VGCF_IN_KERNEL: u64 = 1 << 2;
pub const VGCF_ONLINE: u64 = 1 << 5; pub const VGCF_ONLINE: u64 = 1 << 5;
#[repr(C)]
pub struct GrantEntry {
pub flags: u16,
pub domid: u16,
pub frame: u32,
}