diff --git a/xencall/examples/domain_create.rs b/xencall/examples/domain_create.rs index b041107..f2b2a26 100644 --- a/xencall/examples/domain_create.rs +++ b/xencall/examples/domain_create.rs @@ -3,8 +3,8 @@ use xencall::sys::CreateDomain; use xencall::{XenCall, XenCallError}; fn main() -> Result<(), XenCallError> { - let mut call = XenCall::open()?; - let mut domctl: DomainControl = DomainControl::new(&mut call); + let call = XenCall::open()?; + let domctl: DomainControl = DomainControl::new(&call); let info = domctl.create_domain(CreateDomain::default())?; println!("created domain {}", info.domid); Ok(()) diff --git a/xencall/examples/domain_info.rs b/xencall/examples/domain_info.rs index 72a06c0..3278e34 100644 --- a/xencall/examples/domain_info.rs +++ b/xencall/examples/domain_info.rs @@ -2,8 +2,8 @@ use xencall::domctl::DomainControl; use xencall::{XenCall, XenCallError}; fn main() -> Result<(), XenCallError> { - let mut call = XenCall::open()?; - let mut domctl: DomainControl = DomainControl::new(&mut call); + let call = XenCall::open()?; + let domctl: DomainControl = DomainControl::new(&call); let info = domctl.get_domain_info(1)?; println!("{:?}", info); Ok(()) diff --git a/xencall/examples/version_capabilities.rs b/xencall/examples/version_capabilities.rs index a306997..9cc30b8 100644 --- a/xencall/examples/version_capabilities.rs +++ b/xencall/examples/version_capabilities.rs @@ -1,7 +1,7 @@ use xencall::{XenCall, XenCallError}; fn main() -> Result<(), XenCallError> { - let mut call = XenCall::open()?; + let call = XenCall::open()?; let info = call.get_version_capabilities()?; println!("{:?}", info); Ok(()) diff --git a/xencall/src/domctl.rs b/xencall/src/domctl.rs index e5e5bd8..4a94523 100644 --- a/xencall/src/domctl.rs +++ b/xencall/src/domctl.rs @@ -8,7 +8,7 @@ use std::ffi::c_ulong; use std::ptr::addr_of; pub struct DomainControl<'a> { - call: &'a mut XenCall, + call: &'a XenCall, } pub struct CreatedDomain { @@ -16,11 +16,11 @@ pub struct CreatedDomain { } impl DomainControl<'_> { - pub fn new(call: &mut XenCall) -> DomainControl { + pub fn new(call: &XenCall) -> DomainControl { DomainControl { call } } - pub fn get_domain_info(&mut self, domid: u32) -> Result { + pub fn get_domain_info(&self, domid: u32) -> Result { let domctl = DomCtl { cmd: XEN_DOMCTL_GETDOMAININFO, interface_version: XEN_DOMCTL_INTERFACE_VERSION, @@ -56,7 +56,7 @@ impl DomainControl<'_> { } pub fn create_domain( - &mut self, + &self, create_domain: CreateDomain, ) -> Result { let domctl = DomCtl { diff --git a/xencall/src/lib.rs b/xencall/src/lib.rs index 834a365..06cb493 100644 --- a/xencall/src/lib.rs +++ b/xencall/src/lib.rs @@ -62,7 +62,7 @@ impl XenCall { Ok(XenCall { handle: file }) } - pub fn hypercall(&mut self, op: c_ulong, arg: [c_ulong; 5]) -> Result { + pub fn hypercall(&self, op: c_ulong, arg: [c_ulong; 5]) -> Result { unsafe { let mut call = Hypercall { op, arg }; let result = sys::hypercall(self.handle.as_raw_fd(), &mut call)?; @@ -70,16 +70,16 @@ impl XenCall { } } - pub fn hypercall0(&mut self, op: c_ulong) -> Result { + pub fn hypercall0(&self, op: c_ulong) -> Result { self.hypercall(op, [0, 0, 0, 0, 0]) } - pub fn hypercall1(&mut self, op: c_ulong, arg1: c_ulong) -> Result { + pub fn hypercall1(&self, op: c_ulong, arg1: c_ulong) -> Result { self.hypercall(op, [arg1, 0, 0, 0, 0]) } pub fn hypercall2( - &mut self, + &self, op: c_ulong, arg1: c_ulong, arg2: c_ulong, @@ -88,7 +88,7 @@ impl XenCall { } pub fn hypercall3( - &mut self, + &self, op: c_ulong, arg1: c_ulong, arg2: c_ulong, @@ -98,7 +98,7 @@ impl XenCall { } pub fn hypercall4( - &mut self, + &self, op: c_ulong, arg1: c_ulong, arg2: c_ulong, @@ -109,7 +109,7 @@ impl XenCall { } pub fn hypercall5( - &mut self, + &self, op: c_ulong, arg1: c_ulong, arg2: c_ulong, @@ -120,7 +120,7 @@ impl XenCall { self.hypercall(op, [arg1, arg2, arg3, arg4, arg5]) } - pub fn mmap(&mut self, mmap: Mmap) -> Result { + pub fn mmap(&self, mmap: Mmap) -> Result { unsafe { let mut mmap = mmap.clone(); let result = sys::mmap(self.handle.as_raw_fd(), &mut mmap)?; @@ -128,7 +128,7 @@ impl XenCall { } } - pub fn get_version_capabilities(&mut self) -> Result { + pub fn get_version_capabilities(&self) -> Result { let info = XenCapabilitiesInfo { capabilities: [0; 1024], }; diff --git a/xenclient/examples/boot.rs b/xenclient/examples/boot.rs index 1b4c85d..0304e22 100644 --- a/xenclient/examples/boot.rs +++ b/xenclient/examples/boot.rs @@ -1,10 +1,18 @@ use std::alloc::Layout; -use xenclient::boot::ElfLoader; +use xencall::domctl::DomainControl; +use xencall::sys::CreateDomain; +use xencall::XenCall; +use xenclient::boot::BootImageLoader; +use xenclient::elfloader::ElfImageLoader; use xenclient::XenClientError; fn main() -> Result<(), XenClientError> { - let boot = ElfLoader::load_file_kernel("/boot/vmlinuz-6.1.0-17-amd64")?; + let call = XenCall::open()?; + let domctl = DomainControl::new(&call); + let _domain = domctl.create_domain(CreateDomain::default())?; + let boot = ElfImageLoader::load_file_kernel("/boot/vmlinuz-6.1.0-17-amd64")?; let ptr = unsafe { std::alloc::alloc(Layout::from_size_align(128 * 1024 * 1024, 16).unwrap()) }; - boot.load(ptr)?; + let info = boot.load(ptr)?; + println!("{:?}", info); Ok(()) } diff --git a/xenclient/src/boot.rs b/xenclient/src/boot.rs index d5b7ce4..d52d029 100644 --- a/xenclient/src/boot.rs +++ b/xenclient/src/boot.rs @@ -1,213 +1,13 @@ -use crate::sys::{XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HV_START_LOW, XEN_ELFNOTE_VIRT_BASE}; use crate::XenClientError; -use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE}; -use elf::endian::AnyEndian; -use elf::note::Note; -use elf::{ElfBytes, ParseError}; -use flate2::bufread::GzDecoder; -use memchr::memmem::find_iter; -use std::collections::HashMap; -use std::io::{BufReader, Read}; -use std::mem::size_of; -use xz2::bufread::XzDecoder; -impl From for XenClientError { - fn from(value: ParseError) -> Self { - XenClientError::new(value.to_string().as_str()) - } +pub trait BootImageLoader { + fn load(&self, dst: *mut u8) -> Result; } -pub struct ElfLoader { - data: Vec, -} - -fn xen_note_value_u64(endian: AnyEndian, notes: &HashMap>, key: u64) -> Option { - let value = notes.get(&key); - value?; - let value = value.unwrap(); - let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); - bytes?; - - Some(match endian { - AnyEndian::Little => u64::from_le_bytes(bytes.unwrap()), - AnyEndian::Big => u64::from_be_bytes(bytes.unwrap()), - }) -} - -impl ElfLoader { - pub fn new(data: Vec) -> ElfLoader { - ElfLoader { data } - } - - pub fn load_file(path: &str) -> Result { - let data = std::fs::read(path)?; - Ok(ElfLoader::new(data)) - } - - pub fn load_gz(data: &[u8]) -> Result { - let buff = BufReader::new(data); - let image = ElfLoader::read_one_stream(&mut GzDecoder::new(buff))?; - Ok(ElfLoader::new(image)) - } - - pub fn load_xz(data: &[u8]) -> Result { - let buff = BufReader::new(data); - let image = ElfLoader::read_one_stream(&mut XzDecoder::new(buff))?; - Ok(ElfLoader::new(image)) - } - - fn read_one_stream(read: &mut dyn Read) -> Result, XenClientError> { - let mut result: Vec = Vec::new(); - let mut buffer = [0u8; 8192]; - - loop { - match read.read(&mut buffer) { - Ok(size) => { - if size == 0 { - break; - } - result.extend_from_slice(&buffer[0..size]) - } - Err(error) => { - if !result.is_empty() { - break; - } - return Err(XenClientError::from(error)); - } - } - } - Ok(result) - } - - pub fn load_file_gz(path: &str) -> Result { - let file = std::fs::read(path)?; - ElfLoader::load_gz(file.as_slice()) - } - - pub fn load_file_xz(path: &str) -> Result { - let file = std::fs::read(path)?; - ElfLoader::load_xz(file.as_slice()) - } - - pub fn load_file_kernel(path: &str) -> Result { - let file = std::fs::read(path)?; - - for start in find_iter(file.as_slice(), &[0x1f, 0x8b]) { - if let Ok(elf) = ElfLoader::load_gz(&file[start..]) { - return Ok(elf); - } - } - - for start in find_iter(file.as_slice(), &[0xfd, 0x37, 0x7a, 0x58]) { - match ElfLoader::load_xz(&file[start..]) { - Ok(elf) => return Ok(elf), - Err(error) => { - println!("{}", error); - } - } - } - - Err(XenClientError::new( - "Unable to parse kernel image: unknown compression type", - )) - } - - pub fn load(&self, dst: *mut u8) -> Result<(), XenClientError> { - let elf = ElfBytes::::minimal_parse(self.data.as_slice())?; - let headers = elf.section_headers().ok_or(XenClientError::new( - "Unable to parse kernel image: section headers not found.", - ))?; - let mut linux_notes: HashMap> = HashMap::new(); - let mut xen_notes: HashMap> = HashMap::new(); - - for header in headers { - if header.sh_type != SHT_NOTE { - continue; - } - - let notes = elf.section_data_as_notes(&header)?; - for note in notes { - if let Note::Unknown(note) = note { - if note.name == "Linux" { - linux_notes.insert(note.n_type, note.desc.to_vec()); - } - - if note.name == "Xen" { - xen_notes.insert(note.n_type, note.desc.to_vec()); - continue; - } - } - } - } - - if linux_notes.is_empty() { - return Err(XenClientError::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.", - )); - } - - let virt_base = xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_VIRT_BASE) - .ok_or(XenClientError::new( - "Unable to find virt_base note in kernel.", - ))?; - let entry = xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_ENTRY) - .ok_or(XenClientError::new("Unable to find entry note in kernel."))?; - let hv_start_low = - xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_HV_START_LOW).ok_or( - XenClientError::new("Unable to find hv_start_low note in kernel."), - )?; - - let mut start: u64 = u64::MAX; - let mut end: u64 = 0; - - let segments = elf.segments().ok_or(XenClientError::new( - "Unable to parse kernel image: segments not found.", - ))?; - for segment in segments { - if (segment.p_type != PT_LOAD) || (segment.p_flags & (PF_R | PF_W | PF_X)) == 0 { - continue; - } - let paddr = segment.p_paddr; - let memsz = segment.p_memsz; - if start > paddr { - start = paddr; - } - - if end < paddr + memsz { - end = paddr + memsz; - } - } - - let base_dst_addr = dst as u64; - for header in segments { - let paddr = header.p_paddr; - let filesz = header.p_filesz; - let memsz = header.p_memsz; - let dest = base_dst_addr + paddr - start; - let data = elf.segment_data(&header)?; - - unsafe { - std::ptr::copy(data.as_ptr(), dest as *mut u8, filesz as usize); - std::ptr::write_bytes((dest + filesz) as *mut u8, 0, (memsz - filesz) as usize); - } - } - - let virt_base = if virt_base == u64::MAX { 0 } else { virt_base }; - - let virt_kstart = start + virt_base; - let virt_kend = end + virt_base; - - println!("virt_kstart: {}", virt_kstart); - println!("virt_kend: {}", virt_kend); - println!("entry: {}", entry); - println!("hv_start_low: {}", hv_start_low); - - Ok(()) - } +#[derive(Debug)] +pub struct BootImageInfo { + pub virt_kstart: u64, + pub virt_kend: u64, + pub entry: u64, + pub hv_start_low: u64, } diff --git a/xenclient/src/elfloader.rs b/xenclient/src/elfloader.rs new file mode 100644 index 0000000..01eace1 --- /dev/null +++ b/xenclient/src/elfloader.rs @@ -0,0 +1,216 @@ +use crate::boot::{BootImageInfo, BootImageLoader}; +use crate::sys::{XEN_ELFNOTE_ENTRY, XEN_ELFNOTE_HV_START_LOW, XEN_ELFNOTE_VIRT_BASE}; +use crate::XenClientError; +use elf::abi::{PF_R, PF_W, PF_X, PT_LOAD, SHT_NOTE}; +use elf::endian::AnyEndian; +use elf::note::Note; +use elf::{ElfBytes, ParseError}; +use flate2::bufread::GzDecoder; +use memchr::memmem::find_iter; +use std::collections::HashMap; +use std::io::{BufReader, Read}; +use std::mem::size_of; +use xz2::bufread::XzDecoder; + +impl From for XenClientError { + fn from(value: ParseError) -> Self { + XenClientError::new(value.to_string().as_str()) + } +} + +pub struct ElfImageLoader { + data: Vec, +} + +fn xen_note_value_u64(endian: AnyEndian, notes: &HashMap>, key: u64) -> Option { + let value = notes.get(&key); + value?; + let value = value.unwrap(); + let bytes: Option<[u8; size_of::()]> = value.clone().try_into().ok(); + bytes?; + + Some(match endian { + AnyEndian::Little => u64::from_le_bytes(bytes.unwrap()), + AnyEndian::Big => u64::from_be_bytes(bytes.unwrap()), + }) +} + +impl ElfImageLoader { + pub fn new(data: Vec) -> ElfImageLoader { + ElfImageLoader { data } + } + + pub fn load_file(path: &str) -> Result { + let data = std::fs::read(path)?; + Ok(ElfImageLoader::new(data)) + } + + pub fn load_gz(data: &[u8]) -> Result { + 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 { + 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, XenClientError> { + let mut result: Vec = Vec::new(); + let mut buffer = [0u8; 8192]; + + loop { + match read.read(&mut buffer) { + Ok(size) => { + if size == 0 { + break; + } + result.extend_from_slice(&buffer[0..size]) + } + Err(error) => { + if !result.is_empty() { + break; + } + return Err(XenClientError::from(error)); + } + } + } + Ok(result) + } + + pub fn load_file_gz(path: &str) -> Result { + let file = std::fs::read(path)?; + ElfImageLoader::load_gz(file.as_slice()) + } + + pub fn load_file_xz(path: &str) -> Result { + let file = std::fs::read(path)?; + ElfImageLoader::load_xz(file.as_slice()) + } + + pub fn load_file_kernel(path: &str) -> Result { + let file = std::fs::read(path)?; + + for start in find_iter(file.as_slice(), &[0x1f, 0x8b]) { + if let Ok(elf) = ElfImageLoader::load_gz(&file[start..]) { + return Ok(elf); + } + } + + for start in find_iter(file.as_slice(), &[0xfd, 0x37, 0x7a, 0x58]) { + match ElfImageLoader::load_xz(&file[start..]) { + Ok(elf) => return Ok(elf), + Err(error) => { + println!("{}", error); + } + } + } + + Err(XenClientError::new( + "Unable to parse kernel image: unknown compression type", + )) + } +} + +impl BootImageLoader for ElfImageLoader { + fn load(&self, dst: *mut u8) -> Result { + let elf = ElfBytes::::minimal_parse(self.data.as_slice())?; + let headers = elf.section_headers().ok_or(XenClientError::new( + "Unable to parse kernel image: section headers not found.", + ))?; + let mut linux_notes: HashMap> = HashMap::new(); + let mut xen_notes: HashMap> = HashMap::new(); + + for header in headers { + if header.sh_type != SHT_NOTE { + continue; + } + + let notes = elf.section_data_as_notes(&header)?; + for note in notes { + if let Note::Unknown(note) = note { + if note.name == "Linux" { + linux_notes.insert(note.n_type, note.desc.to_vec()); + } + + if note.name == "Xen" { + xen_notes.insert(note.n_type, note.desc.to_vec()); + continue; + } + } + } + } + + if linux_notes.is_empty() { + return Err(XenClientError::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.", + )); + } + + let virt_base = xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_VIRT_BASE) + .ok_or(XenClientError::new( + "Unable to find virt_base note in kernel.", + ))?; + let entry = xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_ENTRY) + .ok_or(XenClientError::new("Unable to find entry note in kernel."))?; + let hv_start_low = + xen_note_value_u64(elf.ehdr.endianness, &xen_notes, XEN_ELFNOTE_HV_START_LOW).ok_or( + XenClientError::new("Unable to find hv_start_low note in kernel."), + )?; + + let mut start: u64 = u64::MAX; + let mut end: u64 = 0; + + let segments = elf.segments().ok_or(XenClientError::new( + "Unable to parse kernel image: segments not found.", + ))?; + for segment in segments { + if (segment.p_type != PT_LOAD) || (segment.p_flags & (PF_R | PF_W | PF_X)) == 0 { + continue; + } + let paddr = segment.p_paddr; + let memsz = segment.p_memsz; + if start > paddr { + start = paddr; + } + + if end < paddr + memsz { + end = paddr + memsz; + } + } + + let base_dst_addr = dst as u64; + for header in segments { + let paddr = header.p_paddr; + let filesz = header.p_filesz; + let memsz = header.p_memsz; + let dest = base_dst_addr + paddr - start; + let data = elf.segment_data(&header)?; + + unsafe { + std::ptr::copy(data.as_ptr(), dest as *mut u8, filesz as usize); + std::ptr::write_bytes((dest + filesz) as *mut u8, 0, (memsz - filesz) as usize); + } + } + + let virt_base = if virt_base == u64::MAX { 0 } else { virt_base }; + + let virt_kstart = start + virt_base; + let virt_kend = end + virt_base; + + Ok(BootImageInfo { + virt_kstart, + virt_kend, + entry, + hv_start_low, + }) + } +} diff --git a/xenclient/src/lib.rs b/xenclient/src/lib.rs index 6591451..df49974 100644 --- a/xenclient/src/lib.rs +++ b/xenclient/src/lib.rs @@ -1,5 +1,6 @@ pub mod boot; pub mod create; +pub mod elfloader; pub mod sys; use crate::create::DomainConfig; @@ -74,7 +75,7 @@ impl XenClient { } pub fn create(&mut self, config: DomainConfig) -> Result<(), XenClientError> { - let mut domctl = DomainControl::new(&mut self.call); + let domctl = DomainControl::new(&self.call); let created = domctl.create_domain(CreateDomain::default())?; let domain = self.store.get_domain_path(created.domid)?; let vm = self.store.read_string(format!("{}/vm", domain).as_str())?;