feat: implement kernel / initrd oci image support (#103)

* feat: implement kernel / initrd oci image support

* fix: implement image urls more faithfully
This commit is contained in:
Alex Zenla
2024-04-22 12:48:45 -07:00
committed by GitHub
parent 1b90eedbcd
commit 82576df7b7
18 changed files with 470 additions and 262 deletions

View File

@ -1,4 +1,5 @@
use std::{env, process};
use tokio::fs;
use xenclient::error::Result;
use xenclient::{DomainConfig, XenClient};
@ -16,12 +17,12 @@ async fn main() -> Result<()> {
let client = XenClient::open(0).await?;
let config = DomainConfig {
backend_domid: 0,
name: "xenclient-test",
name: "xenclient-test".to_string(),
max_vcpus: 1,
mem_mb: 512,
kernel_path: kernel_image_path.as_str(),
initrd_path: initrd_path.as_str(),
cmdline: "debug elevator=noop",
kernel: fs::read(&kernel_image_path).await?,
initrd: fs::read(&initrd_path).await?,
cmdline: "debug elevator=noop".to_string(),
use_console_backend: None,
disks: vec![],
channels: vec![],

View File

@ -107,17 +107,15 @@ impl ElfImageLoader {
ElfImageLoader::load_xz(file.as_slice())
}
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]) {
if let Ok(elf) = ElfImageLoader::load_gz(&file[start..]) {
pub fn load_file_kernel(data: &[u8]) -> Result<ElfImageLoader> {
for start in find_iter(data, &[0x1f, 0x8b]) {
if let Ok(elf) = ElfImageLoader::load_gz(&data[start..]) {
return Ok(elf);
}
}
for start in find_iter(file.as_slice(), &[0xfd, 0x37, 0x7a, 0x58]) {
if let Ok(elf) = ElfImageLoader::load_xz(&file[start..]) {
for start in find_iter(data, &[0xfd, 0x37, 0x7a, 0x58]) {
if let Ok(elf) = ElfImageLoader::load_xz(&data[start..]) {
return Ok(elf);
}
}

View File

@ -23,7 +23,6 @@ use boot::BootState;
use log::{debug, trace, warn};
use tokio::time::timeout;
use std::fs::read;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
@ -40,60 +39,60 @@ pub struct XenClient {
call: XenCall,
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct BlockDeviceRef {
pub path: String,
pub major: u32,
pub minor: u32,
}
#[derive(Debug)]
pub struct DomainDisk<'a> {
pub vdev: &'a str,
pub block: &'a BlockDeviceRef,
#[derive(Clone, Debug)]
pub struct DomainDisk {
pub vdev: String,
pub block: BlockDeviceRef,
pub writable: bool,
}
#[derive(Debug)]
pub struct DomainFilesystem<'a> {
pub path: &'a str,
pub tag: &'a str,
#[derive(Clone, Debug)]
pub struct DomainFilesystem {
pub path: String,
pub tag: String,
}
#[derive(Debug)]
pub struct DomainNetworkInterface<'a> {
pub mac: &'a str,
#[derive(Clone, Debug)]
pub struct DomainNetworkInterface {
pub mac: String,
pub mtu: u32,
pub bridge: Option<&'a str>,
pub script: Option<&'a str>,
pub bridge: Option<String>,
pub script: Option<String>,
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct DomainChannel {
pub typ: String,
pub initialized: bool,
}
#[derive(Debug)]
pub struct DomainEventChannel<'a> {
pub name: &'a str,
#[derive(Clone, Debug)]
pub struct DomainEventChannel {
pub name: String,
}
#[derive(Debug)]
pub struct DomainConfig<'a> {
#[derive(Clone, Debug)]
pub struct DomainConfig {
pub backend_domid: u32,
pub name: &'a str,
pub name: String,
pub max_vcpus: u32,
pub mem_mb: u64,
pub kernel_path: &'a str,
pub initrd_path: &'a str,
pub cmdline: &'a str,
pub disks: Vec<DomainDisk<'a>>,
pub use_console_backend: Option<&'a str>,
pub kernel: Vec<u8>,
pub initrd: Vec<u8>,
pub cmdline: String,
pub disks: Vec<DomainDisk>,
pub use_console_backend: Option<String>,
pub channels: Vec<DomainChannel>,
pub vifs: Vec<DomainNetworkInterface<'a>>,
pub filesystems: Vec<DomainFilesystem<'a>>,
pub event_channels: Vec<DomainEventChannel<'a>>,
pub vifs: Vec<DomainNetworkInterface>,
pub filesystems: Vec<DomainFilesystem>,
pub event_channels: Vec<DomainEventChannel>,
pub extra_keys: Vec<(String, String)>,
pub extra_rw_paths: Vec<String>,
}
@ -117,7 +116,7 @@ impl XenClient {
Ok(XenClient { store, call })
}
pub async fn create(&self, config: &DomainConfig<'_>) -> Result<CreatedDomain> {
pub async fn create(&self, config: &DomainConfig) -> Result<CreatedDomain> {
let mut domain = CreateDomain {
max_vcpus: config.max_vcpus,
..Default::default()
@ -143,7 +142,7 @@ impl XenClient {
&self,
domid: u32,
domain: &CreateDomain,
config: &DomainConfig<'_>,
config: &DomainConfig,
) -> Result<CreatedDomain> {
trace!(
"XenClient init domid={} domain={:?} config={:?}",
@ -237,9 +236,9 @@ impl XenClient {
&Uuid::from_bytes(domain.handle).to_string(),
)
.await?;
tx.write_string(format!("{}/name", dom_path).as_str(), config.name)
tx.write_string(format!("{}/name", dom_path).as_str(), &config.name)
.await?;
tx.write_string(format!("{}/name", vm_path).as_str(), config.name)
tx.write_string(format!("{}/name", vm_path).as_str(), &config.name)
.await?;
for (key, value) in &config.extra_keys {
@ -257,7 +256,7 @@ impl XenClient {
self.call.set_max_vcpus(domid, config.max_vcpus).await?;
self.call.set_max_mem(domid, config.mem_mb * 1024).await?;
let image_loader = ElfImageLoader::load_file_kernel(config.kernel_path)?;
let image_loader = ElfImageLoader::load_file_kernel(&config.kernel)?;
let xenstore_evtchn: u32;
let xenstore_mfn: u64;
@ -270,18 +269,17 @@ impl XenClient {
let mut arch = Box::new(X86BootSetup::new()) as Box<dyn ArchBootSetup + Send + Sync>;
#[cfg(target_arch = "aarch64")]
let mut arch = Box::new(Arm64BootSetup::new()) as Box<dyn ArchBootSetup + Send + Sync>;
let initrd = read(config.initrd_path)?;
state = boot
.initialize(
&mut arch,
&image_loader,
initrd.as_slice(),
&config.initrd,
config.max_vcpus,
config.mem_mb,
1,
)
.await?;
boot.boot(&mut arch, &mut state, config.cmdline).await?;
boot.boot(&mut arch, &mut state, &config.cmdline).await?;
xenstore_evtchn = state.store_evtchn;
xenstore_mfn = boot.phys.p2m[state.xenstore_segment.pfn as usize];
p2m = boot.phys.p2m;
@ -291,19 +289,9 @@ impl XenClient {
let tx = self.store.transaction().await?;
tx.write_string(format!("{}/image/os_type", vm_path).as_str(), "linux")
.await?;
tx.write_string(
format!("{}/image/kernel", vm_path).as_str(),
config.kernel_path,
)
.await?;
tx.write_string(
format!("{}/image/ramdisk", vm_path).as_str(),
config.initrd_path,
)
.await?;
tx.write_string(
format!("{}/image/cmdline", vm_path).as_str(),
config.cmdline,
&config.cmdline,
)
.await?;
@ -352,7 +340,8 @@ impl XenClient {
&DomainChannel {
typ: config
.use_console_backend
.unwrap_or("xenconsoled")
.clone()
.unwrap_or("xenconsoled".to_string())
.to_string(),
initialized: true,
},
@ -429,7 +418,7 @@ impl XenClient {
.await?;
let channel_path = format!("{}/evtchn/{}", dom_path, channel.name);
self.store
.write_string(&format!("{}/name", channel_path), channel.name)
.write_string(&format!("{}/name", channel_path), &channel.name)
.await?;
self.store
.write_string(&format!("{}/channel", channel_path), &id.to_string())
@ -447,7 +436,7 @@ impl XenClient {
backend_domid: u32,
domid: u32,
index: usize,
disk: &DomainDisk<'_>,
disk: &DomainDisk,
) -> Result<()> {
let id = (202 << 8) | (index << 4) as u64;
let backend_items: Vec<(&str, String)> = vec![
@ -567,7 +556,7 @@ impl XenClient {
backend_domid: u32,
domid: u32,
index: usize,
filesystem: &DomainFilesystem<'_>,
filesystem: &DomainFilesystem,
) -> Result<()> {
let id = 90 + index as u64;
let backend_items: Vec<(&str, String)> = vec![
@ -605,7 +594,7 @@ impl XenClient {
backend_domid: u32,
domid: u32,
index: usize,
vif: &DomainNetworkInterface<'_>,
vif: &DomainNetworkInterface,
) -> Result<()> {
let id = 20 + index as u64;
let mut backend_items: Vec<(&str, String)> = vec![
@ -619,12 +608,12 @@ impl XenClient {
];
if vif.bridge.is_some() {
backend_items.extend_from_slice(&[("bridge", vif.bridge.unwrap().to_string())]);
backend_items.extend_from_slice(&[("bridge", vif.bridge.clone().unwrap())]);
}
if vif.script.is_some() {
backend_items.extend_from_slice(&[
("script", vif.script.unwrap().to_string()),
("script", vif.script.clone().unwrap()),
("hotplug-status", "".to_string()),
]);
} else {