blob: 202fd896b66edec052ced45812610f7ec123411f [file] [log] [blame]
#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);
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");
}