xenclient: convert to using thiserror

This commit is contained in:
Alex Zenla 2024-01-30 18:02:32 -08:00
parent 890a8ee63f
commit 2eb287bf07
No known key found for this signature in database
GPG Key ID: 067B238899B51269
6 changed files with 78 additions and 150 deletions

View File

@ -182,7 +182,7 @@ impl BootSetup<'_> {
let addr = self let addr = self
.call .call
.mmap(0, 1 << XEN_PAGE_SHIFT) .mmap(0, 1 << XEN_PAGE_SHIFT)
.ok_or(Error::new("failed to mmap for resource"))?; .ok_or(Error::MmapFailed)?;
self.call.map_resource(self.domid, 1, 0, 0, 1, addr)?; self.call.map_resource(self.domid, 1, 0, 0, 1, addr)?;
let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) }; let entries = unsafe { slice::from_raw_parts_mut(addr as *mut GrantEntry, 2) };
entries[0].flags = 1 << 0; entries[0].flags = 1 << 0;
@ -194,7 +194,7 @@ impl BootSetup<'_> {
unsafe { unsafe {
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT); let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
if result != 0 { if result != 0 {
return Err(Error::new("failed to unmap resource")); return Err(Error::UnmapFailed);
} }
} }
Ok(()) Ok(())
@ -294,15 +294,11 @@ impl BootSetup<'_> {
fn alloc_padding_pages(&mut self, arch: &mut dyn ArchBootSetup, boundary: u64) -> Result<()> { fn alloc_padding_pages(&mut self, arch: &mut dyn ArchBootSetup, boundary: u64) -> Result<()> {
if (boundary & (arch.page_size() - 1)) != 0 { if (boundary & (arch.page_size() - 1)) != 0 {
return Err(Error::new( return Err(Error::MemorySetupFailed);
format!("segment boundary isn't page aligned: {:#x}", boundary).as_str(),
));
} }
if boundary < self.virt_alloc_end { if boundary < self.virt_alloc_end {
return Err(Error::new( return Err(Error::MemorySetupFailed);
format!("segment boundary too low: {:#x})", boundary).as_str(),
));
} }
let pages = (boundary - self.virt_alloc_end) / arch.page_size(); let pages = (boundary - self.virt_alloc_end) / arch.page_size();
self.chk_alloc_pages(arch, pages)?; self.chk_alloc_pages(arch, pages)?;
@ -314,13 +310,7 @@ impl BootSetup<'_> {
|| self.pfn_alloc_end > self.total_pages || self.pfn_alloc_end > self.total_pages
|| pages > self.total_pages - self.pfn_alloc_end || pages > self.total_pages - self.pfn_alloc_end
{ {
return Err(Error::new( return Err(Error::MemorySetupFailed);
format!(
"segment too large: pages={} total_pages={} pfn_alloc_end={}",
pages, self.total_pages, self.pfn_alloc_end
)
.as_str(),
));
} }
self.pfn_alloc_end += pages; self.pfn_alloc_end += pages;

View File

@ -8,35 +8,16 @@ use crate::Error;
use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE}; use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE};
use elf::endian::AnyEndian; use elf::endian::AnyEndian;
use elf::note::Note; use elf::note::Note;
use elf::{ElfBytes, ParseError}; use elf::ElfBytes;
use flate2::bufread::GzDecoder; use flate2::bufread::GzDecoder;
use log::debug; use log::debug;
use memchr::memmem::find_iter; use memchr::memmem::find_iter;
use slice_copy::copy; use slice_copy::copy;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::{FromVecWithNulError, IntoStringError};
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::mem::size_of; use std::mem::size_of;
use xz2::bufread::XzDecoder; use xz2::bufread::XzDecoder;
impl From<ParseError> for Error {
fn from(value: ParseError) -> Self {
Error::new(value.to_string().as_str())
}
}
impl From<FromVecWithNulError> for Error {
fn from(value: FromVecWithNulError) -> Self {
Error::new(value.to_string().as_str())
}
}
impl From<IntoStringError> for Error {
fn from(value: IntoStringError) -> Self {
Error::new(value.to_string().as_str())
}
}
pub struct ElfImageLoader { pub struct ElfImageLoader {
data: Vec<u8>, data: Vec<u8>,
} }
@ -146,9 +127,7 @@ impl ElfImageLoader {
} }
} }
Err(Error::new( Err(Error::ElfCompressionUnknown)
"Unable to parse kernel image: unknown compression type",
))
} }
} }
@ -159,9 +138,7 @@ struct ElfNoteValue {
impl BootImageLoader for ElfImageLoader { impl BootImageLoader for ElfImageLoader {
fn parse(&self) -> Result<BootImageInfo> { fn parse(&self) -> Result<BootImageInfo> {
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?; let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
let headers = elf.section_headers().ok_or(Error::new( let headers = elf.section_headers().ok_or(Error::ElfInvalidImage)?;
"Unable to parse kernel image: section headers not found.",
))?;
let mut linux_notes: HashMap<u64, Vec<u8>> = HashMap::new(); let mut linux_notes: HashMap<u64, Vec<u8>> = HashMap::new();
let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new(); let mut xen_notes: HashMap<u64, ElfNoteValue> = HashMap::new();
@ -198,46 +175,42 @@ impl BootImageLoader for ElfImageLoader {
} }
if linux_notes.is_empty() { if linux_notes.is_empty() {
return Err(Error::new( return Err(Error::ElfInvalidImage);
"Provided kernel does not appear to be a Linux kernel image.",
));
} }
if xen_notes.is_empty() { if xen_notes.is_empty() {
return Err(Error::new("Provided kernel does not have Xen support.")); return Err(Error::ElfXenSupportMissing);
} }
let paddr_offset = xen_notes let paddr_offset = xen_notes
.get(&XEN_ELFNOTE_PADDR_OFFSET) .get(&XEN_ELFNOTE_PADDR_OFFSET)
.ok_or(Error::new("Unable to find paddr_offset note in kernel."))? .ok_or(Error::ElfInvalidImage)?
.value; .value;
let virt_base = xen_notes let virt_base = xen_notes
.get(&XEN_ELFNOTE_VIRT_BASE) .get(&XEN_ELFNOTE_VIRT_BASE)
.ok_or(Error::new("Unable to find virt_base note in kernel."))? .ok_or(Error::ElfInvalidImage)?
.value; .value;
let entry = xen_notes let entry = xen_notes
.get(&XEN_ELFNOTE_ENTRY) .get(&XEN_ELFNOTE_ENTRY)
.ok_or(Error::new("Unable to find entry note in kernel."))? .ok_or(Error::ElfInvalidImage)?
.value; .value;
let virt_hypercall = xen_notes let virt_hypercall = xen_notes
.get(&XEN_ELFNOTE_HYPERCALL_PAGE) .get(&XEN_ELFNOTE_HYPERCALL_PAGE)
.ok_or(Error::new("Unable to find hypercall_page note in kernel."))? .ok_or(Error::ElfInvalidImage)?
.value; .value;
let init_p2m = xen_notes let init_p2m = xen_notes
.get(&XEN_ELFNOTE_INIT_P2M) .get(&XEN_ELFNOTE_INIT_P2M)
.ok_or(Error::new("Unable to find init_p2m note in kernel."))? .ok_or(Error::ElfInvalidImage)?
.value; .value;
let mod_start_pfn = xen_notes let mod_start_pfn = xen_notes
.get(&XEN_ELFNOTE_MOD_START_PFN) .get(&XEN_ELFNOTE_MOD_START_PFN)
.ok_or(Error::new("Unable to find mod_start_pfn note in kernel."))? .ok_or(Error::ElfInvalidImage)?
.value; .value;
let mut start: u64 = u64::MAX; let mut start: u64 = u64::MAX;
let mut end: u64 = 0; let mut end: u64 = 0;
let segments = elf.segments().ok_or(Error::new( let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
"Unable to parse kernel image: segments not found.",
))?;
for header in segments { for header in segments {
if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 { if (header.p_type != PT_LOAD) || (header.p_flags & (PF_R | PF_W | PF_X)) == 0 {
@ -255,9 +228,7 @@ impl BootImageLoader for ElfImageLoader {
} }
if paddr_offset != XEN_UNSET_ADDR && virt_base == XEN_UNSET_ADDR { if paddr_offset != XEN_UNSET_ADDR && virt_base == XEN_UNSET_ADDR {
return Err(Error::new( return Err(Error::ElfInvalidImage);
"Unable to load kernel image: paddr_offset set but virt_base is unset.",
));
} }
let virt_offset = virt_base - paddr_offset; let virt_offset = virt_base - paddr_offset;
@ -280,9 +251,7 @@ impl BootImageLoader for ElfImageLoader {
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> { fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?; let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
let segments = elf.segments().ok_or(Error::new( let segments = elf.segments().ok_or(Error::ElfInvalidImage)?;
"Unable to parse kernel image: segments not found.",
))?;
debug!( debug!(
"ElfImageLoader load dst={:#x} segments={}", "ElfImageLoader load dst={:#x} segments={}",

View File

@ -1,60 +1,39 @@
use std::fmt::{Display, Formatter}; use std::io;
use std::string::FromUtf8Error;
use xencall::XenCallError; #[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("io issue encountered")]
Io(#[from] io::Error),
#[error("xenstore issue encountered")]
XenStore(#[from] xenstore::error::Error),
#[error("xencall issue encountered")]
XenCall(#[from] xencall::XenCallError),
#[error("domain does not have a tty")]
TtyNotFound,
#[error("introducing the domain failed")]
IntroduceDomainFailed,
#[error("string conversion of a path failed")]
PathStringConversion,
#[error("parent of path not found")]
PathParentNotFound,
#[error("domain does not exist")]
DomainNonExistent,
#[error("elf parse failed")]
ElfParseFailed(#[from] elf::ParseError),
#[error("mmap failed")]
MmapFailed,
#[error("munmap failed")]
UnmapFailed,
#[error("memory setup failed")]
MemorySetupFailed,
#[error("populate physmap failed: wanted={0}, received={1}, input_extents={2}")]
PopulatePhysmapFailed(usize, usize, usize),
#[error("unknown elf compression method")]
ElfCompressionUnknown,
#[error("expected elf image format not found")]
ElfInvalidImage,
#[error("provided elf image does not contain xen support")]
ElfXenSupportMissing,
}
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
message: String,
}
impl Error {
pub fn new(msg: &str) -> Error {
Error {
message: msg.to_string(),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
&self.message
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::new(value.to_string().as_str())
}
}
impl From<xenstore::error::Error> for Error {
fn from(value: xenstore::error::Error) -> Self {
Error::new(value.to_string().as_str())
}
}
impl From<XenCallError> for Error {
fn from(value: XenCallError) -> Self {
Error::new(value.to_string().as_str())
}
}
impl From<FromUtf8Error> for Error {
fn from(value: FromUtf8Error) -> Self {
Error::new(value.to_string().as_str())
}
}
impl From<xenevtchn::error::Error> for Error {
fn from(value: xenevtchn::error::Error) -> Self {
Error::new(value.to_string().as_str())
}
}

View File

@ -98,7 +98,7 @@ impl XenClient {
let dom_path = self.store.get_domain_path(domid)?; let dom_path = self.store.get_domain_path(domid)?;
let vm_path = self.store.read_string(&format!("{}/vm", dom_path))?; let vm_path = self.store.read_string(&format!("{}/vm", dom_path))?;
if vm_path.is_empty() { if vm_path.is_empty() {
return Err(Error::new("cannot destroy domain that doesn't exist")); return Err(Error::DomainNonExistent);
} }
let mut backend_paths: Vec<String> = Vec::new(); let mut backend_paths: Vec<String> = Vec::new();
@ -161,12 +161,8 @@ impl XenClient {
} }
for path in &backend_removals { for path in &backend_removals {
let path = PathBuf::from(path); let path = PathBuf::from(path);
let parent = path let parent = path.parent().ok_or(Error::PathParentNotFound)?;
.parent() tx.rm(parent.to_str().ok_or(Error::PathStringConversion)?)?;
.ok_or(Error::new("unable to get parent of backend path"))?;
tx.rm(parent
.to_str()
.ok_or(Error::new("unable to convert parent to string"))?)?;
} }
tx.rm(&vm_path)?; tx.rm(&vm_path)?;
tx.rm(&dom_path)?; tx.rm(&dom_path)?;
@ -336,7 +332,7 @@ impl XenClient {
.store .store
.introduce_domain(domid, xenstore_mfn, xenstore_evtchn)? .introduce_domain(domid, xenstore_mfn, xenstore_evtchn)?
{ {
return Err(Error::new("failed to introduce domain")); return Err(Error::IntroduceDomainFailed);
} }
self.console_device_add( self.console_device_add(
&dom_path, &dom_path,
@ -570,7 +566,7 @@ impl XenClient {
.read_string_optional(&console_tty_path)? .read_string_optional(&console_tty_path)?
.unwrap_or("".to_string()); .unwrap_or("".to_string());
if tty.is_empty() { if tty.is_empty() {
return Err(Error::new(&format!("domain {} does not have a tty", domid))); return Err(Error::TtyNotFound);
} }
let read = OpenOptions::new().read(true).write(false).open(&tty)?; let read = OpenOptions::new().read(true).write(false).open(&tty)?;
let write = OpenOptions::new().read(false).write(true).open(&tty)?; let write = OpenOptions::new().read(false).write(true).open(&tty)?;

View File

@ -53,7 +53,7 @@ impl PhysicalPages<'_> {
} }
if pfn < page.pfn || (pfn + count) > page.pfn + page.count { if pfn < page.pfn || (pfn + count) > page.pfn + page.count {
return Err(Error::new("request overlaps allocated block")); return Err(Error::MemorySetupFailed);
} }
} else { } else {
if pfn < page.pfn { if pfn < page.pfn {
@ -69,9 +69,7 @@ impl PhysicalPages<'_> {
} }
if count == 0 { if count == 0 {
return Err(Error::new( return Err(Error::MemorySetupFailed);
"allocation is only allowed when a size is given",
));
} }
self.pfn_alloc(pfn, count) self.pfn_alloc(pfn, count)
@ -96,11 +94,11 @@ impl PhysicalPages<'_> {
let addr = self let addr = self
.call .call
.mmap(0, actual_mmap_len) .mmap(0, actual_mmap_len)
.ok_or(Error::new("failed to mmap address"))?; .ok_or(Error::MmapFailed)?;
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr); debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?; let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
if result != 0 { if result != 0 {
return Err(Error::new("mmap_batch call failed")); return Err(Error::MmapFailed);
} }
let page = PhysicalPage { let page = PhysicalPage {
pfn, pfn,
@ -126,11 +124,11 @@ impl PhysicalPages<'_> {
let addr = self let addr = self
.call .call
.mmap(0, actual_mmap_len) .mmap(0, actual_mmap_len)
.ok_or(Error::new("failed to mmap address"))?; .ok_or(Error::MmapFailed)?;
debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr); debug!("mapped {:#x} foreign bytes at {:#x}", actual_mmap_len, addr);
let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?; let result = self.call.mmap_batch(self.domid, num as u64, addr, pfns)?;
if result != 0 { if result != 0 {
return Err(Error::new("mmap_batch call failed")); return Err(Error::MmapFailed);
} }
let page = PhysicalPage { let page = PhysicalPage {
pfn: u64::MAX, pfn: u64::MAX,
@ -153,7 +151,7 @@ impl PhysicalPages<'_> {
(page.count << X86_PAGE_SHIFT) as usize, (page.count << X86_PAGE_SHIFT) as usize,
); );
if err != 0 { if err != 0 {
return Err(Error::new("failed to munmap all pages")); return Err(Error::UnmapFailed);
} }
} }
} }
@ -164,7 +162,7 @@ impl PhysicalPages<'_> {
pub fn unmap(&mut self, pfn: u64) -> Result<()> { pub fn unmap(&mut self, pfn: u64) -> Result<()> {
let page = self.pages.iter().enumerate().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(Error::new("unable to find page to unmap")); return Err(Error::MemorySetupFailed);
} }
let (i, page) = page.unwrap(); let (i, page) = page.unwrap();
@ -179,7 +177,7 @@ impl PhysicalPages<'_> {
page.ptr page.ptr
); );
if err != 0 { if err != 0 {
return Err(Error::new("failed to munmap page")); return Err(Error::UnmapFailed);
} }
self.pages.remove(i); self.pages.remove(i);
} }

View File

@ -203,19 +203,19 @@ impl X86BootSetup {
) -> Result<usize> { ) -> Result<usize> {
debug!("counting pgtables from={} to={} pfn={}", from, to, pfn); debug!("counting pgtables from={} to={} pfn={}", from, to, pfn);
if self.table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS { if self.table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS {
return Err(Error::new("too many mappings")); return Err(Error::MemorySetupFailed);
} }
let m = self.table.mappings_count; let m = self.table.mappings_count;
let pfn_end = pfn + ((to - from) >> X86_PAGE_SHIFT); let pfn_end = pfn + ((to - from) >> X86_PAGE_SHIFT);
if pfn_end >= setup.phys.p2m_size() { if pfn_end >= setup.phys.p2m_size() {
return Err(Error::new("not enough memory for initial mapping")); return Err(Error::MemorySetupFailed);
} }
for idx in 0..self.table.mappings_count { for idx in 0..self.table.mappings_count {
if from < self.table.mappings[idx].area.to && to > self.table.mappings[idx].area.from { if from < self.table.mappings[idx].area.to && to > self.table.mappings[idx].area.from {
return Err(Error::new("overlapping mappings")); return Err(Error::MemorySetupFailed);
} }
} }
let mut map = PageTableMapping::default(); let mut map = PageTableMapping::default();
@ -494,7 +494,7 @@ impl ArchBootSetup for X86BootSetup {
} }
if total != total_pages { if total != total_pages {
return Err(Error::new("page count mismatch while calculating pages")); return Err(Error::MemorySetupFailed);
} }
setup.total_pages = total; setup.total_pages = total;
@ -561,14 +561,10 @@ impl ArchBootSetup for X86BootSetup {
.populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?; .populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?;
if result.len() != allocsz as usize { if result.len() != allocsz as usize {
return Err(Error::new( return Err(Error::PopulatePhysmapFailed(
format!( allocsz as usize,
"failed to populate physmap: wanted={} received={} input_extents={}", result.len(),
allocsz, input_extent_starts.len(),
result.len(),
input_extent_starts.len()
)
.as_str(),
)); ));
} }