Skip to content

Commit d2660d7

Browse files
committed
lib: add krun_set_init_binary, backcompat via new krun_init crate
Signed-off-by: Geoffrey Goodman <geoff@goodman.dev>
1 parent 788cf91 commit d2660d7

16 files changed

Lines changed: 192 additions & 35 deletions

File tree

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["src/libkrun", "src/krun_input"]
2+
members = ["src/libkrun", "src/krun_input", "src/krun_init"]
33
exclude = ["examples/gtk_display"]
44
resolver = "2"
55

include/libkrun.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ int32_t krun_set_vm_config(uint32_t ctx_id, uint8_t num_vcpus, uint32_t ram_mib)
109109
*/
110110
int32_t krun_set_root(uint32_t ctx_id, const char *root_path);
111111

112+
/**
113+
* Sets the init binary payload that will be exposed as /init.krun inside the guest.
114+
*
115+
* IMPORTANT LIFETIME CONTRACT:
116+
* The memory range [init_binary, init_binary + init_binary_len) is NOT copied.
117+
* The caller MUST keep that memory valid and unchanged for the full VM lifetime
118+
* in this context (until krun_start_enter() returns and the VM is torn down).
119+
*
120+
* Arguments:
121+
* "ctx_id" - the configuration context ID.
122+
* "init_binary" - pointer to init binary bytes.
123+
* "init_binary_len" - number of bytes at init_binary.
124+
*
125+
* Returns:
126+
* Zero on success or a negative error number on failure.
127+
*/
128+
int32_t krun_set_init(uint32_t ctx_id, const uint8_t *init_binary, size_t init_binary_len);
129+
112130
/**
113131
* DEPRECATED. Use krun_add_disk instead.
114132
*

src/devices/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ name = "devices"
33
version = "0.1.0"
44
authors = ["The Chromium OS Authors"]
55
edition = "2021"
6-
build = "build.rs"
76

87
[features]
98
tee = []

src/devices/src/virtio/fs/device.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl Fs {
5959
shared_dir: String,
6060
exit_code: Arc<AtomicI32>,
6161
allow_root_dir_delete: bool,
62+
init_payload: Option<&'static [u8]>,
6263
) -> super::Result<Fs> {
6364
let avail_features = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_RING_F_EVENT_IDX);
6465

@@ -70,6 +71,7 @@ impl Fs {
7071
let fs_cfg = passthrough::Config {
7172
root_dir: shared_dir,
7273
allow_root_dir_delete,
74+
init_payload,
7375
..Default::default()
7476
};
7577

src/devices/src/virtio/fs/linux/passthrough.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ const EMPTY_CSTR: &[u8] = b"\0";
3333
const PROC_CSTR: &[u8] = b"/proc/self/fd\0";
3434
const INIT_CSTR: &[u8] = b"init.krun\0";
3535

36-
static INIT_BINARY: &[u8] = include_bytes!(env!("KRUN_INIT_BINARY_PATH"));
37-
3836
type Inode = u64;
3937
type Handle = u64;
4038

@@ -328,6 +326,7 @@ pub struct Config {
328326
/// Table of exported FDs to share with other subsystems.
329327
pub export_table: Option<ExportTable>,
330328
pub allow_root_dir_delete: bool,
329+
pub init_payload: Option<&'static [u8]>,
331330
}
332331

333332
impl Default for Config {
@@ -343,6 +342,7 @@ impl Default for Config {
343342
export_fsid: 0,
344343
export_table: None,
345344
allow_root_dir_delete: false,
345+
init_payload: None,
346346
}
347347
}
348348
}
@@ -439,7 +439,11 @@ impl PassthroughFs {
439439
Ok(PassthroughFs {
440440
inodes: RwLock::new(MultikeyBTreeMap::new()),
441441
next_inode: AtomicU64::new(fuse::ROOT_ID + 2),
442-
init_inode: fuse::ROOT_ID + 1,
442+
init_inode: if cfg.init_payload.is_some() {
443+
fuse::ROOT_ID + 1
444+
} else {
445+
0
446+
},
443447

444448
handles: RwLock::new(BTreeMap::new()),
445449
next_handle: AtomicU64::new(1),
@@ -456,6 +460,10 @@ impl PassthroughFs {
456460
})
457461
}
458462

463+
fn init_payload(&self) -> io::Result<&[u8]> {
464+
self.cfg.init_payload.ok_or_else(ebadf)
465+
}
466+
459467
fn open_inode(&self, inode: Inode, mut flags: i32) -> io::Result<File> {
460468
let data = self
461469
.inodes
@@ -996,7 +1004,7 @@ impl FileSystem for PassthroughFs {
9961004

9971005
if self.init_inode != 0 && name == init_name {
9981006
let mut st: libc::stat64 = unsafe { mem::zeroed() };
999-
st.st_size = INIT_BINARY.len() as i64;
1007+
st.st_size = self.init_payload()?.len() as i64;
10001008
st.st_ino = self.init_inode;
10011009
st.st_mode = 0o100_755;
10021010

@@ -1235,13 +1243,16 @@ impl FileSystem for PassthroughFs {
12351243
) -> io::Result<usize> {
12361244
debug!("read: {inode:?}");
12371245
if inode == self.init_inode {
1246+
let init_payload = self.init_payload()?;
12381247
let off: usize = offset.try_into().map_err(|_| einval())?;
1239-
let len = if off + (size as usize) < INIT_BINARY.len() {
1240-
size as usize
1241-
} else {
1242-
INIT_BINARY.len() - off
1243-
};
1244-
return w.write(&INIT_BINARY[off..(off + len)]);
1248+
if off >= init_payload.len() {
1249+
// Read past EOF should return 0 bytes.
1250+
return Ok(0);
1251+
}
1252+
1253+
let remaining = init_payload.len() - off;
1254+
let len = remaining.min(size as usize);
1255+
return w.write(&init_payload[off..(off + len)]);
12451256
}
12461257

12471258
let data = self
@@ -2088,6 +2099,7 @@ impl FileSystem for PassthroughFs {
20882099
debug!("setupmapping: ino {inode:?} addr={addr:x} len={len}");
20892100

20902101
if inode == self.init_inode {
2102+
let init_payload = self.init_payload()?;
20912103
let ret = unsafe {
20922104
libc::mmap(
20932105
addr as *mut libc::c_void,
@@ -2102,15 +2114,15 @@ impl FileSystem for PassthroughFs {
21022114
return Err(io::Error::last_os_error());
21032115
}
21042116

2105-
let to_copy = if len as usize > INIT_BINARY.len() {
2106-
INIT_BINARY.len()
2117+
let to_copy = if len as usize > init_payload.len() {
2118+
init_payload.len()
21072119
} else {
21082120
len as usize
21092121
};
21102122
unsafe {
21112123
libc::memcpy(
21122124
addr as *mut libc::c_void,
2113-
INIT_BINARY.as_ptr() as *const _,
2125+
init_payload.as_ptr() as *const _,
21142126
to_copy,
21152127
)
21162128
};

src/devices/src/virtio/fs/macos/passthrough.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ const SECURITY_CAPABILITY: &[u8] = b"security.capability\0";
3737

3838
const UID_MAX: u32 = u32::MAX - 1;
3939

40-
static INIT_BINARY: &[u8] = include_bytes!(env!("KRUN_INIT_BINARY_PATH"));
41-
4240
type Inode = u64;
4341
type Handle = u64;
4442

@@ -517,6 +515,7 @@ pub struct Config {
517515
/// Table of exported FDs to share with other subsystems. Not supported for macos.
518516
pub export_table: Option<ExportTable>,
519517
pub allow_root_dir_delete: bool,
518+
pub init_payload: Option<&'static [u8]>,
520519
}
521520

522521
impl Default for Config {
@@ -532,6 +531,7 @@ impl Default for Config {
532531
export_fsid: 0,
533532
export_table: None,
534533
allow_root_dir_delete: false,
534+
init_payload: None,
535535
}
536536
}
537537
}
@@ -580,7 +580,11 @@ impl PassthroughFs {
580580
Ok(PassthroughFs {
581581
inodes: RwLock::new(MultikeyBTreeMap::new()),
582582
next_inode: AtomicU64::new(fuse::ROOT_ID + 2),
583-
init_inode: fuse::ROOT_ID + 1,
583+
init_inode: if cfg.init_payload.is_some() {
584+
fuse::ROOT_ID + 1
585+
} else {
586+
0
587+
},
584588

585589
handles: RwLock::new(BTreeMap::new()),
586590
next_handle: AtomicU64::new(1),
@@ -594,6 +598,10 @@ impl PassthroughFs {
594598
})
595599
}
596600

601+
fn init_payload(&self) -> io::Result<&[u8]> {
602+
self.cfg.init_payload.ok_or_else(ebadf)
603+
}
604+
597605
fn inode_to_handle(&self, inode: Inode, supports_fd: bool) -> io::Result<InodeHandle> {
598606
debug!("inode_to_handle: inode={inode}");
599607
let data = self
@@ -1205,7 +1213,7 @@ impl FileSystem for PassthroughFs {
12051213

12061214
if self.init_inode != 0 && name == _init_name {
12071215
let mut st: bindings::stat64 = unsafe { mem::zeroed() };
1208-
st.st_size = INIT_BINARY.len() as i64;
1216+
st.st_size = self.init_payload()?.len() as i64;
12091217
st.st_ino = self.init_inode;
12101218
st.st_mode = 0o100_755;
12111219

@@ -1457,15 +1465,18 @@ impl FileSystem for PassthroughFs {
14571465
) -> io::Result<usize> {
14581466
debug!("read: {inode:?}");
14591467
if inode == self.init_inode {
1468+
let init_payload = self.init_payload()?;
14601469
let off: usize = offset
14611470
.try_into()
14621471
.map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
1463-
let len = if off + (size as usize) < INIT_BINARY.len() {
1464-
size as usize
1465-
} else {
1466-
INIT_BINARY.len() - off
1467-
};
1468-
return w.write(&INIT_BINARY[off..(off + len)]);
1472+
if off >= init_payload.len() {
1473+
// Read past EOF should return 0 bytes.
1474+
return Ok(0);
1475+
}
1476+
1477+
let remaining = init_payload.len() - off;
1478+
let len = remaining.min(size as usize);
1479+
return w.write(&init_payload[off..(off + len)]);
14691480
}
14701481

14711482
let data = self

src/krun_init/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "krun_init"
3+
version = "0.1.0"
4+
edition = "2021"
5+
build = "build.rs"
6+
7+
[lib]
8+
path = "src/lib.rs"
Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ffi::OsStr;
22
use std::path::PathBuf;
33
use std::process::Command;
44

5-
fn build_default_init() -> PathBuf {
5+
fn build_default_init() -> Option<PathBuf> {
66
let manifest_dir = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
77
let libkrun_root = manifest_dir.join("../..");
88
let init_src = libkrun_root.join("init/init.c");
@@ -39,20 +39,35 @@ fn build_default_init() -> PathBuf {
3939
.unwrap_or_else(|e| panic!("failed to execute {cc}: {e}"));
4040

4141
if !status.success() {
42-
panic!("failed to compile init/init.c: {status}");
42+
return None;
4343
}
44-
init_bin
44+
Some(init_bin)
4545
}
4646

4747
fn main() {
48+
let manifest_dir = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
49+
let repo_init_bin = manifest_dir.join("../..").join("init/init");
50+
println!("cargo:rerun-if-changed={}", repo_init_bin.display());
51+
4852
let init_binary_path = std::env::var_os("KRUN_INIT_BINARY_PATH")
4953
.map(PathBuf::from)
54+
.or_else(build_default_init)
55+
.or_else(|| {
56+
if repo_init_bin.exists() {
57+
Some(repo_init_bin)
58+
} else {
59+
None
60+
}
61+
})
5062
.unwrap_or_else(|| {
51-
let init_path = build_default_init();
52-
// SAFETY: The build script is single threaded.
53-
unsafe { std::env::set_var("KRUN_INIT_BINARY_PATH", &init_path) };
54-
init_path
63+
panic!(
64+
"failed to produce an init binary: set KRUN_INIT_BINARY_PATH or ensure init/init exists"
65+
)
5566
});
67+
68+
// SAFETY: The build script is single threaded.
69+
unsafe { std::env::set_var("KRUN_INIT_BINARY_PATH", &init_binary_path) };
70+
5671
println!(
5772
"cargo:rustc-env=KRUN_INIT_BINARY_PATH={}",
5873
init_binary_path.display()

src/krun_init/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub static DEFAULT_INIT: &[u8] = include_bytes!(env!("KRUN_INIT_BINARY_PATH"));

0 commit comments

Comments
 (0)