]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: add kernel-identity command
authorGerd Hoffmann <kraxel@redhat.com>
Mon, 16 Jan 2023 17:58:21 +0000 (18:58 +0100)
committerGerd Hoffmann <kraxel@redhat.com>
Wed, 18 Jan 2023 13:32:14 +0000 (14:32 +0100)
The command takes a kernel as argument and checks what kind of kernel
the image is.  Returns one of uki, pe or unknown.

man/bootctl.xml
meson.build
src/boot/bootctl-uki.c [new file with mode: 0644]
src/boot/bootctl-uki.h [new file with mode: 0644]
src/boot/bootctl.c

index c9edf77b3734a4b29522ead280c5abfcce823091..2479d5fcd6b36d202ed0377b1b05aa54edbd6517 100644 (file)
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title><command>kernel</command> Commands</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>kernel-identify</option> <replaceable>kernel</replaceable></term>
+
+        <listitem><para>Takes a kernel image as argument. Checks what kind of kernel the image is.  Returns
+        one of uki, pe or unknown.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Options</title>
     <para>The following options are understood:</para>
index 64f2e00be5f710f4af46bfda2f033a178d0504d9..5d6ab2a7bdf948837584b9d632cb236a38758073 100644 (file)
@@ -2622,6 +2622,8 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
                  'src/boot/bootctl-status.h',
                  'src/boot/bootctl-systemd-efi-options.c',
                  'src/boot/bootctl-systemd-efi-options.h',
+                 'src/boot/bootctl-uki.c',
+                 'src/boot/bootctl-uki.h',
                  'src/boot/bootctl-util.c',
                  'src/boot/bootctl-util.h'],
                 include_directories : includes,
diff --git a/src/boot/bootctl-uki.c b/src/boot/bootctl-uki.c
new file mode 100644 (file)
index 0000000..1a439af
--- /dev/null
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bootctl.h"
+#include "bootctl-uki.h"
+#include "fd-util.h"
+#include "parse-util.h"
+#include "pe-header.h"
+
+#define MAX_SECTIONS 96
+
+static const uint8_t dos_file_magic[2] = "MZ";
+static const uint8_t pe_file_magic[4] = "PE\0\0";
+
+static const uint8_t name_osrel[8] = ".osrel";
+static const uint8_t name_linux[8] = ".linux";
+static const uint8_t name_initrd[8] = ".initrd";
+
+static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) {
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        struct DosFileHeader dos;
+        struct PeHeader pe;
+        size_t scount;
+        uint64_t soff, items;
+        int rc;
+
+        items = fread(&dos, 1, sizeof(dos), uki);
+        if (items != sizeof(dos))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "DOS header read error");
+        if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0)
+                goto no_sections;
+
+        rc = fseek(uki, le32toh(dos.ExeHeader), SEEK_SET);
+        if (rc < 0)
+                return log_error_errno(errno, "seek to PE header");
+        items = fread(&pe, 1, sizeof(pe), uki);
+        if (items != sizeof(pe))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error");
+        if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0)
+                goto no_sections;
+
+        soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader);
+        rc = fseek(uki, soff, SEEK_SET);
+        if (rc < 0)
+                return log_error_errno(errno, "seek to PE section headers");
+
+        scount = le16toh(pe.FileHeader.NumberOfSections);
+        if (scount > MAX_SECTIONS)
+                goto no_sections;
+        sections = new(struct PeSectionHeader, scount);
+        if (!sections)
+                return log_oom();
+        items = fread(sections, sizeof(*sections), scount, uki);
+        if (items != scount)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error");
+
+        *ret = TAKE_PTR(sections);
+        *ret_n = scount;
+        return 0;
+
+no_sections:
+        *ret = NULL;
+        *ret_n = 0;
+        return 0;
+}
+
+static int find_pe_section(struct PeSectionHeader *sections, size_t scount,
+                           const uint8_t *name, size_t namelen, size_t *ret) {
+        for (size_t s = 0; s < scount; s++) {
+                if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name),
+                              name, namelen) == 0) {
+                        if (ret)
+                                *ret = s;
+                        return 1;
+                }
+        }
+        return 0;
+}
+
+static bool is_uki(struct PeSectionHeader *sections, size_t scount) {
+        return (find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) &&
+                find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) &&
+                find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL));
+}
+
+int verb_kernel_identify(int argc, char *argv[], void *userdata) {
+        _cleanup_fclose_ FILE *uki = NULL;
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        size_t scount;
+        int rc;
+
+        uki = fopen(argv[1], "re");
+        if (!uki)
+                return log_error_errno(errno, "Failed to open UKI file '%s': %m", argv[1]);
+
+        rc = pe_sections(uki, &sections, &scount);
+        if (rc < 0)
+                return EXIT_FAILURE;
+
+        if (sections) {
+                if (is_uki(sections, scount)) {
+                        puts("uki");
+                        return EXIT_SUCCESS;
+                }
+                puts("pe");
+                return EXIT_SUCCESS;
+        }
+
+        puts("unknown");
+        return EXIT_SUCCESS;
+}
diff --git a/src/boot/bootctl-uki.h b/src/boot/bootctl-uki.h
new file mode 100644 (file)
index 0000000..3c1fb5b
--- /dev/null
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+int verb_kernel_identify(int argc, char *argv[], void *userdata);
index b443f9970b25b73e29c87c04cfcb05c07ff6fa73..901533cf096fbc86d002bff6067e3ebcaa980a42 100644 (file)
@@ -9,6 +9,7 @@
 #include "bootctl-set-efivar.h"
 #include "bootctl-status.h"
 #include "bootctl-systemd-efi-options.h"
+#include "bootctl-uki.h"
 #include "build.h"
 #include "dissect-image.h"
 #include "escape.h"
@@ -150,6 +151,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  remove              Remove systemd-boot from the ESP and EFI variables\n"
                "  is-installed        Test whether systemd-boot is installed in the ESP\n"
                "  random-seed         Initialize random seed in ESP and EFI variables\n"
+               "\n%3$skernel Commands:%4$s\n"
+               "  kernel-identify     Identify kernel image type.\n"
                "\n%3$sOptions:%4$s\n"
                "  -h --help            Show this help\n"
                "     --version         Print version\n"
@@ -406,6 +409,7 @@ static int bootctl_main(int argc, char *argv[]) {
                 { "update",              VERB_ANY, 1,        0,            verb_install             },
                 { "remove",              VERB_ANY, 1,        0,            verb_remove              },
                 { "is-installed",        VERB_ANY, 1,        0,            verb_is_installed        },
+                { "kernel-identify",     2,        2,        0,            verb_kernel_identify     },
                 { "list",                VERB_ANY, 1,        0,            verb_list                },
                 { "set-default",         2,        2,        0,            verb_set_efivar          },
                 { "set-oneshot",         2,        2,        0,            verb_set_efivar          },