Skip to content

Commit ab92f2b

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 ab92f2b

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: 27 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,12 @@ impl PassthroughFs {
456460
})
457461
}
458462

463+
fn init_payload(&self) -> io::Result<&[u8]> {
464+
self.cfg
465+
.init_payload
466+
.ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
467+
}
468+
459469
fn open_inode(&self, inode: Inode, mut flags: i32) -> io::Result<File> {
460470
let data = self
461471
.inodes
@@ -996,7 +1006,7 @@ impl FileSystem for PassthroughFs {
9961006

9971007
if self.init_inode != 0 && name == init_name {
9981008
let mut st: libc::stat64 = unsafe { mem::zeroed() };
999-
st.st_size = INIT_BINARY.len() as i64;
1009+
st.st_size = self.init_payload()?.len() as i64;
10001010
st.st_ino = self.init_inode;
10011011
st.st_mode = 0o100_755;
10021012

@@ -1235,13 +1245,16 @@ impl FileSystem for PassthroughFs {
12351245
) -> io::Result<usize> {
12361246
debug!("read: {inode:?}");
12371247
if inode == self.init_inode {
1248+
let init_payload = self.init_payload()?;
12381249
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)]);
1250+
if off >= init_payload.len() {
1251+
// Read past EOF should return 0 bytes.
1252+
return Ok(0);
1253+
}
1254+
1255+
let remaining = init_payload.len() - off;
1256+
let len = remaining.min(size as usize);
1257+
return w.write(&init_payload[off..(off + len)]);
12451258
}
12461259

12471260
let data = self
@@ -2088,6 +2101,7 @@ impl FileSystem for PassthroughFs {
20882101
debug!("setupmapping: ino {inode:?} addr={addr:x} len={len}");
20892102

20902103
if inode == self.init_inode {
2104+
let init_payload = self.init_payload()?;
20912105
let ret = unsafe {
20922106
libc::mmap(
20932107
addr as *mut libc::c_void,
@@ -2102,15 +2116,15 @@ impl FileSystem for PassthroughFs {
21022116
return Err(io::Error::last_os_error());
21032117
}
21042118

2105-
let to_copy = if len as usize > INIT_BINARY.len() {
2106-
INIT_BINARY.len()
2119+
let to_copy = if len as usize > init_payload.len() {
2120+
init_payload.len()
21072121
} else {
21082122
len as usize
21092123
};
21102124
unsafe {
21112125
libc::memcpy(
21122126
addr as *mut libc::c_void,
2113-
INIT_BINARY.as_ptr() as *const _,
2127+
init_payload.as_ptr() as *const _,
21142128
to_copy,
21152129
)
21162130
};

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

Lines changed: 23 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,12 @@ impl PassthroughFs {
594598
})
595599
}
596600

601+
fn init_payload(&self) -> io::Result<&[u8]> {
602+
self.cfg
603+
.init_payload
604+
.ok_or_else(|| linux_error(io::Error::from_raw_os_error(libc::ENOENT)))
605+
}
606+
597607
fn inode_to_handle(&self, inode: Inode, supports_fd: bool) -> io::Result<InodeHandle> {
598608
debug!("inode_to_handle: inode={inode}");
599609
let data = self
@@ -1205,7 +1215,7 @@ impl FileSystem for PassthroughFs {
12051215

12061216
if self.init_inode != 0 && name == _init_name {
12071217
let mut st: bindings::stat64 = unsafe { mem::zeroed() };
1208-
st.st_size = INIT_BINARY.len() as i64;
1218+
st.st_size = self.init_payload()?.len() as i64;
12091219
st.st_ino = self.init_inode;
12101220
st.st_mode = 0o100_755;
12111221

@@ -1457,15 +1467,18 @@ impl FileSystem for PassthroughFs {
14571467
) -> io::Result<usize> {
14581468
debug!("read: {inode:?}");
14591469
if inode == self.init_inode {
1470+
let init_payload = self.init_payload()?;
14601471
let off: usize = offset
14611472
.try_into()
14621473
.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)]);
1474+
if off >= init_payload.len() {
1475+
// Read past EOF should return 0 bytes.
1476+
return Ok(0);
1477+
}
1478+
1479+
let remaining = init_payload.len() - off;
1480+
let len = remaining.min(size as usize);
1481+
return w.write(&init_payload[off..(off + len)]);
14691482
}
14701483

14711484
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: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,31 @@ 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+
panic!("failed to compile init/init.c with {cc}: {status}");
4343
}
44+
4445
init_bin
4546
}
4647

4748
fn main() {
49+
let manifest_dir = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
50+
let repo_init_bin = manifest_dir.join("../..").join("init/init");
51+
println!("cargo:rerun-if-changed={}", repo_init_bin.display());
52+
4853
let init_binary_path = std::env::var_os("KRUN_INIT_BINARY_PATH")
4954
.map(PathBuf::from)
50-
.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
55-
});
55+
.or_else(|| {
56+
if repo_init_bin.exists() {
57+
Some(repo_init_bin)
58+
} else {
59+
None
60+
}
61+
})
62+
.unwrap_or_else(build_default_init);
63+
64+
// SAFETY: The build script is single threaded.
65+
unsafe { std::env::set_var("KRUN_INIT_BINARY_PATH", &init_binary_path) };
66+
5667
println!(
5768
"cargo:rustc-env=KRUN_INIT_BINARY_PATH={}",
5869
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)