mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-02 12:50:54 +00:00
xenclient: move error to it's own mod
This commit is contained in:
parent
eec213c712
commit
890a8ee63f
@ -5,6 +5,7 @@ edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
elf = { workspace = true }
|
||||
flate2 = { workspace = true }
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::{env, process};
|
||||
use xenclient::{DomainConfig, XenClient, XenClientError};
|
||||
use xenclient::error::Result;
|
||||
use xenclient::{DomainConfig, XenClient};
|
||||
|
||||
fn main() -> Result<(), XenClientError> {
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::error::Result;
|
||||
use crate::mem::PhysicalPages;
|
||||
use crate::sys::{GrantEntry, XEN_PAGE_SHIFT};
|
||||
use crate::XenClientError;
|
||||
use crate::Error;
|
||||
use libc::munmap;
|
||||
use log::debug;
|
||||
use slice_copy::copy;
|
||||
@ -10,8 +11,8 @@ use std::slice;
|
||||
use xencall::XenCall;
|
||||
|
||||
pub trait BootImageLoader {
|
||||
fn parse(&self) -> Result<BootImageInfo, XenClientError>;
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError>;
|
||||
fn parse(&self) -> Result<BootImageInfo>;
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()>;
|
||||
}
|
||||
|
||||
pub const XEN_UNSET_ADDR: u64 = -1i64 as u64;
|
||||
@ -77,11 +78,7 @@ impl BootSetup<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_memory(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
total_pages: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn initialize_memory(&mut self, arch: &mut dyn ArchBootSetup, total_pages: u64) -> Result<()> {
|
||||
self.call.set_address_size(self.domid, 64)?;
|
||||
arch.meminit(self, total_pages)?;
|
||||
Ok(())
|
||||
@ -94,7 +91,7 @@ impl BootSetup<'_> {
|
||||
initrd: &[u8],
|
||||
max_vcpus: u32,
|
||||
mem_mb: u64,
|
||||
) -> Result<BootState, XenClientError> {
|
||||
) -> Result<BootState> {
|
||||
debug!(
|
||||
"BootSetup initialize max_vcpus={:?} mem_mb={:?}",
|
||||
max_vcpus, mem_mb
|
||||
@ -164,7 +161,7 @@ impl BootSetup<'_> {
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
state: &mut BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
let domain_info = self.call.get_domain_info(self.domid)?;
|
||||
let shared_info_frame = domain_info.shared_info_frame;
|
||||
state.shared_info_frame = shared_info_frame;
|
||||
@ -179,13 +176,13 @@ impl BootSetup<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gnttab_seed(&mut self, state: &mut BootState) -> Result<(), XenClientError> {
|
||||
fn gnttab_seed(&mut self, state: &mut BootState) -> Result<()> {
|
||||
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
|
||||
.call
|
||||
.mmap(0, 1 << XEN_PAGE_SHIFT)
|
||||
.ok_or(XenClientError::new("failed to mmap for resource"))?;
|
||||
.ok_or(Error::new("failed to mmap for resource"))?;
|
||||
self.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;
|
||||
@ -197,7 +194,7 @@ impl BootSetup<'_> {
|
||||
unsafe {
|
||||
let result = munmap(addr as *mut c_void, 1 << XEN_PAGE_SHIFT);
|
||||
if result != 0 {
|
||||
return Err(XenClientError::new("failed to unmap resource"));
|
||||
return Err(Error::new("failed to unmap resource"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -208,7 +205,7 @@ impl BootSetup<'_> {
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
image_loader: &dyn BootImageLoader,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
) -> Result<DomainSegment> {
|
||||
let kernel_segment = self.alloc_segment(
|
||||
arch,
|
||||
image_info.virt_kstart,
|
||||
@ -234,7 +231,7 @@ impl BootSetup<'_> {
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
start: u64,
|
||||
size: u64,
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
) -> Result<DomainSegment> {
|
||||
if start > 0 {
|
||||
self.alloc_padding_pages(arch, start)?;
|
||||
}
|
||||
@ -268,10 +265,7 @@ impl BootSetup<'_> {
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_page(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
fn alloc_page(&mut self, arch: &mut dyn ArchBootSetup) -> Result<DomainSegment> {
|
||||
let start = self.virt_alloc_end;
|
||||
let pfn = self.pfn_alloc_end;
|
||||
|
||||
@ -291,26 +285,22 @@ impl BootSetup<'_> {
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
buffer: &[u8],
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
) -> Result<DomainSegment> {
|
||||
let segment = self.alloc_segment(arch, 0, buffer.len() as u64)?;
|
||||
let slice = unsafe { slice::from_raw_parts_mut(segment.addr as *mut u8, buffer.len()) };
|
||||
copy(slice, buffer);
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn alloc_padding_pages(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
boundary: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn alloc_padding_pages(&mut self, arch: &mut dyn ArchBootSetup, boundary: u64) -> Result<()> {
|
||||
if (boundary & (arch.page_size() - 1)) != 0 {
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
format!("segment boundary isn't page aligned: {:#x}", boundary).as_str(),
|
||||
));
|
||||
}
|
||||
|
||||
if boundary < self.virt_alloc_end {
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
format!("segment boundary too low: {:#x})", boundary).as_str(),
|
||||
));
|
||||
}
|
||||
@ -319,16 +309,12 @@ impl BootSetup<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn chk_alloc_pages(
|
||||
&mut self,
|
||||
arch: &mut dyn ArchBootSetup,
|
||||
pages: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn chk_alloc_pages(&mut self, arch: &mut dyn ArchBootSetup, pages: u64) -> Result<()> {
|
||||
if pages > self.total_pages
|
||||
|| self.pfn_alloc_end > self.total_pages
|
||||
|| pages > self.total_pages - self.pfn_alloc_end
|
||||
{
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
format!(
|
||||
"segment too large: pages={} total_pages={} pfn_alloc_end={}",
|
||||
pages, self.total_pages, self.pfn_alloc_end
|
||||
@ -351,44 +337,32 @@ pub trait ArchBootSetup {
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment, XenClientError>;
|
||||
) -> Result<DomainSegment>;
|
||||
|
||||
fn alloc_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment, XenClientError>;
|
||||
) -> Result<DomainSegment>;
|
||||
|
||||
fn setup_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &mut BootState,
|
||||
) -> Result<(), XenClientError>;
|
||||
fn setup_page_tables(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
|
||||
fn setup_start_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<(), XenClientError>;
|
||||
) -> Result<()>;
|
||||
|
||||
fn setup_shared_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
shared_info_frame: u64,
|
||||
) -> Result<(), XenClientError>;
|
||||
fn setup_shared_info(&mut self, setup: &mut BootSetup, shared_info_frame: u64) -> Result<()>;
|
||||
|
||||
fn setup_hypercall_page(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<(), XenClientError>;
|
||||
) -> Result<()>;
|
||||
|
||||
fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<(), XenClientError>;
|
||||
fn bootlate(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &mut BootState,
|
||||
) -> Result<(), XenClientError>;
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<(), XenClientError>;
|
||||
fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<()>;
|
||||
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()>;
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::boot::{BootImageInfo, BootImageLoader, XEN_UNSET_ADDR};
|
||||
use crate::error::Result;
|
||||
use crate::sys::{
|
||||
XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HYPERCALL_PAGE, XEN_ELFNOTE_INIT_P2M, XEN_ELFNOTE_MOD_START_PFN,
|
||||
XEN_ELFNOTE_PADDR_OFFSET, XEN_ELFNOTE_TYPES, XEN_ELFNOTE_VIRT_BASE,
|
||||
};
|
||||
use crate::XenClientError;
|
||||
use crate::Error;
|
||||
use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE};
|
||||
use elf::endian::AnyEndian;
|
||||
use elf::note::Note;
|
||||
@ -18,21 +19,21 @@ use std::io::{BufReader, Read};
|
||||
use std::mem::size_of;
|
||||
use xz2::bufread::XzDecoder;
|
||||
|
||||
impl From<ParseError> for XenClientError {
|
||||
impl From<ParseError> for Error {
|
||||
fn from(value: ParseError) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
Error::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromVecWithNulError> for XenClientError {
|
||||
impl From<FromVecWithNulError> for Error {
|
||||
fn from(value: FromVecWithNulError) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
Error::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IntoStringError> for XenClientError {
|
||||
impl From<IntoStringError> for Error {
|
||||
fn from(value: IntoStringError) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
Error::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,24 +81,24 @@ impl ElfImageLoader {
|
||||
ElfImageLoader { data }
|
||||
}
|
||||
|
||||
pub fn load_file(path: &str) -> Result<ElfImageLoader, XenClientError> {
|
||||
pub fn load_file(path: &str) -> Result<ElfImageLoader> {
|
||||
let data = std::fs::read(path)?;
|
||||
Ok(ElfImageLoader::new(data))
|
||||
}
|
||||
|
||||
pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader, XenClientError> {
|
||||
pub fn load_gz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
let buff = BufReader::new(data);
|
||||
let image = ElfImageLoader::read_one_stream(&mut GzDecoder::new(buff))?;
|
||||
Ok(ElfImageLoader::new(image))
|
||||
}
|
||||
|
||||
pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader, XenClientError> {
|
||||
pub fn load_xz(data: &[u8]) -> Result<ElfImageLoader> {
|
||||
let buff = BufReader::new(data);
|
||||
let image = ElfImageLoader::read_one_stream(&mut XzDecoder::new(buff))?;
|
||||
Ok(ElfImageLoader::new(image))
|
||||
}
|
||||
|
||||
fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>, XenClientError> {
|
||||
fn read_one_stream(read: &mut dyn Read) -> Result<Vec<u8>> {
|
||||
let mut result: Vec<u8> = Vec::new();
|
||||
let mut buffer = [0u8; 8192];
|
||||
|
||||
@ -113,24 +114,24 @@ impl ElfImageLoader {
|
||||
if !result.is_empty() {
|
||||
break;
|
||||
}
|
||||
return Err(XenClientError::from(error));
|
||||
return Err(Error::from(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn load_file_gz(path: &str) -> Result<ElfImageLoader, XenClientError> {
|
||||
pub fn load_file_gz(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
ElfImageLoader::load_gz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_xz(path: &str) -> Result<ElfImageLoader, XenClientError> {
|
||||
pub fn load_file_xz(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
ElfImageLoader::load_xz(file.as_slice())
|
||||
}
|
||||
|
||||
pub fn load_file_kernel(path: &str) -> Result<ElfImageLoader, XenClientError> {
|
||||
pub fn load_file_kernel(path: &str) -> Result<ElfImageLoader> {
|
||||
let file = std::fs::read(path)?;
|
||||
|
||||
for start in find_iter(file.as_slice(), &[0x1f, 0x8b]) {
|
||||
@ -145,7 +146,7 @@ impl ElfImageLoader {
|
||||
}
|
||||
}
|
||||
|
||||
Err(XenClientError::new(
|
||||
Err(Error::new(
|
||||
"Unable to parse kernel image: unknown compression type",
|
||||
))
|
||||
}
|
||||
@ -156,9 +157,9 @@ struct ElfNoteValue {
|
||||
}
|
||||
|
||||
impl BootImageLoader for ElfImageLoader {
|
||||
fn parse(&self) -> Result<BootImageInfo, XenClientError> {
|
||||
fn parse(&self) -> Result<BootImageInfo> {
|
||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||
let headers = elf.section_headers().ok_or(XenClientError::new(
|
||||
let headers = elf.section_headers().ok_or(Error::new(
|
||||
"Unable to parse kernel image: section headers not found.",
|
||||
))?;
|
||||
let mut linux_notes: HashMap<u64, Vec<u8>> = HashMap::new();
|
||||
@ -197,56 +198,44 @@ impl BootImageLoader for ElfImageLoader {
|
||||
}
|
||||
|
||||
if linux_notes.is_empty() {
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
"Provided kernel does not appear to be a Linux kernel image.",
|
||||
));
|
||||
}
|
||||
|
||||
if xen_notes.is_empty() {
|
||||
return Err(XenClientError::new(
|
||||
"Provided kernel does not have Xen support.",
|
||||
));
|
||||
return Err(Error::new("Provided kernel does not have Xen support."));
|
||||
}
|
||||
|
||||
let paddr_offset = xen_notes
|
||||
.get(&XEN_ELFNOTE_PADDR_OFFSET)
|
||||
.ok_or(XenClientError::new(
|
||||
"Unable to find paddr_offset note in kernel.",
|
||||
))?
|
||||
.ok_or(Error::new("Unable to find paddr_offset note in kernel."))?
|
||||
.value;
|
||||
let virt_base = xen_notes
|
||||
.get(&XEN_ELFNOTE_VIRT_BASE)
|
||||
.ok_or(XenClientError::new(
|
||||
"Unable to find virt_base note in kernel.",
|
||||
))?
|
||||
.ok_or(Error::new("Unable to find virt_base note in kernel."))?
|
||||
.value;
|
||||
let entry = xen_notes
|
||||
.get(&XEN_ELFNOTE_ENTRY)
|
||||
.ok_or(XenClientError::new("Unable to find entry note in kernel."))?
|
||||
.ok_or(Error::new("Unable to find entry note in kernel."))?
|
||||
.value;
|
||||
let virt_hypercall = xen_notes
|
||||
.get(&XEN_ELFNOTE_HYPERCALL_PAGE)
|
||||
.ok_or(XenClientError::new(
|
||||
"Unable to find hypercall_page note in kernel.",
|
||||
))?
|
||||
.ok_or(Error::new("Unable to find hypercall_page note in kernel."))?
|
||||
.value;
|
||||
let init_p2m = xen_notes
|
||||
.get(&XEN_ELFNOTE_INIT_P2M)
|
||||
.ok_or(XenClientError::new(
|
||||
"Unable to find init_p2m note in kernel.",
|
||||
))?
|
||||
.ok_or(Error::new("Unable to find init_p2m note in kernel."))?
|
||||
.value;
|
||||
let mod_start_pfn = xen_notes
|
||||
.get(&XEN_ELFNOTE_MOD_START_PFN)
|
||||
.ok_or(XenClientError::new(
|
||||
"Unable to find mod_start_pfn note in kernel.",
|
||||
))?
|
||||
.ok_or(Error::new("Unable to find mod_start_pfn note in kernel."))?
|
||||
.value;
|
||||
|
||||
let mut start: u64 = u64::MAX;
|
||||
let mut end: u64 = 0;
|
||||
|
||||
let segments = elf.segments().ok_or(XenClientError::new(
|
||||
let segments = elf.segments().ok_or(Error::new(
|
||||
"Unable to parse kernel image: segments not found.",
|
||||
))?;
|
||||
|
||||
@ -266,7 +255,7 @@ impl BootImageLoader for ElfImageLoader {
|
||||
}
|
||||
|
||||
if paddr_offset != XEN_UNSET_ADDR && virt_base == XEN_UNSET_ADDR {
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
"Unable to load kernel image: paddr_offset set but virt_base is unset.",
|
||||
));
|
||||
}
|
||||
@ -289,9 +278,9 @@ impl BootImageLoader for ElfImageLoader {
|
||||
Ok(image_info)
|
||||
}
|
||||
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<(), XenClientError> {
|
||||
fn load(&self, image_info: &BootImageInfo, dst: &mut [u8]) -> Result<()> {
|
||||
let elf = ElfBytes::<AnyEndian>::minimal_parse(self.data.as_slice())?;
|
||||
let segments = elf.segments().ok_or(XenClientError::new(
|
||||
let segments = elf.segments().ok_or(Error::new(
|
||||
"Unable to parse kernel image: segments not found.",
|
||||
))?;
|
||||
|
||||
|
60
xen/xenclient/src/error.rs
Normal file
60
xen/xenclient/src/error.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::string::FromUtf8Error;
|
||||
use xencall::XenCallError;
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
@ -1,24 +1,24 @@
|
||||
pub mod boot;
|
||||
pub mod elfloader;
|
||||
pub mod error;
|
||||
pub mod mem;
|
||||
pub mod sys;
|
||||
mod x86;
|
||||
pub mod x86;
|
||||
|
||||
use crate::boot::BootSetup;
|
||||
use crate::elfloader::ElfImageLoader;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::x86::X86BootSetup;
|
||||
use log::{trace, warn};
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use std::fs::{read, File, OpenOptions};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::string::FromUtf8Error;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
use xencall::sys::CreateDomain;
|
||||
use xencall::{XenCall, XenCallError};
|
||||
use xencall::XenCall;
|
||||
use xenstore::client::{
|
||||
XsPermission, XsdClient, XsdInterface, XS_PERM_NONE, XS_PERM_READ, XS_PERM_READ_WRITE,
|
||||
};
|
||||
@ -28,61 +28,6 @@ pub struct XenClient {
|
||||
call: XenCall,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XenClientError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl XenClientError {
|
||||
pub fn new(msg: &str) -> XenClientError {
|
||||
XenClientError {
|
||||
message: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for XenClientError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for XenClientError {
|
||||
fn description(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for XenClientError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<xenstore::error::Error> for XenClientError {
|
||||
fn from(value: xenstore::error::Error) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XenCallError> for XenClientError {
|
||||
fn from(value: XenCallError) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for XenClientError {
|
||||
fn from(value: FromUtf8Error) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<xenevtchn::error::Error> for XenClientError {
|
||||
fn from(value: xenevtchn::error::Error) -> Self {
|
||||
XenClientError::new(value.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlockDeviceRef {
|
||||
pub path: String,
|
||||
@ -118,13 +63,13 @@ pub struct DomainConfig<'a> {
|
||||
}
|
||||
|
||||
impl XenClient {
|
||||
pub fn open() -> Result<XenClient, XenClientError> {
|
||||
pub fn open() -> Result<XenClient> {
|
||||
let store = XsdClient::open()?;
|
||||
let call = XenCall::open()?;
|
||||
Ok(XenClient { store, call })
|
||||
}
|
||||
|
||||
pub fn create(&mut self, config: &DomainConfig) -> Result<u32, XenClientError> {
|
||||
pub fn create(&mut self, config: &DomainConfig) -> Result<u32> {
|
||||
let domain = CreateDomain {
|
||||
max_vcpus: config.max_vcpus,
|
||||
..Default::default()
|
||||
@ -141,7 +86,7 @@ impl XenClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(&mut self, domid: u32) -> Result<(), XenClientError> {
|
||||
pub fn destroy(&mut self, domid: u32) -> Result<()> {
|
||||
if let Err(err) = self.destroy_store(domid) {
|
||||
warn!("failed to destroy store for domain {}: {}", domid, err);
|
||||
}
|
||||
@ -149,13 +94,11 @@ impl XenClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy_store(&mut self, domid: u32) -> Result<(), XenClientError> {
|
||||
fn destroy_store(&mut self, domid: u32) -> Result<()> {
|
||||
let dom_path = self.store.get_domain_path(domid)?;
|
||||
let vm_path = self.store.read_string(&format!("{}/vm", dom_path))?;
|
||||
if vm_path.is_empty() {
|
||||
return Err(XenClientError::new(
|
||||
"cannot destroy domain that doesn't exist",
|
||||
));
|
||||
return Err(Error::new("cannot destroy domain that doesn't exist"));
|
||||
}
|
||||
|
||||
let mut backend_paths: Vec<String> = Vec::new();
|
||||
@ -220,10 +163,10 @@ impl XenClient {
|
||||
let path = PathBuf::from(path);
|
||||
let parent = path
|
||||
.parent()
|
||||
.ok_or(XenClientError::new("unable to get parent of backend path"))?;
|
||||
.ok_or(Error::new("unable to get parent of backend path"))?;
|
||||
tx.rm(parent
|
||||
.to_str()
|
||||
.ok_or(XenClientError::new("unable to convert parent to string"))?)?;
|
||||
.ok_or(Error::new("unable to convert parent to string"))?)?;
|
||||
}
|
||||
tx.rm(&vm_path)?;
|
||||
tx.rm(&dom_path)?;
|
||||
@ -231,12 +174,7 @@ impl XenClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init(
|
||||
&mut self,
|
||||
domid: u32,
|
||||
domain: &CreateDomain,
|
||||
config: &DomainConfig,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn init(&mut self, domid: u32, domain: &CreateDomain, config: &DomainConfig) -> Result<()> {
|
||||
trace!(
|
||||
"XenClient init domid={} domain={:?} config={:?}",
|
||||
domid,
|
||||
@ -398,7 +336,7 @@ impl XenClient {
|
||||
.store
|
||||
.introduce_domain(domid, xenstore_mfn, xenstore_evtchn)?
|
||||
{
|
||||
return Err(XenClientError::new("failed to introduce domain"));
|
||||
return Err(Error::new("failed to introduce domain"));
|
||||
}
|
||||
self.console_device_add(
|
||||
&dom_path,
|
||||
@ -440,7 +378,7 @@ impl XenClient {
|
||||
domid: u32,
|
||||
index: usize,
|
||||
disk: &DomainDisk,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
let id = (202 << 8) | (index << 4) as u64;
|
||||
let backend_items: Vec<(&str, String)> = vec![
|
||||
("frontend-id", domid.to_string()),
|
||||
@ -491,7 +429,7 @@ impl XenClient {
|
||||
domid: u32,
|
||||
port: u32,
|
||||
mfn: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
let backend_entries = vec![
|
||||
("frontend-id", domid.to_string()),
|
||||
("online", "1".to_string()),
|
||||
@ -530,7 +468,7 @@ impl XenClient {
|
||||
domid: u32,
|
||||
index: usize,
|
||||
filesystem: &DomainFilesystem,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
let id = 90 + index as u64;
|
||||
let backend_items: Vec<(&str, String)> = vec![
|
||||
("frontend-id", domid.to_string()),
|
||||
@ -570,7 +508,7 @@ impl XenClient {
|
||||
domid: u32,
|
||||
frontend_items: Vec<(&str, String)>,
|
||||
backend_items: Vec<(&str, String)>,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
let console_zero = typ == "console" && id == 0;
|
||||
|
||||
let frontend_path = if console_zero {
|
||||
@ -624,7 +562,7 @@ impl XenClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn open_console(&mut self, domid: u32) -> Result<(File, File), XenClientError> {
|
||||
pub fn open_console(&mut self, domid: u32) -> Result<(File, File)> {
|
||||
let dom_path = self.store.get_domain_path(domid)?;
|
||||
let console_tty_path = format!("{}/console/tty", dom_path);
|
||||
let tty = self
|
||||
@ -632,10 +570,7 @@ impl XenClient {
|
||||
.read_string_optional(&console_tty_path)?
|
||||
.unwrap_or("".to_string());
|
||||
if tty.is_empty() {
|
||||
return Err(XenClientError::new(&format!(
|
||||
"domain {} does not have a tty",
|
||||
domid
|
||||
)));
|
||||
return Err(Error::new(&format!("domain {} does not have a tty", domid)));
|
||||
}
|
||||
let read = OpenOptions::new().read(true).write(false).open(&tty)?;
|
||||
let write = OpenOptions::new().read(false).write(true).open(&tty)?;
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::error::Result;
|
||||
use crate::sys::{XEN_PAGE_SHIFT, XEN_PAGE_SIZE};
|
||||
use crate::XenClientError;
|
||||
use crate::Error;
|
||||
use libc::munmap;
|
||||
use log::debug;
|
||||
use std::ffi::c_void;
|
||||
@ -40,7 +41,7 @@ impl PhysicalPages<'_> {
|
||||
self.p2m.len() as u64
|
||||
}
|
||||
|
||||
pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64, XenClientError> {
|
||||
pub fn pfn_to_ptr(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
for page in &self.pages {
|
||||
if pfn >= page.pfn + page.count {
|
||||
continue;
|
||||
@ -52,7 +53,7 @@ impl PhysicalPages<'_> {
|
||||
}
|
||||
|
||||
if pfn < page.pfn || (pfn + count) > page.pfn + page.count {
|
||||
return Err(XenClientError::new("request overlaps allocated block"));
|
||||
return Err(Error::new("request overlaps allocated block"));
|
||||
}
|
||||
} else {
|
||||
if pfn < page.pfn {
|
||||
@ -68,7 +69,7 @@ impl PhysicalPages<'_> {
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
"allocation is only allowed when a size is given",
|
||||
));
|
||||
}
|
||||
@ -76,7 +77,7 @@ impl PhysicalPages<'_> {
|
||||
self.pfn_alloc(pfn, count)
|
||||
}
|
||||
|
||||
fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result<u64, XenClientError> {
|
||||
fn pfn_alloc(&mut self, pfn: u64, count: u64) -> Result<u64> {
|
||||
let mut entries = vec![MmapEntry::default(); count as usize];
|
||||
for (i, entry) in entries.iter_mut().enumerate() {
|
||||
entry.mfn = self.p2m[pfn as usize + i];
|
||||
@ -95,11 +96,11 @@ impl PhysicalPages<'_> {
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, actual_mmap_len)
|
||||
.ok_or(XenClientError::new("failed to mmap address"))?;
|
||||
.ok_or(Error::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"));
|
||||
return Err(Error::new("mmap_batch call failed"));
|
||||
}
|
||||
let page = PhysicalPage {
|
||||
pfn,
|
||||
@ -114,7 +115,7 @@ impl PhysicalPages<'_> {
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result<u64, XenClientError> {
|
||||
pub fn map_foreign_pages(&mut self, mfn: u64, size: u64) -> Result<u64> {
|
||||
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) {
|
||||
@ -125,11 +126,11 @@ impl PhysicalPages<'_> {
|
||||
let addr = self
|
||||
.call
|
||||
.mmap(0, actual_mmap_len)
|
||||
.ok_or(XenClientError::new("failed to mmap address"))?;
|
||||
.ok_or(Error::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"));
|
||||
return Err(Error::new("mmap_batch call failed"));
|
||||
}
|
||||
let page = PhysicalPage {
|
||||
pfn: u64::MAX,
|
||||
@ -144,7 +145,7 @@ impl PhysicalPages<'_> {
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn unmap_all(&mut self) -> Result<(), XenClientError> {
|
||||
pub fn unmap_all(&mut self) -> Result<()> {
|
||||
for page in &self.pages {
|
||||
unsafe {
|
||||
let err = munmap(
|
||||
@ -152,7 +153,7 @@ impl PhysicalPages<'_> {
|
||||
(page.count << X86_PAGE_SHIFT) as usize,
|
||||
);
|
||||
if err != 0 {
|
||||
return Err(XenClientError::new("failed to munmap all pages"));
|
||||
return Err(Error::new("failed to munmap all pages"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,10 +161,10 @@ impl PhysicalPages<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, pfn: u64) -> Result<(), XenClientError> {
|
||||
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(XenClientError::new("unable to find page to unmap"));
|
||||
return Err(Error::new("unable to find page to unmap"));
|
||||
}
|
||||
let (i, page) = page.unwrap();
|
||||
|
||||
@ -178,7 +179,7 @@ impl PhysicalPages<'_> {
|
||||
page.ptr
|
||||
);
|
||||
if err != 0 {
|
||||
return Err(XenClientError::new("failed to munmap page"));
|
||||
return Err(Error::new("failed to munmap page"));
|
||||
}
|
||||
self.pages.remove(i);
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use crate::boot::{
|
||||
ArchBootSetup, BootImageInfo, BootSetup, BootState, DomainSegment, XEN_UNSET_ADDR,
|
||||
};
|
||||
use crate::error::Result;
|
||||
use crate::sys::{
|
||||
SUPERPAGE_2MB_NR_PFNS, SUPERPAGE_2MB_SHIFT, SUPERPAGE_BATCH_SIZE, VGCF_IN_KERNEL, VGCF_ONLINE,
|
||||
XEN_PAGE_SHIFT,
|
||||
};
|
||||
use crate::XenClientError;
|
||||
use crate::Error;
|
||||
use libc::c_char;
|
||||
use log::{debug, trace};
|
||||
use slice_copy::copy;
|
||||
@ -139,6 +140,12 @@ struct VmemRange {
|
||||
_nid: u32,
|
||||
}
|
||||
|
||||
impl Default for X86BootSetup {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl X86BootSetup {
|
||||
pub fn new() -> X86BootSetup {
|
||||
X86BootSetup {
|
||||
@ -193,22 +200,22 @@ impl X86BootSetup {
|
||||
from: u64,
|
||||
to: u64,
|
||||
pfn: u64,
|
||||
) -> Result<usize, XenClientError> {
|
||||
) -> Result<usize> {
|
||||
debug!("counting pgtables from={} to={} pfn={}", from, to, pfn);
|
||||
if self.table.mappings_count == X86_PAGE_TABLE_MAX_MAPPINGS {
|
||||
return Err(XenClientError::new("too many mappings"));
|
||||
return Err(Error::new("too many mappings"));
|
||||
}
|
||||
|
||||
let m = self.table.mappings_count;
|
||||
|
||||
let pfn_end = pfn + ((to - from) >> X86_PAGE_SHIFT);
|
||||
if pfn_end >= setup.phys.p2m_size() {
|
||||
return Err(XenClientError::new("not enough memory for initial mapping"));
|
||||
return Err(Error::new("not enough memory for initial mapping"));
|
||||
}
|
||||
|
||||
for idx in 0..self.table.mappings_count {
|
||||
if from < self.table.mappings[idx].area.to && to > self.table.mappings[idx].area.from {
|
||||
return Err(XenClientError::new("overlapping mappings"));
|
||||
return Err(Error::new("overlapping mappings"));
|
||||
}
|
||||
}
|
||||
let mut map = PageTableMapping::default();
|
||||
@ -284,7 +291,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
) -> Result<DomainSegment> {
|
||||
let mut p2m_alloc_size =
|
||||
((setup.phys.p2m_size() * 8) + X86_PAGE_SIZE - 1) & !(X86_PAGE_SIZE - 1);
|
||||
let from = image_info.virt_p2m_base;
|
||||
@ -310,7 +317,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<DomainSegment, XenClientError> {
|
||||
) -> Result<DomainSegment> {
|
||||
let mut extra_pages = 1;
|
||||
extra_pages += (512 * 1024) / X86_PAGE_SIZE;
|
||||
let mut pages = extra_pages;
|
||||
@ -341,11 +348,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(segment)
|
||||
}
|
||||
|
||||
fn setup_page_tables(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &mut BootState,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn setup_page_tables(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let p2m_guest = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
state.p2m_segment.addr as *mut u64,
|
||||
@ -407,7 +410,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
setup: &mut BootSetup,
|
||||
state: &BootState,
|
||||
cmdline: &str,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
let ptr = setup.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) };
|
||||
@ -441,11 +444,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_shared_info(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
shared_info_frame: u64,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn setup_shared_info(&mut self, setup: &mut BootSetup, shared_info_frame: u64) -> Result<()> {
|
||||
let info = setup
|
||||
.phys
|
||||
.map_foreign_pages(shared_info_frame, X86_PAGE_SIZE)?
|
||||
@ -466,7 +465,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
image_info: &BootImageInfo,
|
||||
) -> Result<(), XenClientError> {
|
||||
) -> Result<()> {
|
||||
if image_info.virt_hypercall == XEN_UNSET_ADDR {
|
||||
return Ok(());
|
||||
}
|
||||
@ -477,7 +476,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<(), XenClientError> {
|
||||
fn meminit(&mut self, setup: &mut BootSetup, total_pages: u64) -> Result<()> {
|
||||
setup.call.claim_pages(setup.domid, total_pages)?;
|
||||
let mut vmemranges: Vec<VmemRange> = Vec::new();
|
||||
let stub = VmemRange {
|
||||
@ -495,9 +494,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
}
|
||||
|
||||
if total != total_pages {
|
||||
return Err(XenClientError::new(
|
||||
"page count mismatch while calculating pages",
|
||||
));
|
||||
return Err(Error::new("page count mismatch while calculating pages"));
|
||||
}
|
||||
|
||||
setup.total_pages = total;
|
||||
@ -564,7 +561,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
.populate_physmap(setup.domid, allocsz, 0, 0, input_extent_starts)?;
|
||||
|
||||
if result.len() != allocsz as usize {
|
||||
return Err(XenClientError::new(
|
||||
return Err(Error::new(
|
||||
format!(
|
||||
"failed to populate physmap: wanted={} received={} input_extents={}",
|
||||
allocsz,
|
||||
@ -589,11 +586,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bootlate(
|
||||
&mut self,
|
||||
setup: &mut BootSetup,
|
||||
state: &mut BootState,
|
||||
) -> Result<(), XenClientError> {
|
||||
fn bootlate(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let pg_pfn = state.page_table_segment.pfn;
|
||||
let pg_mfn = setup.phys.p2m[pg_pfn as usize];
|
||||
setup.phys.unmap(pg_pfn)?;
|
||||
@ -604,7 +597,7 @@ impl ArchBootSetup for X86BootSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<(), XenClientError> {
|
||||
fn vcpu(&mut self, setup: &mut BootSetup, state: &mut BootState) -> Result<()> {
|
||||
let pg_pfn = state.page_table_segment.pfn;
|
||||
let pg_mfn = setup.phys.p2m[pg_pfn as usize];
|
||||
let mut vcpu = VcpuGuestContext::default();
|
||||
|
Loading…
Reference in New Issue
Block a user