diff --git a/xencall/src/domctl.rs b/xencall/src/domctl.rs index 45e34fb..09c5871 100644 --- a/xencall/src/domctl.rs +++ b/xencall/src/domctl.rs @@ -1,8 +1,9 @@ use crate::sys::{ - ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, GetDomainInfo, HypercallInit, MaxMem, - MaxVcpus, HYPERVISOR_DOMCTL, XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN, - XEN_DOMCTL_GETDOMAININFO, XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_INTERFACE_VERSION, - XEN_DOMCTL_MAX_MEM, XEN_DOMCTL_MAX_VCPUS, + ArchDomainConfig, CreateDomain, DomCtl, DomCtlValue, DomCtlVcpuContext, GetDomainInfo, + HypercallInit, MaxMem, MaxVcpus, VcpuGuestContext, VcpuGuestContextAny, HYPERVISOR_DOMCTL, + XEN_DOMCTL_CREATEDOMAIN, XEN_DOMCTL_DESTROYDOMAIN, XEN_DOMCTL_GETDOMAININFO, + XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_INTERFACE_VERSION, XEN_DOMCTL_MAX_MEM, + XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_SETVCPUCONTEXT, }; use crate::{XenCall, XenCallError}; use log::trace; @@ -116,6 +117,39 @@ impl DomainControl<'_> { Ok(()) } + pub fn set_vcpu_context( + &self, + domid: u32, + vcpu: u32, + context: Option<&VcpuGuestContext>, + ) -> Result<(), XenCallError> { + trace!( + "domctl fd={} set_vcpu_context domid={} context={:?}", + self.call.handle.as_raw_fd(), + domid, + context, + ); + let mut wrapper = context.map(|ctx| VcpuGuestContextAny { value: *ctx }); + let mut domctl = DomCtl { + cmd: XEN_DOMCTL_SETVCPUCONTEXT, + interface_version: XEN_DOMCTL_INTERFACE_VERSION, + domid, + value: DomCtlValue { + vcpu_context: DomCtlVcpuContext { + vcpu, + ctx: if wrapper.is_some() { + addr_of_mut!(wrapper) as c_ulong + } else { + 0 + }, + }, + }, + }; + self.call + .hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?; + Ok(()) + } + pub fn hypercall_init(&self, domid: u32, gmfn: u64) -> Result<(), XenCallError> { trace!( "domctl fd={} hypercall_init domid={} max_vcpus={}", diff --git a/xencall/src/sys.rs b/xencall/src/sys.rs index 979ff13..e45ed7f 100644 --- a/xencall/src/sys.rs +++ b/xencall/src/sys.rs @@ -200,6 +200,13 @@ pub struct DomCtl { pub value: DomCtlValue, } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct DomCtlVcpuContext { + pub vcpu: u32, + pub ctx: u64, +} + #[repr(C)] #[derive(Copy, Clone)] pub union DomCtlValue { @@ -209,6 +216,7 @@ pub union DomCtlValue { pub max_cpus: MaxVcpus, pub hypercall_init: HypercallInit, pub pad: [u8; 128], + pub vcpu_context: DomCtlVcpuContext, } #[repr(C)] @@ -327,3 +335,113 @@ pub struct MultiCallEntry { } pub const XEN_MEM_POPULATE_PHYSMAP: u32 = 6; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct VcpuGuestContextFpuCtx { + pub x: [c_char; 512], +} + +impl Default for VcpuGuestContextFpuCtx { + fn default() -> Self { + VcpuGuestContextFpuCtx { x: [0; 512] } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub struct CpuUserRegs { + pub r15: u64, + pub r14: u64, + pub r13: u64, + pub r12: u64, + pub rbp: u64, + pub rbx: u64, + pub r11: u64, + pub r10: u64, + pub r9: u64, + pub r8: u64, + pub rax: u64, + pub rcx: u64, + pub rdx: u64, + pub rsi: u64, + pub rdi: u64, + pub error_code: u32, + pub entry_vector: u32, + pub rip: u64, + pub cs: u16, + _pad0: [u16; 1], + pub saved_upcall_mask: u8, + _pad1: [u8; 3], + pub rflags: u64, + pub rsp: u64, + pub ss: u16, + _pad2: [u16; 3], + pub es: u16, + _pad3: [u16; 3], + pub ds: u16, + _pad4: [u16; 3], + pub fs: u16, + _pad5: [u16; 3], + pub gs: u16, + _pad6: [u16; 3], +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub struct TrapInfo { + pub vector: u8, + pub flags: u8, + pub cs: u16, + pub address: u64, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct VcpuGuestContext { + pub fpu_ctx: VcpuGuestContextFpuCtx, + pub flags: c_ulong, + pub user_regs: CpuUserRegs, + pub trap_ctx: [TrapInfo; 256], + pub ldt_base: u64, + pub ldt_ents: u64, + pub gdt_frames: [u64; 16], + pub gdt_ents: u64, + pub kernel_ss: u64, + pub kernel_sp: u64, + pub ctrlreg: [u64; 8], + pub debugreg: [u64; 8], + pub syscall_callback_eip: u64, + pub vm_assist: u64, + pub fs_base: u64, + pub gs_base_kernel: u64, + pub gs_base_user: u64, +} + +impl Default for VcpuGuestContext { + fn default() -> Self { + VcpuGuestContext { + fpu_ctx: Default::default(), + flags: 0, + user_regs: Default::default(), + trap_ctx: [TrapInfo::default(); 256], + ldt_base: 0, + ldt_ents: 0, + gdt_frames: [0; 16], + gdt_ents: 0, + kernel_ss: 0, + kernel_sp: 0, + ctrlreg: [0; 8], + debugreg: [0; 8], + syscall_callback_eip: 0, + vm_assist: 0, + fs_base: 0, + gs_base_kernel: 0, + gs_base_user: 0, + } + } +} + +pub union VcpuGuestContextAny { + pub value: VcpuGuestContext, +} diff --git a/xenclient/examples/boot.rs b/xenclient/examples/boot.rs index b9a34a2..8965680 100644 --- a/xenclient/examples/boot.rs +++ b/xenclient/examples/boot.rs @@ -19,13 +19,24 @@ fn main() -> Result<(), XenClientError> { let call = XenCall::open()?; let domctl = DomainControl::new(&call); let domid = domctl.create_domain(CreateDomain::default())?; - println!("domain created: {:?}", domid); - let image_loader = ElfImageLoader::load_file_kernel(kernel_image_path.as_str())?; - let memctl = MemoryControl::new(&call); - let mut boot = BootSetup::new(&call, &domctl, &memctl, domid); - let mut state = boot.initialize(&image_loader, 512 * 1024)?; - boot.boot(&mut state, "debug")?; + let result = boot(domid, kernel_image_path.as_str(), &call, &domctl); domctl.destroy_domain(domid)?; + result?; println!("domain destroyed: {}", domid); Ok(()) } + +fn boot( + domid: u32, + kernel_image_path: &str, + call: &XenCall, + domctl: &DomainControl, +) -> Result<(), XenClientError> { + println!("domain created: {:?}", domid); + let image_loader = ElfImageLoader::load_file_kernel(kernel_image_path)?; + let memctl = MemoryControl::new(call); + let mut boot = BootSetup::new(call, domctl, &memctl, domid); + let mut state = boot.initialize(&image_loader, 512 * 1024)?; + boot.boot(&mut state, "debug")?; + Ok(()) +} diff --git a/xenclient/src/boot.rs b/xenclient/src/boot.rs index 923ddec..2aed541 100644 --- a/xenclient/src/boot.rs +++ b/xenclient/src/boot.rs @@ -15,6 +15,7 @@ use std::ffi::c_void; use std::slice; use xencall::domctl::DomainControl; use xencall::memory::MemoryControl; +use xencall::sys::VcpuGuestContext; use xencall::XenCall; pub trait BootImageLoader { @@ -26,6 +27,7 @@ pub const XEN_UNSET_ADDR: u64 = -1i64 as u64; #[derive(Debug)] pub struct BootImageInfo { + pub start: u64, pub virt_base: u64, pub virt_kstart: u64, pub virt_kend: u64, @@ -70,6 +72,7 @@ pub struct BootState { pub boot_stack_segment: DomainSegment, pub page_table_segment: DomainSegment, pub page_table: PageTable, + pub image_info: BootImageInfo, } impl BootSetup<'_> { @@ -195,11 +198,14 @@ impl BootSetup<'_> { Ok(()) } - fn _initialize_hypercall(&mut self, image_info: BootImageInfo) -> Result<(), XenClientError> { - if image_info.virt_hypercall != XEN_UNSET_ADDR { - self.domctl - .hypercall_init(self.domid, image_info.virt_hypercall)?; + 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.domctl.hypercall_init(self.domid, mfn)?; Ok(()) } @@ -213,6 +219,7 @@ impl BootSetup<'_> { self.initialize_memory(memkb)?; let image_info = image_loader.parse()?; + self.virt_alloc_end = image_info.virt_base; let kernel_segment = self.load_kernel_segment(image_loader, &image_info)?; let start_info_segment = self.alloc_page()?; let xenstore_segment = self.alloc_page()?; @@ -227,12 +234,37 @@ impl BootSetup<'_> { boot_stack_segment, page_table_segment, page_table, + image_info, }) } pub fn boot(&mut self, state: &mut BootState, cmdline: &str) -> Result<(), XenClientError> { self.setup_page_tables(state)?; self.setup_start_info(state, cmdline); + self.setup_hypercall_page(&state.image_info)?; + + 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 = (1 << 2) | (1 << 5); + let cr3_pfn = self.phys.p2m[state.page_table_segment.pfn as usize]; + vcpu.ctrlreg[3] = cr3_pfn << 12; + vcpu.user_regs.ds = 0xe021; + vcpu.user_regs.es = 0xe021; + vcpu.user_regs.fs = 0xe021; + vcpu.user_regs.gs = 0xe021; + vcpu.user_regs.ss = 0xe02b; + vcpu.user_regs.cs = 0xe019; + vcpu.kernel_ss = vcpu.user_regs.ss as u64; + vcpu.kernel_sp = vcpu.user_regs.rsp; + let _vcpu = vcpu; + self.domctl.set_vcpu_context(self.domid, 0, None)?; Ok(()) } @@ -263,7 +295,8 @@ impl BootSetup<'_> { >> (X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT); let p_e = (min(to, lvl.to) - from) >> (X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT); - let mut pfn = (max(from, lvl.from) - from) >> ((X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT) + lvl.pfn); + let mut pfn = (max(from, lvl.from) - from) + >> ((X86_PAGE_SHIFT + lvl_idx as u64 * X86_PGTABLE_LEVEL_SHIFT) + lvl.pfn); for p in p_s..p_e + 1 { unsafe { diff --git a/xenclient/src/elfloader.rs b/xenclient/src/elfloader.rs index bd35089..f8babc4 100644 --- a/xenclient/src/elfloader.rs +++ b/xenclient/src/elfloader.rs @@ -265,24 +265,13 @@ impl BootImageLoader for ElfImageLoader { )); } - let virt_base = 0; - - let _paddr_offset = if paddr_offset == XEN_UNSET_ADDR { - 0 - } else { - paddr_offset - }; - - let virt_offset = 0; + let virt_offset = virt_base - paddr_offset; let virt_kstart = start + virt_offset; let virt_kend = end + virt_offset; - let virt_entry = if entry == XEN_UNSET_ADDR { - elf.ehdr.e_entry - } else { - entry - }; + let virt_entry = entry; Ok(BootImageInfo { + start, virt_base, virt_kstart, virt_kend, @@ -307,7 +296,7 @@ impl BootImageLoader for ElfImageLoader { let paddr = header.p_paddr; let filesz = header.p_filesz; let memsz = header.p_memsz; - let base_offset = paddr - image_info.virt_kstart; + let base_offset = paddr - image_info.start; let data = elf.segment_data(&header)?; let segment_dst = &mut dst[base_offset as usize..]; let copy_slice = &data[0..filesz as usize]; diff --git a/xenclient/src/x86.rs b/xenclient/src/x86.rs index 619d893..ba51644 100644 --- a/xenclient/src/x86.rs +++ b/xenclient/src/x86.rs @@ -8,8 +8,7 @@ pub const X86_PGTABLE_LEVELS: u64 = 4; pub const X86_PGTABLE_LEVEL_SHIFT: u64 = 9; #[repr(C)] -#[derive(Debug, Clone)] -#[derive(Default)] +#[derive(Debug, Clone, Default)] pub struct PageTableMappingLevel { pub from: u64, pub to: u64, @@ -18,8 +17,7 @@ pub struct PageTableMappingLevel { } #[repr(C)] -#[derive(Debug, Clone)] -#[derive(Default)] +#[derive(Debug, Clone, Default)] pub struct PageTableMapping { pub area: PageTableMappingLevel, pub levels: [PageTableMappingLevel; X86_PGTABLE_LEVELS as usize], @@ -28,19 +26,12 @@ pub struct PageTableMapping { pub const X86_PAGE_TABLE_MAX_MAPPINGS: usize = 2; #[repr(C)] -#[derive(Debug, Clone)] -#[derive(Default)] +#[derive(Debug, Clone, Default)] pub struct PageTable { pub mappings_count: usize, pub mappings: [PageTableMapping; X86_PAGE_TABLE_MAX_MAPPINGS], } - - - - - - #[repr(C)] #[derive(Debug)] pub struct StartInfoConsole {