A UKI image is a PE file that consists of several sections, typically
including: .text, .data, .linux, .initrd, .cmdline, and others.

The kernel image is stored in the .linux section, which is one of the
formats currently recognized by kexec-tools. Therefore, the UKI parser
can be used to strip away the UKI layer, allowing the other parser to
continue the procession of the kernel image.

Signed-off-by: Pingfan Liu <pi...@redhat.com>
Cc: Simon Horman <ho...@kernel.org>
Cc: kexec@lists.infradead.org
---
 include/Makefile  |   1 +
 include/pe.h      | 104 +++++++++++++++++++++++++++++++++++++++
 kexec/Makefile    |   1 +
 kexec/kexec-uki.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++
 kexec/kexec.h     |   4 ++
 5 files changed, 233 insertions(+)
 create mode 100644 include/pe.h
 create mode 100644 kexec/kexec-uki.c

diff --git a/include/Makefile b/include/Makefile
index cd88a26..6a3e854 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -8,6 +8,7 @@ dist += include/Makefile                \
        include/x86/mb_header.h         \
        include/x86/multiboot2.h        \
        include/elf.h                   \
+       include/pe.h                    \
        include/image.h                 \
        include/unused.h                \
        include/boot/linuxbios_tables.h \
diff --git a/include/pe.h b/include/pe.h
new file mode 100644
index 0000000..2617074
--- /dev/null
+++ b/include/pe.h
@@ -0,0 +1,104 @@
+/*
+ * Extract from linux kernel include/linux/pe.h
+ */
+
+#ifndef __PE_H__
+#define __PE_H__
+
+struct pe_hdr {
+       uint32_t magic;         /* PE magic */
+       uint16_t machine;       /* machine type */
+       uint16_t sections;      /* number of sections */
+       uint32_t timestamp;     /* time_t */
+       uint32_t symbol_table;  /* symbol table offset */
+       uint32_t symbols;       /* number of symbols */
+       uint16_t opt_hdr_size;  /* size of optional header */
+       uint16_t flags;         /* flags */
+};
+
+/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't
+ * work right.  vomit. */
+struct pe32_opt_hdr {
+       /* "standard" header */
+       uint16_t magic;         /* file type */
+       uint8_t  ld_major;      /* linker major version */
+       uint8_t  ld_minor;      /* linker minor version */
+       uint32_t text_size;     /* size of text section(s) */
+       uint32_t data_size;     /* size of data section(s) */
+       uint32_t bss_size;      /* size of bss section(s) */
+       uint32_t entry_point;   /* file offset of entry point */
+       uint32_t code_base;     /* relative code addr in ram */
+       uint32_t data_base;     /* relative data addr in ram */
+       /* "windows" header */
+       uint32_t image_base;    /* preferred load address */
+       uint32_t section_align; /* alignment in bytes */
+       uint32_t file_align;    /* file alignment in bytes */
+       uint16_t os_major;      /* major OS version */
+       uint16_t os_minor;      /* minor OS version */
+       uint16_t image_major;   /* major image version */
+       uint16_t image_minor;   /* minor image version */
+       uint16_t subsys_major;  /* major subsystem version */
+       uint16_t subsys_minor;  /* minor subsystem version */
+       uint32_t win32_version; /* reserved, must be 0 */
+       uint32_t image_size;    /* image size */
+       uint32_t header_size;   /* header size rounded up to
+                                  file_align */
+       uint32_t csum;          /* checksum */
+       uint16_t subsys;        /* subsystem */
+       uint16_t dll_flags;     /* more flags! */
+       uint32_t stack_size_req;/* amt of stack requested */
+       uint32_t stack_size;    /* amt of stack required */
+       uint32_t heap_size_req; /* amt of heap requested */
+       uint32_t heap_size;     /* amt of heap required */
+       uint32_t loader_flags;  /* reserved, must be 0 */
+       uint32_t data_dirs;     /* number of data dir entries */
+};
+
+struct pe32plus_opt_hdr {
+       uint16_t magic;         /* file type */
+       uint8_t  ld_major;      /* linker major version */
+       uint8_t  ld_minor;      /* linker minor version */
+       uint32_t text_size;     /* size of text section(s) */
+       uint32_t data_size;     /* size of data section(s) */
+       uint32_t bss_size;      /* size of bss section(s) */
+       uint32_t entry_point;   /* file offset of entry point */
+       uint32_t code_base;     /* relative code addr in ram */
+       /* "windows" header */
+       uint64_t image_base;    /* preferred load address */
+       uint32_t section_align; /* alignment in bytes */
+       uint32_t file_align;    /* file alignment in bytes */
+       uint16_t os_major;      /* major OS version */
+       uint16_t os_minor;      /* minor OS version */
+       uint16_t image_major;   /* major image version */
+       uint16_t image_minor;   /* minor image version */
+       uint16_t subsys_major;  /* major subsystem version */
+       uint16_t subsys_minor;  /* minor subsystem version */
+       uint32_t win32_version; /* reserved, must be 0 */
+       uint32_t image_size;    /* image size */
+       uint32_t header_size;   /* header size rounded up to
+                                  file_align */
+       uint32_t csum;          /* checksum */
+       uint16_t subsys;        /* subsystem */
+       uint16_t dll_flags;     /* more flags! */
+       uint64_t stack_size_req;/* amt of stack requested */
+       uint64_t stack_size;    /* amt of stack required */
+       uint64_t heap_size_req; /* amt of heap requested */
+       uint64_t heap_size;     /* amt of heap required */
+       uint32_t loader_flags;  /* reserved, must be 0 */
+       uint32_t data_dirs;     /* number of data dir entries */
+};
+
+struct section_header {
+       char name[8];                   /* name or "/12\0" string tbl offset */
+       uint32_t virtual_size;          /* size of loaded section in ram */
+       uint32_t virtual_address;       /* relative virtual address */
+       uint32_t raw_data_size;         /* size of the section */
+       uint32_t data_addr;             /* file pointer to first page of sec */
+       uint32_t relocs;                /* file pointer to relocation entries */
+       uint32_t line_numbers;          /* line numbers! */
+       uint16_t num_relocs;            /* number of relocations */
+       uint16_t num_lin_numbers;       /* srsly. */
+       uint32_t flags;
+};
+
+#endif
diff --git a/kexec/Makefile b/kexec/Makefile
index 11682bf..d4f26d7 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -18,6 +18,7 @@ KEXEC_SRCS_base += kexec/kexec-elf-core.c
 KEXEC_SRCS_base += kexec/kexec-elf-rel.c
 KEXEC_SRCS_base += kexec/kexec-elf-boot.c
 KEXEC_SRCS_base += kexec/kexec-pe-zboot.c
+KEXEC_SRCS_base += kexec/kexec-uki.c
 KEXEC_SRCS_base += kexec/kexec-iomem.c
 KEXEC_SRCS_base += kexec/firmware_memmap.c
 KEXEC_SRCS_base += kexec/crashdump.c
diff --git a/kexec/kexec-uki.c b/kexec/kexec-uki.c
new file mode 100644
index 0000000..210ebb6
--- /dev/null
+++ b/kexec/kexec-uki.c
@@ -0,0 +1,123 @@
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pe.h>
+#include "kexec.h"
+
+#define UKI_LINUX_SECTION ".linux"
+#define UKI_INITRD_SECTION ".initrd"
+#define UKI_CMDLINE_SECTION ".cmdline"
+#define UKI_DTB_SECTION ".dtb"
+
+#define FILENAME_UKI_INITRD          "/tmp/InitrdXXXXXX"
+
+static int embeded_linux_format_index = -1;
+
+/*
+ * Return -1 if not PE, else offset of the PE header
+ */
+static int get_pehdr_offset(const char *buf)
+{
+       int pe_hdr_offset;
+
+       pe_hdr_offset = *((int *)(buf + 0x3c));
+       buf += pe_hdr_offset;
+       if (!!memcmp(buf, "PE\0\0", 4)) {
+               printf("Not a PE file\n");
+               return -1;
+       }
+
+       return pe_hdr_offset;
+}
+
+int uki_image_probe(const char *file_buf, off_t buf_sz)
+{
+       struct pe_hdr *pe_hdr;
+       struct pe32plus_opt_hdr *opt_hdr;
+       struct section_header *sect_hdr;
+       int pe_hdr_offset, section_nr, linux_sz = -1;
+       char *pe_part_buf, *linux_src;
+       char *initrd_fname = NULL;
+       int initrd_fd = -1;
+
+       pe_hdr_offset = get_pehdr_offset(file_buf);
+       pe_part_buf = (char *)file_buf + pe_hdr_offset;
+       pe_hdr = (struct pe_hdr *)pe_part_buf;
+       if (pe_hdr->opt_hdr_size == 0) {
+               printf("ERR: optional header is missing\n");
+               return -1;
+       }
+       section_nr = pe_hdr->sections;
+       opt_hdr = (struct pe32plus_opt_hdr *)(pe_part_buf + sizeof(struct 
pe_hdr));
+       sect_hdr = (struct section_header *)((char *)opt_hdr + 
pe_hdr->opt_hdr_size);
+
+       for (int i = 0; i < section_nr; i++) {
+               if (!strcmp(sect_hdr->name, UKI_LINUX_SECTION)) {
+                       /* data_addr is relative to the whole file */
+                       linux_src = (char *)file_buf + sect_hdr->data_addr;
+                       linux_sz = sect_hdr->raw_data_size;
+
+               } else if (!strcmp(sect_hdr->name, UKI_INITRD_SECTION)) {
+                       if (!(initrd_fname = strdup(FILENAME_UKI_INITRD))) {
+                               dbgprintf("%s: Can't duplicate strings\n", 
__func__);
+                               goto next;
+                       }
+
+                       if ((initrd_fd = mkstemp(initrd_fname)) < 0) {
+                               dbgprintf("%s: Can't open file %s\n", __func__, 
initrd_fname);
+                               goto next;
+                       }
+
+                       if (write(initrd_fd, (char *)file_buf + 
sect_hdr->data_addr,
+                                       sect_hdr->raw_data_size) != 
sect_hdr->raw_data_size) {
+                               dbgprintf("%s: Can't write the compressed file 
%s\n",
+                                               __func__, initrd_fname);
+                               close(initrd_fd);
+                               goto next;
+                       } else {
+                               implicit_initrd_fd = open(initrd_fname, 
O_RDONLY);
+                               close(initrd_fd);
+                       }
+               }
+next:
+               sect_hdr++;
+       }
+
+       if (linux_sz == -1) {
+               printf("ERR: can not find .linux section\n");
+               return -1;
+       }
+       /*
+        * After stripping the UKI coat, the real kernel format can be handled 
now.
+        */
+       for (int i = 0; i < file_types; i++) {
+               /* kernel_fd will be created by probe */
+               if (file_type[i].probe != uki_image_probe &&
+                   file_type[i].probe(linux_src, linux_sz) >= 0) {
+                       embeded_linux_format_index = i;
+                       break;
+               }
+       }
+       if (embeded_linux_format_index < 0) {
+               printf("Can not recognize the kernel format in .linux 
section\n");
+               return -1;
+       }
+       return 0;
+}
+
+int uki_image_load(int argc, char **argv, const char *buf, off_t len,
+                       struct kexec_info *info)
+{
+       return file_type[embeded_linux_format_index].load(argc, argv, buf, len, 
info);
+}
+
+void uki_image_usage(void)
+{
+       printf(
+"     An UKI image.\n");
+}
diff --git a/kexec/kexec.h b/kexec/kexec.h
index e70c18d..a2e19c4 100644
--- a/kexec/kexec.h
+++ b/kexec/kexec.h
@@ -357,4 +357,8 @@ static inline void ultoa(unsigned long val, char *str)
        str[pos] = 0;
 }
 
+extern int uki_image_probe(const char *file_buf, off_t buf_sz);
+extern int uki_image_load(int argc, char **argv, const char *buf, off_t len,
+               struct kexec_info *info);
+extern void uki_image_usage(void);
 #endif /* KEXEC_H */
-- 
2.41.0


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to