diff --git a/xencall/Cargo.toml b/xencall/Cargo.toml index 90a0030..8020031 100644 --- a/xencall/Cargo.toml +++ b/xencall/Cargo.toml @@ -33,3 +33,7 @@ path = "examples/domain_create.rs" [[example]] name = "xencall-version-capabilities" path = "examples/version_capabilities.rs" + +[[example]] +name = "xencall-vcpu-context" +path = "examples/vcpu_context.rs" diff --git a/xencall/examples/vcpu_context.rs b/xencall/examples/vcpu_context.rs new file mode 100644 index 0000000..b46e879 --- /dev/null +++ b/xencall/examples/vcpu_context.rs @@ -0,0 +1,12 @@ +use xencall::domctl::DomainControl; +use xencall::{XenCall, XenCallError}; + +fn main() -> Result<(), XenCallError> { + env_logger::init(); + + let call = XenCall::open()?; + let domctl: DomainControl = DomainControl::new(&call); + let context = domctl.get_vcpu_context(224, 0)?; + println!("{:?}", context); + Ok(()) +} diff --git a/xencall/src/domctl.rs b/xencall/src/domctl.rs index 53816a6..f24e622 100644 --- a/xencall/src/domctl.rs +++ b/xencall/src/domctl.rs @@ -2,8 +2,9 @@ use crate::sys::{ AddressSize, 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, XEN_DOMCTL_SET_ADDRESS_SIZE, + XEN_DOMCTL_GETVCPUCONTEXT, XEN_DOMCTL_HYPERCALL_INIT, XEN_DOMCTL_INTERFACE_VERSION, + XEN_DOMCTL_MAX_MEM, XEN_DOMCTL_MAX_VCPUS, XEN_DOMCTL_PAUSEDOMAIN, XEN_DOMCTL_SETVCPUCONTEXT, + XEN_DOMCTL_SET_ADDRESS_SIZE, }; use crate::{XenCall, XenCallError}; use log::trace; @@ -77,6 +78,23 @@ impl DomainControl<'_> { Ok(domctl.domid) } + pub fn pause_domain(&self, domid: u32) -> Result<(), XenCallError> { + trace!( + "domctl fd={} pause_domain domid={:?}", + self.call.handle.as_raw_fd(), + domid, + ); + let mut domctl = DomCtl { + cmd: XEN_DOMCTL_PAUSEDOMAIN, + interface_version: XEN_DOMCTL_INTERFACE_VERSION, + domid, + value: DomCtlValue { pad: [0; 128] }, + }; + self.call + .hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?; + Ok(()) + } + pub fn set_max_mem(&self, domid: u32, memkb: u64) -> Result<(), XenCallError> { trace!( "domctl fd={} set_max_mem domid={} memkb={}", @@ -137,6 +155,35 @@ impl DomainControl<'_> { Ok(()) } + pub fn get_vcpu_context( + &self, + domid: u32, + vcpu: u32, + ) -> Result { + trace!( + "domctl fd={} get_vcpu_context domid={}", + self.call.handle.as_raw_fd(), + domid, + ); + let mut wrapper = VcpuGuestContextAny { + value: VcpuGuestContext::default(), + }; + let mut domctl = DomCtl { + cmd: XEN_DOMCTL_GETVCPUCONTEXT, + interface_version: XEN_DOMCTL_INTERFACE_VERSION, + domid, + value: DomCtlValue { + vcpu_context: DomCtlVcpuContext { + vcpu, + ctx: addr_of_mut!(wrapper) as c_ulong, + }, + }, + }; + self.call + .hypercall1(HYPERVISOR_DOMCTL, addr_of_mut!(domctl) as c_ulong)?; + Ok(unsafe { wrapper.value }) + } + pub fn set_vcpu_context( &self, domid: u32, diff --git a/xencall/src/memory.rs b/xencall/src/memory.rs index 0e065ca..68ba896 100644 --- a/xencall/src/memory.rs +++ b/xencall/src/memory.rs @@ -3,11 +3,9 @@ use crate::sys::{ }; use crate::{XenCall, XenCallError}; +use log::{trace}; use std::ffi::c_ulong; use std::os::fd::AsRawFd; - -use libc::c_long; -use log::trace; use std::ptr::addr_of_mut; pub struct MemoryControl<'a> { @@ -29,8 +27,10 @@ impl MemoryControl<'_> { ) -> Result, XenCallError> { trace!("memory fd={} populate_physmap domid={} nr_extents={} extent_order={} mem_flags={} extent_starts={:?}", self.call.handle.as_raw_fd(), domid, nr_extents, extent_order, mem_flags, extent_starts); let mut extent_starts = extent_starts.to_vec(); + let ptr = extent_starts.as_mut_ptr(); + let mut reservation = MemoryReservation { - extent_start: extent_starts.as_mut_ptr() as c_ulong, + extent_start: ptr as c_ulong, nr_extents, extent_order, mem_flags, @@ -50,15 +50,13 @@ impl MemoryControl<'_> { ], }]; self.call.multicall(calls)?; - let code = calls[0].result as c_long; - if code < 0 { + let code = calls[0].result; + if code > !0xfff { return Err(XenCallError::new("failed to populate physmap")); } - if code as usize > extent_starts.len() { return Err(XenCallError::new("failed to populate physmap")); } - Ok(extent_starts[0..code as usize].to_vec()) } } diff --git a/xencall/src/sys.rs b/xencall/src/sys.rs index 2fd1b77..73c6966 100644 --- a/xencall/src/sys.rs +++ b/xencall/src/sys.rs @@ -313,7 +313,7 @@ pub struct HypercallInit { } pub const XEN_DOMCTL_INTERFACE_VERSION: u32 = 0x00000015; -pub const SECINITSID_DOMU: u32 = 13; +pub const SECINITSID_DOMU: u32 = 12; #[repr(C)] #[derive(Copy, Clone, Debug)] diff --git a/xenclient/examples/boot.rs b/xenclient/examples/boot.rs index 8965680..1548754 100644 --- a/xenclient/examples/boot.rs +++ b/xenclient/examples/boot.rs @@ -19,6 +19,8 @@ fn main() -> Result<(), XenClientError> { let call = XenCall::open()?; let domctl = DomainControl::new(&call); let domid = domctl.create_domain(CreateDomain::default())?; + domctl.pause_domain(domid)?; + domctl.set_max_vcpus(domid, 1)?; let result = boot(domid, kernel_image_path.as_str(), &call, &domctl); domctl.destroy_domain(domid)?; result?; diff --git a/xenclient/src/boot.rs b/xenclient/src/boot.rs index a060f0d..3d414fc 100644 --- a/xenclient/src/boot.rs +++ b/xenclient/src/boot.rs @@ -10,7 +10,8 @@ use crate::x86::{ }; use crate::XenClientError; use libc::{c_char, memset}; -use log::debug; +use log::{debug, trace}; +use slice_copy::copy; use std::cmp::{max, min}; use std::ffi::c_void; use std::slice; @@ -34,7 +35,7 @@ pub struct BootImageInfo { pub virt_kend: u64, pub virt_hypercall: u64, pub virt_entry: u64, - pub init_p2m: u64, + pub virt_p2m_base: u64, } pub struct BootSetup<'a> { @@ -72,6 +73,7 @@ pub struct BootState { pub xenstore_segment: DomainSegment, pub console_segment: DomainSegment, pub boot_stack_segment: DomainSegment, + pub p2m_segment: DomainSegment, pub page_table_segment: DomainSegment, pub page_table: PageTable, pub image_info: BootImageInfo, @@ -119,7 +121,7 @@ impl BootSetup<'_> { if total != page_count { return Err(XenClientError::new( - "Page count mismatch while calculating pages.", + "page count mismatch while calculating pages", )); } @@ -127,7 +129,7 @@ impl BootSetup<'_> { let mut p2m = vec![-1i64 as u64; p2m_size as usize]; for range in &vmemranges { - let mut extents = vec![0u64; SUPERPAGE_BATCH_SIZE as usize]; + 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; @@ -147,21 +149,21 @@ impl BootSetup<'_> { if pfn >= pfn_base_idx + (count << SUPERPAGE_2MB_SHIFT) { break; } - extents[j] = p2m[pfn as usize]; + extents_init[j] = p2m[pfn as usize]; pfn += SUPERPAGE_2MB_NR_PFNS; j += 1; } - let starts = self.memctl.populate_physmap( + let extents = self.memctl.populate_physmap( self.domid, count, SUPERPAGE_2MB_SHIFT as u32, 0, - extents.as_slice(), + extents_init.as_slice(), )?; pfn = pfn_base_idx; - for mfn in starts { + for mfn in extents { for k in 0..SUPERPAGE_2MB_NR_PFNS { p2m[pfn as usize] = mfn + k; pfn += 1; @@ -192,8 +194,7 @@ impl BootSetup<'_> { format!("failed to populate physmap: {:?}", result).as_str(), )); } - - p2m[p2m_idx] = result[0]; + copy(p2m.as_mut_slice(), result.as_slice()); j += allocsz; } } @@ -223,27 +224,31 @@ impl BootSetup<'_> { self.initialize_memory(memkb)?; 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 start_info_segment = self.alloc_page()?; let xenstore_segment = self.alloc_page()?; let console_segment = self.alloc_page()?; - let (page_table_segment, page_table) = self.alloc_page_tables(&image_info)?; + let mut page_table = PageTable::default(); + let page_table_segment = self.alloc_page_tables(&mut page_table, &image_info)?; let boot_stack_segment = self.alloc_page()?; if self.virt_pgtab_end > 0 { self.alloc_padding_pages(self.virt_pgtab_end)?; } + let p2m_segment = self.alloc_p2m_segment(&mut page_table, &image_info)?; let state = BootState { kernel_segment, start_info_segment, xenstore_segment, console_segment, boot_stack_segment, + p2m_segment, page_table_segment, page_table, image_info, }; - debug!("initialize state={:?}", state); + debug!("BootSetup initialize state={:?}", state); Ok(state) } @@ -264,12 +269,12 @@ impl BootSetup<'_> { vcpu.flags = VGCF_IN_KERNEL | VGCF_ONLINE; 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.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 = 0xe019; + vcpu.user_regs.cs = 0xe033; vcpu.kernel_ss = vcpu.user_regs.ss as u64; vcpu.kernel_sp = vcpu.user_regs.rsp; self.domctl.set_vcpu_context(self.domid, 0, Some(&vcpu))?; @@ -277,7 +282,15 @@ impl BootSetup<'_> { } fn setup_page_tables(&mut self, state: &mut BootState) -> Result<(), XenClientError> { - for lvl_idx in (0usize..3usize).rev() { + let p2m_guest = unsafe { + slice::from_raw_parts_mut( + state.p2m_segment.addr as *mut u64, + state.p2m_segment.size as usize, + ) + }; + copy(p2m_guest, &self.phys.p2m); + + for lvl_idx in (0usize..X86_PGTABLE_LEVELS as usize).rev() { for map_idx_1 in 0usize..state.page_table.mappings_count { let map1 = &state.page_table.mappings[map_idx_1]; let from = map1.levels[lvl_idx].from; @@ -337,7 +350,7 @@ impl BootSetup<'_> { (*info).nr_p2m_frames = 0; (*info).flags = 0; (*info).store_evtchn = 0; - (*info).store_mfn = 0; + (*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 = 0; (*info).mod_start = 0; @@ -346,7 +359,7 @@ impl BootSetup<'_> { (*info).cmdline[i] = c as c_char; (*info).cmdline[MAX_GUEST_CMDLINE - 1] = 0; } - debug!("BootSetup setup_start_info={:?}", *info); + trace!("BootSetup setup_start_info start_info={:?}", *info); } Ok(()) } @@ -361,10 +374,6 @@ impl BootSetup<'_> { image_info.virt_kend - image_info.virt_kstart, )?; let kernel_segment_ptr = kernel_segment.addr as *mut u8; - debug!( - "BootSetup initialize kernel_segment ptr={:#x}", - kernel_segment_ptr as u64 - ); let kernel_segment_slice = unsafe { slice::from_raw_parts_mut(kernel_segment_ptr, kernel_segment.size as usize) }; image_loader.load(image_info, kernel_segment_slice)?; @@ -399,7 +408,7 @@ impl BootSetup<'_> { map.area.from = from & X86_VIRT_MASK; map.area.to = to & X86_VIRT_MASK; - for lvl_index in (0usize..3usize).rev() { + for lvl_index in (0usize..X86_PGTABLE_LEVELS as usize).rev() { let lvl = &mut map.levels[lvl_index]; lvl.pfn = self.pfn_alloc_end + map.area.pgtables as u64; if lvl_index as u64 == X86_PGTABLE_LEVELS - 1 { @@ -443,7 +452,7 @@ impl BootSetup<'_> { } debug!( - "count_pgtables {:#x}/{}: {:#x} -> {:#x}, {} tables", + "BootSetup count_pgtables {:#x}/{}: {:#x} -> {:#x}, {} tables", mask, bits, lvl.from, lvl.to, lvl.pgtables ); map.area.pgtables += lvl.pgtables; @@ -451,20 +460,47 @@ impl BootSetup<'_> { Ok(()) } + fn alloc_p2m_segment( + &mut self, + page_table: &mut PageTable, + image_info: &BootImageInfo, + ) -> Result { + let mut p2m_alloc_size = + ((self.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1); + let from = image_info.virt_p2m_base; + let to = from + p2m_alloc_size - 1; + self.count_page_tables(page_table, from, to, self.pfn_alloc_end)?; + + let pgtables: usize; + { + let map = &mut page_table.mappings[page_table.mappings_count]; + 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 alloc_page_tables( &mut self, + table: &mut PageTable, image_info: &BootImageInfo, - ) -> Result<(DomainSegment, PageTable), XenClientError> { - let mut table = PageTable::default(); + ) -> Result { let mut extra_pages = 1; extra_pages += (512 * 1024) / X86_PAGE_SIZE; let mut pages = extra_pages; + let nr_mappings = table.mappings_count; let mut try_virt_end: u64; loop { try_virt_end = (self.virt_alloc_end + pages * X86_PAGE_SIZE) | ((1 << 22) - 1); - self.count_page_tables(&mut table, image_info.virt_base, try_virt_end, 0)?; - pages = table.mappings[0].area.pgtables as u64 + extra_pages; + self.count_page_tables(table, image_info.virt_base, try_virt_end, 0)?; + pages = table.mappings[nr_mappings].area.pgtables as u64 + extra_pages; if self.virt_alloc_end + pages * X86_PAGE_SIZE <= try_virt_end + 1 { break; } @@ -472,18 +508,17 @@ impl BootSetup<'_> { let segment: DomainSegment; { - let map = &mut table.mappings[table.mappings_count]; + let map = &mut table.mappings[nr_mappings]; map.area.pfn = 0; table.mappings_count += 1; self.virt_pgtab_end = try_virt_end + 1; - segment = - self.alloc_segment(0, (map.area.pgtables as u64 * X86_PAGE_SIZE) + extra_pages)?; + segment = self.alloc_segment(0, map.area.pgtables as u64 * X86_PAGE_SIZE)?; } debug!( "BootSetup alloc_page_tables table={:?} segment={:?}", table, segment ); - Ok((segment, table)) + Ok(segment) } fn alloc_segment(&mut self, start: u64, size: u64) -> Result { @@ -513,7 +548,7 @@ impl BootSetup<'_> { } segment._vend = self.virt_alloc_end; debug!( - "alloc_segment {:#x} -> {:#x} (pfn {:#x} + {:#x} pages)", + "BootSetup alloc_segment {:#x} -> {:#x} (pfn {:#x} + {:#x} pages)", start, segment._vend, segment.pfn, pages ); Ok(segment) @@ -524,7 +559,7 @@ impl BootSetup<'_> { let pfn = self.pfn_alloc_end; self.chk_alloc_pages(1)?; - debug!("alloc_page {:#x} (pfn {:#x})", start, pfn); + debug!("BootSetup alloc_page {:#x} (pfn {:#x})", start, pfn); Ok(DomainSegment { vstart: start, _vend: (start + X86_PAGE_SIZE) - 1, diff --git a/xenclient/src/elfloader.rs b/xenclient/src/elfloader.rs index f8babc4..d038c22 100644 --- a/xenclient/src/elfloader.rs +++ b/xenclient/src/elfloader.rs @@ -270,15 +270,16 @@ impl BootImageLoader for ElfImageLoader { let virt_kend = end + virt_offset; let virt_entry = entry; - Ok(BootImageInfo { + let image_info = BootImageInfo { start, virt_base, virt_kstart, virt_kend, virt_hypercall, virt_entry, - init_p2m, - }) + virt_p2m_base: init_p2m, + }; + Ok(image_info) } fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError> {