summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2023-01-16 18:58:21 +0100
committerGerd Hoffmann <kraxel@redhat.com>2023-01-18 14:32:14 +0100
commit53c368d71ba43da7414ac86c58291a11da05ba84 (patch)
treee0e387f36cd2ea24c483441868bdd495b35e2238 /src
parentMerge pull request #26004 from poettering/cleanuo-erase-moar (diff)
downloadsystemd-53c368d71ba43da7414ac86c58291a11da05ba84.tar.xz
systemd-53c368d71ba43da7414ac86c58291a11da05ba84.zip
bootctl: add kernel-identity command
The command takes a kernel as argument and checks what kind of kernel the image is. Returns one of uki, pe or unknown.
Diffstat (limited to 'src')
-rw-r--r--src/boot/bootctl-uki.c110
-rw-r--r--src/boot/bootctl-uki.h3
-rw-r--r--src/boot/bootctl.c4
3 files changed, 117 insertions, 0 deletions
diff --git a/src/boot/bootctl-uki.c b/src/boot/bootctl-uki.c
new file mode 100644
index 0000000000..1a439afaae
--- /dev/null
+++ b/src/boot/bootctl-uki.c
@@ -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
index 0000000000..3c1fb5bc6a
--- /dev/null
+++ b/src/boot/bootctl-uki.h
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+int verb_kernel_identify(int argc, char *argv[], void *userdata);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index b443f9970b..901533cf09 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -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 },