use crate::error::Result; use crate::sys::XEN_PAGE_SHIFT; use crate::Error; use libc::munmap; use log::debug; use nix::errno::Errno; use std::ffi::c_void; use std::slice; use xencall::sys::MmapEntry; use xencall::XenCall; #[derive(Debug, Clone)] pub struct PhysicalPage { pfn: u64, pub ptr: u64, count: u64, } pub struct PhysicalPages { page_shift: u64, domid: u32, pub p2m: Vec, call: XenCall, pages: Vec, } impl PhysicalPages { pub fn new(call: XenCall, domid: u32, page_shift: u64) -> PhysicalPages { PhysicalPages { page_shift, domid, p2m: Vec::new(), call, pages: Vec::new(), } } pub fn load_p2m(&mut self, p2m: Vec) { self.p2m = p2m; } pub fn p2m_size(&mut self) -> u64 { self.p2m.len() as u64 } pub async fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result { for page in &self.pages { if pfn >= page.pfn + page.count { continue; } if count > 0 { if (pfn + count) <= page.pfn { continue; } if pfn < page.pfn || (pfn + count) > page.pfn + page.count { return Err(Error::MemorySetupFailed("pfn is out of range")); } } else { if pfn < page.pfn { continue; } if pfn >= page.pfn + page.count { continue; } } return Ok(page.ptr + ((pfn - page.pfn) << self.page_shift)); } if count == 0 { return Err(Error::MemorySetupFailed("page count is zero")); } self.pfn_alloc(pfn, count).await } async fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result { let mut entries = vec![MmapEntry::default(); count as usize]; for (i, entry) in entries.iter_mut().enumerate() { if !self.p2m.is_empty() { entry.mfn = self.p2m[pfn as usize + i]; } else { entry.mfn = pfn + i as u64; } } let chunk_size = 1 << XEN_PAGE_SHIFT; let num_per_entry = chunk_size >> XEN_PAGE_SHIFT; let num = num_per_entry * count as usize; let mut pfns = vec![u64::MAX; num]; for i in 0..count as usize { for j in 0..num_per_entry { pfns[i * num_per_entry + j] = entries[i].mfn + j as u64; } } let actual_mmap_len = (num as u64) << XEN_PAGE_SHIFT; let addr = self .call .mmap(0, actual_mmap_len) .await .ok_or(Error::MmapFailed)?; debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr); let result = self .call .mmap_batch(self.domid, num as u64, addr, pfns) .await?; if result != 0 { return Err(Error::MmapFailed); } let page = PhysicalPage { pfn, ptr: addr, count, }; debug!( "alloc_pfn {:#x}+{:#x} at {:#x}", page.pfn, page.count, page.ptr ); self.pages.push(page); Ok(addr) } pub async fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result { let count = (size >> XEN_PAGE_SHIFT) as usize; let mut entries = vec![MmapEntry::default(); count]; for (i, entry) in entries.iter_mut().enumerate() { entry.mfn = mfn + i as u64; } let chunk_size = 1 << XEN_PAGE_SHIFT; let num_per_entry = chunk_size >> XEN_PAGE_SHIFT; let num = num_per_entry * count; let mut pfns = vec![u64::MAX; num]; for i in 0..count { for j in 0..num_per_entry { pfns[i * num_per_entry + j] = entries[i].mfn + j as u64; } } let actual_mmap_len = (num as u64) << XEN_PAGE_SHIFT; let addr = self .call .mmap(0, actual_mmap_len) .await .ok_or(Error::MmapFailed)?; debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr); let result = self .call .mmap_batch(self.domid, num as u64, addr, pfns) .await?; if result != 0 { return Err(Error::MmapFailed); } let page = PhysicalPage { pfn: mfn, ptr: addr, count: count as u64, }; debug!( "alloc_mfn {:#x}+{:#x} at {:#x}", page.pfn, page.count, page.ptr ); self.pages.push(page); Ok(addr) } pub async fn clear_pages(&mut self, pfn: u64, count: u64) -> Result<()> { let ptr = self.pfn_to_ptr(pfn, count).await?; let slice = unsafe { slice::from_raw_parts_mut(ptr as *mut u8, (count * (1 << self.page_shift)) as usize) }; slice.fill(0); Ok(()) } pub fn unmap_all(&mut self) -> Result<()> { for page in &self.pages { unsafe { let err = munmap( page.ptr as *mut c_void, (page.count << self.page_shift) as usize, ); if err != 0 { return Err(Error::UnmapFailed(Errno::from_raw(err))); } } } self.pages.clear(); Ok(()) } pub fn unmap(&mut self, pfn: u64) -> Result<()> { let page = self.pages.iter().enumerate().find(|(_, x)| x.pfn == pfn); if page.is_none() { return Err(Error::MemorySetupFailed("cannot unmap missing page")); } let (i, page) = page.unwrap(); unsafe { let err = munmap( page.ptr as *mut c_void, (page.count << self.page_shift) as usize, ); debug!( "unmapped {:#x} foreign bytes at {:#x}", (page.count << self.page_shift) as usize, page.ptr ); if err != 0 { return Err(Error::UnmapFailed(Errno::from_raw(err))); } self.pages.remove(i); } Ok(()) } }