Skip to content

Commit

Permalink
bin/generate-zbm: use mkinitcpio method to calc offsets
Browse files Browse the repository at this point in the history
Newer versions of the EFI stub provided by systemd-boot are incompatible
with the method used by generate-zbm to add sections to the output
bundle. mkinitcpio uses a process that evaluates the sizes of each
component being combined into the EFI bundle and adjusts their section
VMA accordingly.

This has been tested with the latest systemd-boot EFI stub and the most
recent Gummiboot EFI stub.
  • Loading branch information
zdykstra committed Oct 24, 2023
1 parent 9588659 commit 0893838
Showing 1 changed file with 78 additions and 22 deletions.
100 changes: 78 additions & 22 deletions bin/generate-zbm
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ use warnings;
our $VERSION = '2.2.1';

use Getopt::Long qw(:config no_ignore_case auto_version);
use Pod::Usage qw(pod2usage);
use Pod::Usage qw(pod2usage);
use File::Basename;
use File::Temp qw(tempfile tempdir);
use File::Copy;
use File::stat;
use File::Path qw(make_path remove_tree);
use File::Glob qw(:globally :nocase);
use Sort::Versions;
use bigint qw(hex);

use Pod::Usage qw(pod2usage);

Expand Down Expand Up @@ -53,7 +54,7 @@ BEGIN {

my ( %runConf, %config );

$runConf{config} = "/etc/zfsbootmenu/config.yaml";
$runConf{config} = "/etc/zfsbootmenu/config.yaml";
$runConf{bootdir} = "/boot";

GetOptions(
Expand Down Expand Up @@ -713,8 +714,32 @@ EOF
return $namever;
}

# Given a sections size, calculate where the next section should be placed,
# while respecting the stub alignment value
sub increaseBundleOffset {
my ( $step, $offset, $alignment ) = @_;
$offset += int( ( $step + $alignment - 1 ) / $alignment * $alignment );
Log( "New offset is: " . hex($offset) );
return $offset;
}

# Adds the commands necessary to put another section into the EFI bundle,
# and then calculates where the bundle offset has been moved to
sub addBundleSection {
my ( $cmds, $secname, $filename, $offset, $alignment ) = @_;

my $hex_offset = sprintf( "0x%X", $offset );
push( @$cmds, ( "--add-section", "$secname=\"$filename\"" ), qw(--change-section-vma), ("$secname=\"$hex_offset\""),
);

my $sb = stat($filename);
return increaseBundleOffset( $sb->size, $offset, $alignment );

}

# Creates a UEFI bundle from an initramfs and kernel
# Returns the path to the bundle or dies with an error

sub createUEFIBundle {
my ( $imagedir, $kernel, $initramfs ) = @_;

Expand All @@ -734,6 +759,7 @@ sub createUEFIBundle {
exit 1;
}
} else {

# For now, default stub locations are x86_64 only
my @uefi_stub_defaults = qw(
/usr/lib/gummiboot/linuxx64.efi.stub
Expand All @@ -756,45 +782,75 @@ sub createUEFIBundle {
}
}

my @cmd = qw(objcopy);
my ( $uki_alignment, $uki_offset );

# Determine stub alignment, most likely 4096
my @cmd = qw(objdump -p);
push( @cmd, $uefi_stub );

my @output = execute(@cmd);
my $status = pop(@output);
if ( $status eq 0 ) {
foreach my $line (@output) {
if ( $line =~ m/SectionAlignment\s+(\d+)/ ) {
Log( "Alignment is: " . hex($1) );
$uki_alignment = hex($1);
}
}
} else {
print "Unable to determine stub alignment!\n";
exit 1;
}

# Determine initial UKI offset value by grabbing the size and VMA of
# the last section of the EFI stub.
@cmd = qw(objdump -w -h);
push( @cmd, $uefi_stub );

@output = execute(@cmd);
$status = pop(@output);
if ( $status eq 0 ) {
my @sizes = split( /\s+/, @output[ scalar @output - 1 ] );

my $size = "0x" . $sizes[3];
my $vma = "0x" . $sizes[4];
my $sum = hex($size) + hex($vma);

$uki_offset = increaseBundleOffset( $sum, 0, $uki_alignment );
Log( "Initial offset is: " . hex($uki_offset) );
} else {
print "Unable to determine initial stub offset!\n";
exit 1;
}

@cmd = qw(objcopy);

my ( $hex_offset, $sb );

# Add os-release, if it exists
if ( -f "/etc/os-release" ) {
push( @cmd,
qw(--add-section .osrel=/etc/os-release),
qw(--change-section-vma .osrel=0x20000)
);
$uki_offset = addBundleSection( \@cmd, ".osrel", "/etc/os-release", $uki_offset, $uki_alignment );
}

# Add cmdline, if it exists
if ( nonempty $runConf{cmdline} ) {
my $cmdline = join( '/', $imagedir, "cmdline.txt" );

open( my $fh, '>', $cmdline );
print $fh $runConf{cmdline};
close($fh);

push( @cmd,
( "--add-section", ".cmdline=\"$cmdline\"" ),
qw(--change-section-vma .cmdline=0x30000)
);
$uki_offset = addBundleSection( \@cmd, ".cmdline", $cmdline, $uki_offset, $uki_alignment );
}

# Mandatory kernel and initramfs images
push( @cmd, (
( "--add-section", ".linux=\"$kernel\"" ),
qw(--change-section-vma .linux=0x2000000),
( "--add-section", ".initrd=\"$initramfs\"" ),
qw(--change-section-vma .initrd=0x3000000)
));
$uki_offset = addBundleSection( \@cmd, ".linux", $kernel, $uki_offset, $uki_alignment );
$uki_offset = addBundleSection( \@cmd, ".initrd", $initramfs, $uki_offset, $uki_alignment );

push( @cmd, ( $uefi_stub, $output_file ) );

my $command = join( ' ', @cmd );
Log("Executing: $command");

my @output = execute(@cmd);
my $status = pop(@output);
@output = execute(@cmd);
$status = pop(@output);
if ( $status eq 0 ) {
foreach my $line (@output) {
Log($line);
Expand Down

0 comments on commit 0893838

Please sign in to comment.