From 0e22f932e706af95a520e36b4ccdf784d9a79b8f Mon Sep 17 00:00:00 2001 From: Jared Baur Date: Wed, 12 Feb 2025 11:32:33 -0800 Subject: [PATCH] Fix firmware inclusion in initrd --- build.zig | 162 +++++++++++++++------------- compress-firmware.nix | 51 +++++++++ flake.lock | 6 +- options.nix | 9 +- pkgs/linux/check_config.bash | 24 ----- pkgs/linux/default.nix | 20 +++- scripts/install.bash | 7 -- scripts/update.bash | 4 - src/cpio.zig | 48 ++++----- src/runner.zig | 11 +- src/tboot-bless-boot-generator.zig | 51 +++++++-- src/tboot-bless-boot.zig | 6 +- src/tboot-initrd.zig | 166 +++++++++++++++-------------- src/tboot-nixos-install.zig | 21 ++-- src/tboot-sign.zig | 10 +- src/utils.zig | 16 +++ src/vpd.zig | 2 +- src/ymodem.zig | 2 +- 18 files changed, 355 insertions(+), 261 deletions(-) create mode 100644 compress-firmware.nix delete mode 100644 pkgs/linux/check_config.bash delete mode 100644 scripts/install.bash delete mode 100644 scripts/update.bash diff --git a/build.zig b/build.zig index 0b6eebe1..0129e5ce 100644 --- a/build.zig +++ b/build.zig @@ -1,4 +1,7 @@ const std = @import("std"); +const utils = @import("./src/utils.zig"); + +const TBOOT_INITRD_NAME = "tboot-initrd"; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions( @@ -46,84 +49,19 @@ pub fn build(b: *std.Build) !void { }); const linux_headers_module = linux_headers_translated.addModule("linux_headers"); - if (with_loader) { - const tboot_loader = b.addExecutable(.{ - .name = "tboot-loader", - .root_source_file = b.path("src/tboot-loader.zig"), - .target = target, - .optimize = tboot_loader_optimize, - .strip = optimize != std.builtin.OptimizeMode.Debug, - }); - tboot_loader.root_module.addAnonymousImport("test_key", .{ - .root_source_file = b.path("tests/keys/tboot/key.der"), - }); - tboot_loader.root_module.addImport("linux_headers", linux_headers_module); - - var run_tboot_initrd = b.addSystemCommand(&.{"tboot-initrd"}); - - // TODO(jared): Would be nicer to have generic - // --file=tboot_loader:/init CLI interface, but don't know how to - // obtain path and string format it into that form. Further, would - // be nicer to not shell-out to a separate tool at all and just do - // the CPIO generation in here. - run_tboot_initrd.addPrefixedFileArg("-i", tboot_loader.getEmittedBin()); - - if (firmware_directory) |directory| { - const directory_ = b.addWriteFiles().addCopyDirectory( - .{ .cwd_relative = directory }, - "", - .{}, - ); - - run_tboot_initrd.addPrefixedDirectoryArg("-d", directory_); - } - - const cpio_output_file = run_tboot_initrd.addPrefixedOutputFileArg( - "-o", - "tboot-loader.cpio", - ); - - run_tboot_initrd.expectExitCode(0); - - const cpio_archive = b.addInstallFile( - cpio_output_file, - "tboot-loader.cpio", - ); - - // install the cpio archive during "zig build install" - b.getInstallStep().dependOn(&cpio_archive.step); - - const runner_tool = b.addRunArtifact(b.addExecutable(.{ - .name = "tboot-runner", - .target = b.graph.host, - .root_source_file = b.path("src/runner.zig"), - })); - runner_tool.step.dependOn(&cpio_archive.step); - runner_tool.addArg(b.makeTempPath()); - runner_tool.addFileArg(cpio_archive.source); - - var env = try std.process.getEnvMap(b.allocator); - defer env.deinit(); - - if (env.get("TINYBOOT_KERNEL")) |kernel| { - runner_tool.addArg(kernel); - } - - // extra args passed through to qemu - if (b.args) |args| { - runner_tool.addArgs(args); - } - - const run_step = b.step("run", "Run in qemu"); - run_step.dependOn(&runner_tool.step); - } + // For re-usage with building the tboot-loader initrd, if we are also + // building tools. + var maybe_tboot_initrd_tool: ?*std.Build.Step.Compile = null; if (with_tools) { - const tboot_initrd_tool = b.addExecutable(.{ - .name = "tboot-initrd", + maybe_tboot_initrd_tool = b.addExecutable(.{ + .name = TBOOT_INITRD_NAME, .target = target, + .optimize = optimize, .root_source_file = b.path("src/tboot-initrd.zig"), }); + + var tboot_initrd_tool = maybe_tboot_initrd_tool.?; tboot_initrd_tool.linkLibC(); tboot_initrd_tool.linkSystemLibrary("liblzma"); tboot_initrd_tool.root_module.addImport("clap", clap.module("clap")); @@ -146,6 +84,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, .strip = optimize != std.builtin.OptimizeMode.Debug, }); + tboot_bless_boot_generator.root_module.addImport("clap", clap.module("clap")); b.installArtifact(tboot_bless_boot_generator); const tboot_sign = b.addExecutable(.{ @@ -195,6 +134,83 @@ pub fn build(b: *std.Build) !void { b.installArtifact(tboot_vpd); } + if (with_loader) { + const tboot_loader = b.addExecutable(.{ + .name = "tboot-loader", + .root_source_file = b.path("src/tboot-loader.zig"), + .target = target, + .optimize = tboot_loader_optimize, + .strip = optimize != std.builtin.OptimizeMode.Debug, + }); + tboot_loader.root_module.addAnonymousImport("test_key", .{ + .root_source_file = b.path("tests/keys/tboot/key.der"), + }); + tboot_loader.root_module.addImport("linux_headers", linux_headers_module); + + // First look for a local build of tboot-initrd, helpful for iteration + // on the tool itself, otherwise use the one that exists on $PATH. + var run_tboot_initrd = if (maybe_tboot_initrd_tool) |tboot_initrd_tool| + b.addRunArtifact(tboot_initrd_tool) + else + b.addSystemCommand(&.{TBOOT_INITRD_NAME}); + + // TODO(jared): Would be nicer to have generic + // --file=tboot_loader:/init CLI interface, but don't know how to + // obtain path and string format it into that form. Further, would + // be nicer to not shell-out to a separate tool at all and just do + // the CPIO generation in here. + run_tboot_initrd.addPrefixedFileArg("-i", tboot_loader.getEmittedBin()); + + if (firmware_directory) |directory| { + const directory_ = b.addWriteFiles().addCopyDirectory( + .{ .cwd_relative = directory }, + "", + .{}, + ); + + run_tboot_initrd.addPrefixedDirectoryArg("-d", directory_); + } + + const cpio_output_file = run_tboot_initrd.addPrefixedOutputFileArg( + "-o", + "tboot-loader.cpio", + ); + + run_tboot_initrd.expectExitCode(0); + + const cpio_archive = b.addInstallFile( + cpio_output_file, + "tboot-loader.cpio", + ); + + // install the cpio archive during "zig build install" + b.getInstallStep().dependOn(&cpio_archive.step); + + const runner_tool = b.addRunArtifact(b.addExecutable(.{ + .name = "tboot-runner", + .target = b.graph.host, + .root_source_file = b.path("src/runner.zig"), + })); + runner_tool.step.dependOn(&cpio_archive.step); + runner_tool.addArg(b.makeTempPath()); + runner_tool.addFileArg(cpio_archive.source); + + var env = try std.process.getEnvMap(b.allocator); + defer env.deinit(); + + if (env.get("TINYBOOT_KERNEL")) |kernel| { + runner_tool.addArg(kernel); + } + + // extra args passed through to qemu + if (b.args) |args| { + runner_tool.addArgs(args); + } + + const run_step = b.step("run", "Run in qemu"); + run_step.dependOn(&runner_tool.step); + } + const unit_tests = b.addTest(.{ .root_source_file = b.path("src/test.zig"), .target = target, diff --git a/compress-firmware.nix b/compress-firmware.nix new file mode 100644 index 00000000..dfc42a75 --- /dev/null +++ b/compress-firmware.nix @@ -0,0 +1,51 @@ +# This is adapted from https://github.com/nixos/nixpkgs/blob/5e4947a31bd21b33ccabcb9ff06d685b68d1e9c4/pkgs/build-support/kernel/compress-firmware.nix, +# but dereferences all symlinks so that the zig build system is capable of +# including all paths we want. See https://github.com/ziglang/zig/blob/53216d2f22053ca94a68f5da234038c01f73d60f/lib/std/Build/Step/WriteFile.zig#L232. + +{ + runCommand, + lib, + zstd, +}: + +firmwares: + +let + compressor = { + ext = "xz"; + nativeBuildInputs = [ ]; + cmd = file: target: ''xz -9c -T1 -C crc32 --lzma2=dict=2MiB "${file}" > "${target}"''; + }; +in + +runCommand "firmware-xz" + { + allowedRequisites = [ ]; + inherit (compressor) nativeBuildInputs; + } + ( + lib.concatLines ( + map (firmware: '' + mkdir -p $out/lib + (cd ${firmware} && find lib/firmware -type d -print0) | + (cd $out && xargs -0 mkdir -v --) + (cd ${firmware} && find lib/firmware -type f -print0) | + (cd $out && xargs -0rtP "$NIX_BUILD_CORES" -n1 \ + sh -c '${compressor.cmd "${firmware}/$1" "$1.${compressor.ext}"}' --) + (cd ${firmware} && find lib/firmware -type l) | while read link; do + target="$(readlink "${firmware}/$link")" + if [ -f "${firmware}/$link" ]; then + cp -vL -- "''${target/^${firmware}/$out}.${compressor.ext}" "$out/$link.${compressor.ext}" + else + echo HI + cp -vrL -- "''${target/^${firmware}/$out}" "$out/$link" + fi + done + + find $out + + echo "Checking for broken symlinks:" + find -L $out -type l -print -execdir false -- '{}' '+' + '') firmwares + ) + ) diff --git a/flake.lock b/flake.lock index d155c85f..3d22d46f 100644 --- a/flake.lock +++ b/flake.lock @@ -102,11 +102,11 @@ ] }, "locked": { - "lastModified": 1739320185, - "narHash": "sha256-PkKANseOVBpxdaWSWFgvyC8VXmGuMoYpGTjjNZy0tVU=", + "lastModified": 1739362309, + "narHash": "sha256-p9TEl8E+LKKsrU2WqXsFilKg8geHnHjzOY3KXSbGwQE=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "e24790ca464357d7286d62f3558228dfb8d81aee", + "rev": "1d0da603c0177b84a73768000e7fa40c46598149", "type": "github" }, "original": { diff --git a/options.nix b/options.nix index d576f806..fd7b0cb9 100644 --- a/options.nix +++ b/options.nix @@ -193,14 +193,7 @@ in firmware = mkOption { type = types.listOf types.package; default = [ ]; - apply = - list: - pkgs.buildEnv { - name = "firmware"; - paths = map pkgs.compressFirmwareXz list; - pathsToLink = [ "/lib/firmware" ]; - ignoreCollisions = true; - }; + apply = pkgs.callPackage ./compress-firmware.nix { }; }; }; diff --git a/pkgs/linux/check_config.bash b/pkgs/linux/check_config.bash deleted file mode 100644 index fa1d8d44..00000000 --- a/pkgs/linux/check_config.bash +++ /dev/null @@ -1,24 +0,0 @@ -# shellcheck shell=bash - -set -o errexit -set -o nounset -set -o pipefail - -start_config=$1 -end_config=$2 - -missing=() -while read -r line; do - if ! grep --silent "$line" "$end_config"; then - missing+=("$line") - fi -done <"$start_config" - -if [[ ${#missing[@]} -gt 0 ]]; then - echo - for line in "${missing[@]}"; do - echo "\"$line\" not found in final config!" - done - echo - exit 1 -fi diff --git a/pkgs/linux/default.nix b/pkgs/linux/default.nix index a59b069d..1fe38d30 100644 --- a/pkgs/linux/default.nix +++ b/pkgs/linux/default.nix @@ -25,7 +25,25 @@ stdenv.mkDerivation { postConfigure = '' cat $kconfigPath $extraConfigPath > all.config make -j$NIX_BUILD_CORES ARCH=${stdenv.hostPlatform.linuxArch} KCONFIG_ALLCONFIG=1 allnoconfig - bash ${./check_config.bash} all.config .config + + start_config=all.config + end_config=.config + + missing=() + while read -r line; do + if ! grep --silent "$line" "$end_config"; then + missing+=("$line") + fi + done <"$start_config" + + if [[ ''${#missing[@]} -gt 0 ]]; then + echo + for line in "''${missing[@]}"; do + echo "\"$line\" not found in final config!" + done + echo + exit 1 + fi ''; buildFlags = [ "DTC_FLAGS=-@" diff --git a/scripts/install.bash b/scripts/install.bash deleted file mode 100644 index 66ea61c5..00000000 --- a/scripts/install.bash +++ /dev/null @@ -1,7 +0,0 @@ -# shellcheck shell=bash - -flashrom --programmer internal --wp-disable -vpd -f OLD_ROM -l | grep VPD_INHERIT_FROM_OLD_GREP_FLAGS | xargs -n1 vpd -f NEW_ROM -s -flashrom --programmer internal --wp-range 0 0 -flashrom --programmer internal --write NEW_ROM EXTRA_FLAGS -flashrom --programmer internal --wp-range START LENGTH --wp-enable diff --git a/scripts/update.bash b/scripts/update.bash deleted file mode 100644 index aa69c1fa..00000000 --- a/scripts/update.bash +++ /dev/null @@ -1,4 +0,0 @@ -# shellcheck shell=bash - -# TODO(jared): detect which slot to write to -flashrom --programmer internal --noverify-all --fmap --include RW_SECTION_A --write NEW_ROM diff --git a/src/cpio.zig b/src/cpio.zig index fae06eb2..ba2f300b 100644 --- a/src/cpio.zig +++ b/src/cpio.zig @@ -36,7 +36,7 @@ const CpioEntryType = enum { pub const CpioArchive = @This(); -dest: *std.io.StreamSource, +destination: *std.io.StreamSource, ino: u32 = 0, total_written: usize = 0, @@ -45,8 +45,8 @@ const Error = error{ UnexpectedSource, }; -pub fn init(dest: *std.io.StreamSource) !@This() { - return @This(){ .dest = dest }; +pub fn init(destination: *std.io.StreamSource) !@This() { + return @This(){ .destination = destination }; } pub fn addEntry( @@ -103,7 +103,7 @@ pub fn addEntry( .namesize = @intCast(filepath_len), }; - try self.dest.writer().print("{X:0>6}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{ + try self.destination.writer().print("{X:0>6}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{ header.magic, header.ino, header.mode, @@ -121,13 +121,13 @@ pub fn addEntry( }); self.total_written += ASCII_CPIO_HEADER_SIZE; - try self.dest.writer().writeAll(path); - try self.dest.writer().writeByte(0); // null terminator + try self.destination.writer().writeAll(path); + try self.destination.writer().writeByte(0); // null terminator self.total_written += filepath_len; // pad the file name const header_padding = (4 - ((ASCII_CPIO_HEADER_SIZE + filepath_len) % 4)) % 4; - try self.dest.writer().writeByteNTimes(0, header_padding); + try self.destination.writer().writeByteNTimes(0, header_padding); self.total_written += header_padding; if (source) |source_| { @@ -139,14 +139,14 @@ pub fn addEntry( while (pos < end) { try source_.seekTo(pos); const bytes_read = try source_.read(&buf); - try self.dest.writer().writeAll(buf[0..bytes_read]); + try self.destination.writer().writeAll(buf[0..bytes_read]); self.total_written += bytes_read; pos += bytes_read; } // pad the file data const filedata_padding = (4 - (end % 4)) % 4; - try self.dest.writer().writeByteNTimes(0, filedata_padding); + try self.destination.writer().writeByteNTimes(0, filedata_padding); self.total_written += filedata_padding; } } @@ -184,17 +184,17 @@ pub fn finalize(self: *@This()) !void { // Maintain a block size of 512 by adding padding to the end of the // archive. - try self.dest.writer().writeByteNTimes(0, (512 - (self.total_written % 512)) % 512); + try self.destination.writer().writeByteNTimes(0, (512 - (self.total_written % 512)) % 512); } fn handleFile( arena: *std.heap.ArenaAllocator, kind: std.fs.File.Kind, filename: []const u8, - directory_path: []const u8, + current_directory: []const u8, starting_directory: []const u8, archive: *CpioArchive, - directory: std.fs.Dir, + directory: *std.fs.Dir, ) anyerror!void { var fullpath_buf: [std.fs.max_path_bytes]u8 = undefined; const full_entry_path = try directory.realpath(filename, &fullpath_buf); @@ -212,7 +212,7 @@ fn handleFile( arena, starting_directory, archive, - sub_directory, + &sub_directory, ); }, .file => { @@ -221,6 +221,9 @@ fn handleFile( const stat = try file.stat(); var source = std.io.StreamSource{ .file = file }; + + std.log.debug("adding file to archive at {s}", .{entry_path}); + try archive.addFile(entry_path, &source, @intCast(stat.mode)); }, .sym_link => { @@ -232,7 +235,7 @@ fn handleFile( if (std.mem.startsWith(u8, resolved_path, starting_directory)) { const symlink_path = try std.fs.path.join( arena.allocator(), - &.{ directory_path, filename }, + &.{ current_directory, filename }, ); try archive.addSymlink(symlink_path, entry_path); @@ -243,22 +246,13 @@ fn handleFile( arena, stat.kind, resolved_path, - directory_path, + current_directory, starting_directory, archive, directory, ); - - // std.log.warn( - // "Resolved symlink {s} is outside of {s}, refusing to add to CPIO archive", - // .{ resolved_path, starting_directory }, - // ); } }, - // We explicitly don't add these kinds of files. - .block_device => {}, - .character_device => {}, - .unix_domain_socket => {}, else => { std.log.warn( "Do not know how to add file {s} of kind {} to CPIO archive", @@ -272,7 +266,7 @@ pub fn walkDirectory( arena: *std.heap.ArenaAllocator, starting_directory: []const u8, archive: *CpioArchive, - directory: std.fs.Dir, + directory: *std.fs.Dir, ) anyerror!void { var iter = directory.iterate(); @@ -281,8 +275,12 @@ pub fn walkDirectory( const full_directory_path = try directory.realpath(".", &fullpath_buf); const directory_path = try std.fs.path.relative(arena.allocator(), starting_directory, full_directory_path); + std.log.debug("walking directory {s}", .{full_directory_path}); + // We don't need to add the root directory, as it will already exist. if (!std.mem.eql(u8, directory_path, "")) { + // Before iterating through the directory, add the directory itself to + // the archive. try archive.addDirectory(directory_path, 0o755); } diff --git a/src/runner.zig b/src/runner.zig index afaf8eba..f383d1c1 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -1,13 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); - -fn pathExists(p: []const u8) bool { - std.fs.cwd().access(p, .{}) catch { - return false; - }; - - return true; -} +const utils = @import("./utils.zig"); pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); @@ -34,7 +27,7 @@ pub fn main() !void { else => @compileError("don't know how to run qemu on build system"), }); - if (builtin.target.os.tag == .linux and pathExists("/dev/kvm")) { + if (builtin.target.os.tag == .linux and utils.absolutePathExists("/dev/kvm")) { try qemu_args.append("-enable-kvm"); } diff --git a/src/tboot-bless-boot-generator.zig b/src/tboot-bless-boot-generator.zig index 09b3af9a..dc88fa18 100644 --- a/src/tboot-bless-boot-generator.zig +++ b/src/tboot-bless-boot-generator.zig @@ -1,22 +1,53 @@ +const builtin = @import("builtin"); const std = @import("std"); +const clap = @import("clap"); -const GeneratorError = error{ - MissingNormalDir, - MissingEarlyDir, - MissingLateDir, -}; +pub const std_options = std.Options{ .log_level = if (builtin.mode == .Debug) .debug else .info }; pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); - var args = std.process.args(); + const params = comptime clap.parseParamsComptime( + \\-h, --help Display this help and exit. + \\ The normal generator directory. + \\ The early generator directory. + \\ The late generator directory. + \\ + ); - _ = args.next().?; // skip argv[0] - const normal_dir = args.next() orelse return GeneratorError.MissingNormalDir; - const early_dir = args.next() orelse return GeneratorError.MissingEarlyDir; - const late_dir = args.next() orelse return GeneratorError.MissingLateDir; + const parsers = comptime .{ .DIR = clap.parsers.string }; + + const stderr = std.io.getStdErr().writer(); + + var diag = clap.Diagnostic{}; + var res = clap.parse(clap.Help, ¶ms, &parsers, .{ + .diagnostic = &diag, + .allocator = arena.allocator(), + }) catch |err| { + try diag.report(stderr, err); + try clap.usage(stderr, clap.Help, ¶ms); + return; + }; + defer res.deinit(); + + if (res.args.help != 0) { + return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); + } + + if (res.positionals[0] == null or + res.positionals[1] == null or + res.positionals[2] == null) + { + try diag.report(stderr, error.InvalidArgument); + try clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); + return; + } + + const normal_dir = res.positionals[0].?; + const early_dir = res.positionals[1].?; + const late_dir = res.positionals[2].?; _ = normal_dir; _ = late_dir; diff --git a/src/tboot-bless-boot.zig b/src/tboot-bless-boot.zig index 1ec0a3d8..32f88f64 100644 --- a/src/tboot-bless-boot.zig +++ b/src/tboot-bless-boot.zig @@ -1,7 +1,9 @@ +const builtin = @import("builtin"); const std = @import("std"); - const clap = @import("clap"); +pub const std_options = std.Options{ .log_level = if (builtin.mode == .Debug) .debug else .info }; + const DiskBootLoader = @import("./boot/disk.zig"); const BlsEntryFile = DiskBootLoader.BlsEntryFile; @@ -176,7 +178,7 @@ pub fn main() !void { } const esp_mnt = res.args.@"esp-mnt" orelse std.fs.path.sep_str ++ "boot"; - const action = if (res.positionals.len > 0) try Action.fromStr(res.positionals[0].?) else Action.status; + const action = if (res.positionals[0]) |action| try Action.fromStr(action) else Action.status; const kernel_cmdline_file = try std.fs.cwd().openFile("/proc/cmdline", .{}); defer kernel_cmdline_file.close(); diff --git a/src/tboot-initrd.zig b/src/tboot-initrd.zig index f71e5f7e..745615b8 100644 --- a/src/tboot-initrd.zig +++ b/src/tboot-initrd.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const clap = @import("clap"); const CpioArchive = @import("./cpio.zig"); @@ -6,6 +7,8 @@ const C = @cImport({ @cInclude("lzma.h"); }); +pub const std_options = std.Options{ .log_level = if (builtin.mode == .Debug) .debug else .info }; + const BoolArgument = enum { yes, no, @@ -20,6 +23,87 @@ const BoolArgument = enum { } }; +fn compress( + arena: *std.heap.ArenaAllocator, + output: []const u8, + archive_file: std.fs.File, +) !void { + const compressed_output = try std.fmt.allocPrint( + arena.allocator(), + "{s}.xz", + .{output}, + ); + + var compressed_file = try std.fs.cwd().createFile( + compressed_output, + .{ .read = true, .mode = 0o444 }, + ); + defer compressed_file.close(); + + var stream: C.lzma_stream = .{}; + defer C.lzma_end(&stream); + + var lzma_opts: C.lzma_options_lzma = .{}; + if (C.lzma_lzma_preset(&lzma_opts, C.LZMA_PRESET_DEFAULT) != C.LZMA_OK) { + return error.CompressFail; + } + + var filters = [_]C.lzma_filter{.{}} ** (C.LZMA_FILTERS_MAX + 1); + filters[0].id = C.LZMA_FILTER_LZMA2; + filters[0].options = &lzma_opts; + filters[1].id = C.LZMA_VLI_UNKNOWN; + + if (C.lzma_stream_encoder( + &stream, + &filters, + C.LZMA_CHECK_CRC32, // linux kernel expects CRC32 check + ) != C.LZMA_OK) { + return error.CompressFail; + } + + var input_buffer = [_]u8{0} ** 4096; + var output_buffer = [_]u8{0} ** 4096; + + stream.next_in = null; + stream.avail_in = 0; + stream.next_out = &output_buffer; + stream.avail_out = @sizeOf(@TypeOf(output_buffer)); + + try archive_file.seekTo(0); + + var action: C.lzma_action = C.LZMA_RUN; + while (true) { + if (stream.avail_in == 0 and action != C.LZMA_FINISH) { + stream.next_in = &input_buffer; + stream.avail_in = try archive_file.readAll(&input_buffer); + if (stream.avail_in == 0) { + action = C.LZMA_FINISH; + } + } + + const ret = C.lzma_code(&stream, action); + + if (stream.avail_out == 0 or ret == C.LZMA_STREAM_END) { + // write to output file + try compressed_file.writer().writeAll(output_buffer[0 .. output_buffer.len - stream.avail_out]); + + // reset next_out and avail_out + stream.next_out = &output_buffer; + stream.avail_out = @sizeOf(@TypeOf(output_buffer)); + } + + if (ret != C.LZMA_OK) { + if (ret == C.LZMA_STREAM_END) { + break; + } + + return error.CompressFail; + } + } + + try std.fs.cwd().rename(compressed_output, output); +} + pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); @@ -62,7 +146,7 @@ pub fn main() !void { return; } - const compress: bool = if (res.args.compress) |compress| compress.to_bool() else true; + const do_compress: bool = if (res.args.compress) |do_compress| do_compress.to_bool() else true; const init: []const u8 = res.args.init.?; const directories: []const []const u8 = res.args.directory; const output: []const u8 = res.args.output.?; @@ -88,86 +172,12 @@ pub fn main() !void { .{ .iterate = true }, ); defer dir.close(); - try CpioArchive.walkDirectory(&arena, directory_path, &archive, dir); + try CpioArchive.walkDirectory(&arena, directory_path, &archive, &dir); } try archive.finalize(); - // TODO(jared): factor out into function - if (compress) { - const compressed_output = try std.fmt.allocPrint( - arena.allocator(), - "{s}.xz", - .{output}, - ); - - var compressed_file = try std.fs.cwd().createFile( - compressed_output, - .{ .read = true, .mode = 0o444 }, - ); - defer compressed_file.close(); - - var stream: C.lzma_stream = .{}; - defer C.lzma_end(&stream); - - var lzma_opts: C.lzma_options_lzma = .{}; - if (C.lzma_lzma_preset(&lzma_opts, C.LZMA_PRESET_DEFAULT) != C.LZMA_OK) { - return error.CompressFail; - } - - var filters = [_]C.lzma_filter{.{}} ** (C.LZMA_FILTERS_MAX + 1); - filters[0].id = C.LZMA_FILTER_LZMA2; - filters[0].options = &lzma_opts; - filters[1].id = C.LZMA_VLI_UNKNOWN; - - if (C.lzma_stream_encoder( - &stream, - &filters, - C.LZMA_CHECK_CRC32, // linux kernel expects CRC32 check - ) != C.LZMA_OK) { - return error.CompressFail; - } - - var input_buffer = [_]u8{0} ** 4096; - var output_buffer = [_]u8{0} ** 4096; - - stream.next_in = null; - stream.avail_in = 0; - stream.next_out = &output_buffer; - stream.avail_out = @sizeOf(@TypeOf(output_buffer)); - - try archive_file.seekTo(0); - - var action: C.lzma_action = C.LZMA_RUN; - while (true) { - if (stream.avail_in == 0 and action != C.LZMA_FINISH) { - stream.next_in = &input_buffer; - stream.avail_in = try archive_file.readAll(&input_buffer); - if (stream.avail_in == 0) { - action = C.LZMA_FINISH; - } - } - - const ret = C.lzma_code(&stream, action); - - if (stream.avail_out == 0 or ret == C.LZMA_STREAM_END) { - // write to output file - try compressed_file.writer().writeAll(output_buffer[0 .. output_buffer.len - stream.avail_out]); - - // reset next_out and avail_out - stream.next_out = &output_buffer; - stream.avail_out = @sizeOf(@TypeOf(output_buffer)); - } - - if (ret != C.LZMA_OK) { - if (ret == C.LZMA_STREAM_END) { - break; - } - - return error.CompressFail; - } - } - - try std.fs.cwd().rename(compressed_output, output); + if (do_compress) { + try compress(&arena, output, archive_file); } } diff --git a/src/tboot-nixos-install.zig b/src/tboot-nixos-install.zig index e2c9a6a7..7863da51 100644 --- a/src/tboot-nixos-install.zig +++ b/src/tboot-nixos-install.zig @@ -1,8 +1,11 @@ +const builtin = @import("builtin"); const std = @import("std"); const path = std.fs.path; - +const utils = @import("./utils.zig"); const clap = @import("clap"); +pub const std_options = std.Options{ .log_level = if (builtin.mode == .Debug) .debug else .info }; + const DiskBootLoader = @import("./boot/disk.zig"); const signFile = @import("./tboot-sign.zig").signFile; const BootSpecV1 = @import("./bootspec.zig").BootSpecV1; @@ -21,7 +24,7 @@ fn ensureFilesystemState( try esp.makePath("loader/entries"); } - if (!pathExists(esp, "loader/entries.srel")) { + if (!utils.pathExists(esp, "loader/entries.srel")) { if (!args.dry_run) { var entries_srel_file = try esp.createFile("loader/entries.srel", .{}); defer entries_srel_file.close(); @@ -34,14 +37,6 @@ fn ensureFilesystemState( std.log.debug("filesystem state is good", .{}); } -fn pathExists(d: std.fs.Dir, p: []const u8) bool { - d.access(p, .{}) catch { - return false; - }; - - return true; -} - fn installGeneration( allocator: std.mem.Allocator, known_files: *StringSet, @@ -69,7 +64,7 @@ fn installGeneration( &.{ args.efi_sys_mount_point, linux_target }, ); - if (!pathExists(esp, linux_target)) { + if (!utils.pathExists(esp, linux_target)) { if (!args.dry_run) { try signFile( allocator, @@ -105,7 +100,7 @@ fn installGeneration( initrd_target, }); - if (!pathExists(esp, initrd_target)) { + if (!utils.pathExists(esp, initrd_target)) { if (!args.dry_run) { try signFile( allocator, @@ -291,7 +286,7 @@ pub fn main() !void { return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); } - if (res.positionals.len != 1 or res.args.@"private-key" == null or res.args.@"public-key" == null) { + if (res.positionals[0] == null or res.args.@"private-key" == null or res.args.@"public-key" == null) { try diag.report(stderr, error.InvalidArgument); try clap.usage(stderr, clap.Help, ¶ms); return; diff --git a/src/tboot-sign.zig b/src/tboot-sign.zig index 3339f27b..b2c09584 100644 --- a/src/tboot-sign.zig +++ b/src/tboot-sign.zig @@ -1,5 +1,5 @@ +const builtin = @import("builtin"); const std = @import("std"); - const clap = @import("clap"); const C = @cImport({ @@ -11,6 +11,8 @@ const C = @cImport({ @cInclude("openssl/engine.h"); }); +pub const std_options = std.Options{ .log_level = if (builtin.mode == .Debug) .debug else .info }; + const MODULE_SIG_STRING = "~Module signature appended~\n"; // https://github.com/torvalds/linux/blob/ec9eeb89e60d86fcc0243f47c2383399ce0de8f8/include/linux/module_signature.h#L17 @@ -304,7 +306,11 @@ pub fn main() !void { return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); } - if (res.positionals.len != 2 or res.args.@"private-key" == null or res.args.@"public-key" == null) { + if (res.positionals[0] == null or + res.positionals[1] == null or + res.args.@"private-key" == null or + res.args.@"public-key" == null) + { try diag.report(stderr, error.InvalidArgument); try clap.usage(stderr, clap.Help, ¶ms); return; diff --git a/src/utils.zig b/src/utils.zig index 57eb99cc..d9afb347 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1,5 +1,21 @@ const std = @import("std"); +pub fn pathExists(d: std.fs.Dir, p: []const u8) bool { + d.access(p, .{}) catch { + return false; + }; + + return true; +} + +pub fn absolutePathExists(p: []const u8) bool { + std.fs.cwd().access(p, .{}) catch { + return false; + }; + + return true; +} + pub fn enumFromStr(T: anytype, value: []const u8) !T { inline for (std.meta.fields(T)) |field| { if (std.mem.eql(u8, field.name, value)) { diff --git a/src/vpd.zig b/src/vpd.zig index 02b4dae5..8f8474a6 100644 --- a/src/vpd.zig +++ b/src/vpd.zig @@ -253,7 +253,7 @@ pub fn main() !void { return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); } - if (res.positionals.len != 1 or res.args.file == null) { + if (res.positionals[0] == null or res.args.file == null) { try diag.report(stderr, error.InvalidArgument); try clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); return; diff --git a/src/ymodem.zig b/src/ymodem.zig index 4ec871ff..7e94b2c9 100644 --- a/src/ymodem.zig +++ b/src/ymodem.zig @@ -418,7 +418,7 @@ pub fn main() !void { return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); } - if (res.positionals.len != 1 or res.args.directory == null or res.args.tty == null) { + if (res.positionals[0] == null or res.args.directory == null or res.args.tty == null) { try diag.report(stderr, error.InvalidArgument); try clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); return;