WIP: Move krun-init to a separate .so library (similar to libkrunfw)#706
Draft
mtjhrc wants to merge 41 commits into
Draft
WIP: Move krun-init to a separate .so library (similar to libkrunfw)#706mtjhrc wants to merge 41 commits into
mtjhrc wants to merge 41 commits into
Conversation
…th krun_add_disk Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
These were replaced by krun_add_disk. Also remove the internal root_block_cfg/data_block_cfg fields and their setters, and simplify get_block_cfg() now that the legacy compat path is gone. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
…_set_net_mac These were replaced by krun_add_net_unixstream, krun_add_net_unixgram, and krun_add_net_tap (which take mac as a parameter directly). Also remove the internal LegacyNetworkConfig enum, legacy_net_cfg and legacy_mac fields, the compat path in krun_start_enter that converted them to the new net backend, and the now-unused NET_COMPAT_FEATURES constant. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
krun_set_log_level set KRUN_NITRO_DEBUG when level==4 (debug), but krun_init_log did not. Fix the omission so removing krun_set_log_level doesn't regress nitro debug logging. Also fix the condition to level >= 4 so that trace (level 5) also enables nitro debug. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Superseded by krun_init_log which provides control over target fd, log style, and env override options. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
This function has been returning -EINVAL unconditionally. Remove it. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Replace implicit console creation with explicit krun_add_virtio_console_default calls. Also replace krun_set_console_output in nitro.c with krun_add_virtio_console_default. No test or example relies on implicit console injection anymore. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
…nd implicit console Console creation is now fully explicit via krun_add_virtio_console_default or krun_add_virtio_console_multiport. No console is created unless the caller requests one. Remove the disable_implicit_console field from VmResources, the implicit console and serial device creation paths in builder.rs, the console_output field and setter on VmResources, and krun_set_console_output (kept only behind cfg(aws-nitro) where NitroEnclave still needs it). Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
- test_tsi_tcp_guest_connect: add krun_add_vsock(ctx, KRUN_TSI_HIJACK_INET) - test_tsi_tcp_guest_listen: same - test_vsock_guest_connect: add krun_add_vsock(ctx, 0) - chroot_vm.c: replace krun_disable_implicit_vsock + vhost-user with explicit krun_add_vsock when not using vhost-user-vsock No test or example relies on implicit vsock creation anymore. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Vsock creation is now fully explicit via krun_add_vsock(). No vsock device is created unless the caller requests one. Remove the Implicit variant from VsockConfig, the implicit vsock creation heuristics in krun_start_enter, and krun_disable_implicit_vsock. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
All krun_disable_implicit_* functions are gone. The 2.0 API requires explicit resource creation. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Replace the C-based build_default_init() in src/devices/build.rs with a Rust crate (init/) compiled via a cargo subprocess. The new build.rs probes whether the active rustc supports the $(uname -m)-unknown-linux-musl target (for a static binary) and falls back to the native target with a user-visible warning if not. The KRUN_INIT_BINARY_PATH override mechanism is preserved so that out-of-tree binaries (e.g. pre-built SEV or TDX images) can still be injected without rebuilding. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Add init/src/fs.rs with: - mount_once(): helper that treats EBUSY as success - mount_filesystems(): mounts devtmpfs, proc, sysfs, cgroup2, devpts, tmpfs(/dev/shm), and creates the /dev/fd symlink - is_mount_point(): parses /proc/mounts (avoids triggering Podman auto-mounts that stat() would cause) - mount_tmpfs(): mounts a tmpfs at an arbitrary path Implement mount_tee_block_root() function used by both SEV and TDX features to mount /dev/vda and chroot into it. For amd-sev this replaces the previous LUKS/KBS attestation path entirely. The SEV and TDX boot paths are now identical at the init level. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Extend fs.rs with: - try_mount(): mounts with a known fstype, or probes /proc/filesystems when fstype is None - mount_block_root_device(): handles KRUN_BLOCK_ROOT_DEVICE by mounting the block device at /newroot, issuing KRUN_REMOVE_ROOT_DIR_IOCTL to drop the virtiofs temporary root, then pivoting with MS_MOVE - mount_shared_root(): sets MS_REC|MS_SHARED propagation on / Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Port init/dhcp.c to Rust in init/src/dhcp.rs. The public surface is a single do_dhcp(iface) function with the same behaviour as the C version: - Sends DHCPDISCOVER with Rapid Commit (option 80) - On DHCPACK: applies address, route, MTU, and DNS directly - On DHCPOFFER: completes the 4-way handshake, then applies - On no response: returns Ok (VM may be IPv6-only) Netlink structs not exposed by libc (ifinfomsg, ifaddrmsg, rtmsg) are defined locally with #[repr(C)]. sockaddr_nl and sockaddr_in are zero-initialised via mem::zeroed() to handle opaque padding fields. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Add init/src/config.rs, replacing the hand-rolled jsmn-based parser with serde_json. Parses /.krun_config.json (or KRUN_CONFIG env var) and returns a Config struct with: - argv: Entrypoint ++ (args | Cmd), or None if absent - workdir: WorkingDir or Cwd - tmpfs: first tmpfs mount destination not already mounted Environment variables from the Env array are applied during parsing, with HOME and TERM always overwritten, all others set only if unset. A missing or unparseable config file is silently ignored. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Add setup_network() and setup_dhcp() to env.rs. setup_network() brings up lo unconditionally. setup_dhcp() checks that the interface exists before calling do_dhcp(), and logs a warning on failure rather than aborting (DHCP failure is non-fatal — the VM may be IPv6-only or have no network). Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Extend env.rs with: - apply_hostname(): sets hostname from HOSTNAME env var, defaulting to "localhost" - apply_env(): maps KRUN_HOME -> HOME and KRUN_TERM -> TERM - apply_rlimits(): parses the KRUN_RLIMITS comma-separated list of id,cur,max triples and applies each via setrlimit(2) Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Add exec.rs with: - setup_redirects(): walks /sys/class/virtio-ports and dup2s krun-stdin/stdout/stderr onto the corresponding file descriptors - set_exit_code(): reports the workload exit code to the host via KRUN_EXIT_CODE_IOCTL, only when the root fs is virtiofs - run_workload(): forks so PID 1 can reap children; the child calls exec_workload() which sets up redirects and execvp's the argv. Parent waits for the child, reports exit code, syncs, and reboots. KRUN_INIT_PID1=1 skips the fork and exec_workload directly as PID 1. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Connect all modules in main() in order: 1. mount_block_root() [amd-sev | tdx] 2. mount_filesystems() 3. mount_block_root_device() [KRUN_BLOCK_ROOT_DEVICE] 4. mount_shared_root() 5. setsid + TIOCSCTTY 6. setup_network() 7. config::load() 8. mount_tmpfs() [config tmpfs mount] 9. apply_env / apply_hostname / apply_rlimits 10. chdir to workdir 11. run_workload(argv) Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Add init/src/freebsd.rs with: - kenv_get(): reads a variable from the FreeBSD kernel environment via kenv(2), which is the source of env vars for init before the process environment is set up - populate_env_from_kenv(): imports the known KRUN_* variables from kenv into std::env at startup so the rest of the code can use std::env::var uniformly on both platforms - open_console(): replicates login_tty(3) without linking libutil — revokes existing opens of /dev/console, opens it, creates a new session via setsid(2), sets the controlling terminal via TIOCSCTTY, and dup2s it onto stdio; falls back to /dev/null + /init.log - mount_config_iso() / unmount_config_iso(): mounts the KRUN_CONFIG ISO 9660 image at /mnt via nmount(2) so the JSON config file can be read, then unmounts it afterwards Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Connect the FreeBSD helpers into the boot sequence:
- open_console() and populate_env_from_kenv() are called at the very
start of main() before anything else
- setsid/TIOCSCTTY are Linux-only; open_console() handles session setup
on FreeBSD
- setlogin("root") is called on FreeBSD after console setup
- KRUN_DHCP and DHCP setup are Linux-only
- If KRUN_CONFIG is not set, mount_config_iso() is attempted; the ISO
is unmounted immediately after config::load() returns
- fs::* mounts and mount_shared_root are Linux-only
- exec_workload() calls open_console() on FreeBSD instead of
setup_redirects(), giving the child process a fresh controlling
terminal before execvp
Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me>
Assisted-by: Claude Code:claude-sonnet-4.6
Replace the C-based BSD init build rule (which referenced the now-deleted init/init.c) with a cargo build rule targeting the correct Rust triple. Makefile: - Remove dead INIT_SRC = init/init.c variable. - Derive FREEBSD_RUST_TARGET from the host ARCH with arm64→aarch64 substitution to get the correct Rust triple. - Set CARGO_BSD_RUSTFLAGS with the clang cross-linker flags (mirroring the existing CC_BSD setup) so cargo can link for FreeBSD. - aarch64-unknown-freebsd is a Tier 3 target with no prebuilt std; use +nightly -Z build-std for that case. setup-build-env: - Add rustup target add x86_64-unknown-freebsd (Tier 2, prebuilt std). - Install nightly toolchain + rust-src for the aarch64 FreeBSD case. cross-compilation.yml: - Add clang to the Linux cross-compilation dependencies so the FreeBSD linker flags resolve correctly on Linux runners. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Implements the timesync feature behind the `timesync` cargo feature flag. Receives host-side nanosecond timestamps over AF_VSOCK/SOCK_DGRAM on port 123 and applies them via clock_settime when the delta exceeds 100ms. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Delete init/init.c, init/dhcp.c, init/dhcp.h, init/jsmn.h, and the entire init/tee/ directory (snp_attest.c/h and the KBS client). The amd-sev feature no longer performs LUKS unlock or KBS attestation — it mounts /dev/vda as ext4 like the tdx path does. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me> Assisted-by: Claude Code:claude-sonnet-4.6
Port of cd8b2be. The temporary root directory hack has been replaced by NullFs, so the ioctl that cleaned it up is no longer needed. Assisted-by: Claude Code: claude-sonnet-4-6 Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me>
Port of 2593acc. When TSI is active, brings up dummy0 and assigns it 10.0.0.1/8 so applications that probe for network availability see a configured interface. Silently skips setup if the dummy driver is absent in the kernel. Assisted-by: Claude Code: claude-sonnet-4-6 Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me>
Replace all callers of krun_set_root with explicit krun_add_virtiofs3(ctx, "/dev/root", path, 0, false) calls. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Callers should use krun_add_virtiofs3() with KRUN_FS_ROOT_TAG
("/dev/root") instead, which provides explicit control over DAX
window size and read-only mode.
Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
Prepare init/ to serve as a self-contained subtree for all init-related crates. Move the Rust PID-1 binary (Cargo.toml, src/) into init/init-binary/ and the init-blob crate from src/init-blob/ to init/init-blob/ so that new crates (init-blob-cdylib, etc.) can be added alongside them. init.c and aws-nitro/ stay at their existing locations. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Add four crates that together produce libkrun-init — a standalone shared library for building init configurations with a C FFI: - init-blob: Config/ConfigBuilder/GuestFile types with #[ffier::exportable]. Serializes to OCI runtime-spec JSON matching what the Rust init (PR containers#670) parses. ConfigError with FfiError derive for proper error handling across FFI. - init-blob-cdylib: produces libkrun_init.so via ffier bridge macros. Includes krun-init-blob-gen binary for generating C headers and Rust client bindings (strong or --weak mode). - init-blob-via-cdylib: strong Rust client (links libkrun_init.so at load time). For consumers that link both libraries. - init-blob-via-cdylib-weak: weak Rust client (dlsym at runtime). For libkrun, which optionally calls into libkrun-init without a hard link dependency. All crates live under init/ alongside the PID-1 binary. Uses library_tag = 2 (reserving 1 for libkrun) and primitives_prefix = "krun" so KrunStr/KrunBytes are shared. Makefile: gen-init-blob-bindings target, versioned install of libkrun_init.so (SONAME 0), C header, and pkg-config file. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Add krun_inject_init(ctx_id, fs_tag, config_handle) C API that takes a KrunInitConfig handle from libkrun-init.so and injects all its guest files into the specified virtiofs device as overlay files. Uses the weak Rust client (krun-init-blob-via-cdylib-weak) to call into libkrun-init.so via dlsym at runtime. Returns -ENOSYS if the library is not loaded. Uses extend_lifetime<T>() instead of raw transmute to extend borrowed data lifetimes. This unsafety will improve once libkrun accepts parameters using references with lifetimes (Rust API), allowing us to encode that InitConfig must outlive the VM. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Migrate integration tests from legacy krun_set_exec/krun_set_env/ krun_set_workdir to the init-blob API: - common.rs: add build_init_config() helper, setup_fs_and_enter now uses krun_inject_init instead of krun_set_exec + krun_set_workdir. - test_augmentfs: replace manual krun_disable_implicit_init + krun_get_default_init + overlay calls with Config::builder() + krun_inject_init. - test_virtiofs_root_ro: use build_init_config + krun_inject_init. - test_root_disk_remount: kept on legacy API (block device root has no virtiofs to inject into). Tests use krun-init-blob-via-cdylib (strong Rust client) to call the init-blob C API. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Replace legacy krun_set_exec/krun_set_workdir/krun_set_rlimits calls with the init-blob config builder + krun_inject_init: - chroot_vm.c: use ConfigBuilder for args, env, workdir, rlimits - consoles.c: use ConfigBuilder for args - nitro.c: use ConfigBuilder for args and env - gui_vm/main.rs: use krun_init::Config::builder() for args All C examples now link -lkrun_init and include <libkrun_init.h>. launch-tee.c is left unchanged (TEE variant has its own init flow). Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Have krun_inject_init() retrieve the kernel init argument from
the Config handle (e.g. "init=/init.krun") and store it in
ContextConfig. krun_start_enter() now uses this stored value
instead of hardcoding init={INIT_PATH}.
This makes the kernel cmdline init= argument explicit: it comes
from the init-blob library, not from libkrun internals. For
backwards compatibility, the default still falls back to
init=/init.krun when krun_inject_init was not called.
Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
Move config::load() before fs::mount_block_root_device() so that /.krun_config.json is read while the initial (virtiofs) root is still accessible. After pivot_root the NullFs is gone and the config file is unreachable. This enables test_root_disk_remount to use krun_inject_init instead of the legacy krun_set_exec/krun_set_workdir path. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Update tests/Cargo.lock for ffier rev bump. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Remove krun_disable_implicit_init() and krun_get_default_init() — the init binary and config are now injected explicitly via krun_inject_init(). - Remove init_virtual_entry() and DEFAULT_INIT_PAYLOAD - Remove disable_implicit_init field from ContextConfig - krun_add_virtiofs3 no longer injects init.krun for /dev/root - krun_set_root_disk_remount no longer injects init.krun - krun_start_enter only adds init= to kernel cmdline when krun_inject_init was called (TEE path unchanged) - Remove test_disable_implicit_init unit test - Remove krun_disable_implicit_init call from test_root_disk_remount Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
Gate krun_set_exec, krun_set_env, krun_set_workdir, krun_set_rlimits and their supporting code behind cfg(tee/aws-nitro). These functions populated the kernel cmdline with KRUN_INIT/KRUN_WORKDIR/KRUN_RLIMITS environment variables — the non-TEE path now uses krun_inject_init with .krun_config.json instead. - Gate ContextConfig fields (workdir, exec_path, env, args, rlimits) - Gate setter/getter methods and collapse_str_array helper - Split kernel cmdline construction: non-TEE only emits init= (from krun_inject_init) and block root; TEE keeps the full KRUN_* env var scheme - Remove function declarations from libkrun.h TEE builds (libkrun-sev, libkrun-tdx) retain these functions unchanged — launch-tee.c still uses them. Assisted-by: OpenCode:claude-opus-4.6 Signed-off-by: Matej Hrica <mhrica@redhat.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce libkrun_init.so — a small standalone library produced by ffier (https://github.com/mtjhrc/ffier) from the init-blob crate, following the same pattern as libkrunfw for kernel delivery. It owns the InitConfig type and exposes it over C FFI via auto-generated bindings. libkrun calls into it at runtime via dlsym, keeping the dependency optional at runtime.
Bundling the init config builder (host side) and the guest PID-1 binary into a single .so ensures they always stay in sync — the config format between them is an internal detail, no longer guaranteed stable across versions. Callers like crun use krun_init_config_from_oci_config_json() to build the config from their existing OCI JSON.
This design also means libkrun does not require using libkrun_init at all — users who want a custom init can inject arbitrary guest files manually via the virtiofs overlay and set their own init= kernel argument. libkrun is fully agnostic to what runs as PID 1.
The new krun_inject_init(ctx, fs_tag) replaces all implicit init magic: the caller explicitly builds an InitConfig handle, then injects it alongside the init binary into a named virtiofs device. libkrun no longer knows or cares what format the init config uses.
On top of #688's deprecated API removal and #670's Rust init rewrite, this PR also:
Depends on