if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error registering initrd: %r", err);
+ EFI_LOADED_IMAGE *loaded_image;
+ err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **) &loaded_image);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error getting LoadedImageProtocol handle: %r", err);
+
CHAR16 *options = options_initrd ?: entry->options;
if (options) {
- EFI_LOADED_IMAGE *loaded_image;
-
- err = BS->OpenProtocol(image, &LoadedImageProtocol, (void **)&loaded_image,
- parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (EFI_ERROR(err))
- return log_error_status_stall(err, L"Error getting LoadedImageProtocol handle: %r", err);
loaded_image->LoadOptions = options;
loaded_image->LoadOptionsSize = StrSize(options);
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
err = BS->StartImage(image, NULL, NULL);
graphics_mode(FALSE);
- if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
+ if (err == EFI_SUCCESS)
+ return EFI_SUCCESS;
- return EFI_SUCCESS;
+ /* Try calling the kernel compat entry point if one exists. */
+ if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
+ UINT32 kernel_entry_address;
+
+ err = pe_alignment_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
+ if (EFI_ERROR(err)) {
+ if (err != EFI_UNSUPPORTED)
+ return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
+ } else {
+ EFI_IMAGE_ENTRY_POINT kernel_entry =
+ (EFI_IMAGE_ENTRY_POINT) ((UINT8 *) loaded_image->ImageBase + kernel_entry_address);
+
+ err = kernel_entry(image, ST);
+ graphics_mode(FALSE);
+ if (err == EFI_SUCCESS)
+ return EFI_SUCCESS;
+ }
+ }
+
+ return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
}
static void config_free(Config *config) {
#define MAX_SECTIONS 96
#if defined(__i386__)
- #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
+# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
+# define TARGET_MACHINE_TYPE_COMPATIBILITY EFI_IMAGE_MACHINE_X64
#elif defined(__x86_64__)
- #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
+# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
#elif defined(__aarch64__)
- #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
+# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
#elif defined(__arm__)
- #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
+# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
#elif defined(__riscv) && __riscv_xlen == 64
- #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
+# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
#else
- #error Unknown EFI arch
+# error Unknown EFI arch
+#endif
+
+#ifndef TARGET_MACHINE_TYPE_COMPATIBILITY
+# define TARGET_MACHINE_TYPE_COMPATIBILITY 0
#endif
struct DosFileHeader {
return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
}
-static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
+static inline BOOLEAN verify_pe(const struct PeFileHeader *pe, BOOLEAN allow_compatibility) {
assert(pe);
return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
- pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
+ (pe->FileHeader.Machine == TARGET_MACHINE_TYPE ||
+ (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) &&
pe->FileHeader.NumberOfSections > 0 &&
pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC);
}
}
+static UINT32 get_compatibility_entry_address(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
+ UINTN addr = 0, size = 0;
+ static const CHAR8 *sections[] = { (CHAR8 *) ".compat", NULL };
+
+ /* The kernel may provide alternative PE entry points for different PE architectures. This allows
+ * booting a 64bit kernel on 32bit EFI that is otherwise running on a 64bit CPU. The locations of any
+ * such compat entry points are located in a special PE section. */
+
+ locate_sections((const struct PeSectionHeader *) ((const UINT8 *) dos + section_table_offset(dos, pe)),
+ pe->FileHeader.NumberOfSections,
+ sections,
+ &addr,
+ NULL,
+ &size);
+
+ if (size == 0)
+ return 0;
+
+ typedef struct {
+ UINT8 type;
+ UINT8 size;
+ UINT16 machine_type;
+ UINT32 entry_point;
+ } _packed_ LinuxPeCompat1;
+
+ while (size >= sizeof(LinuxPeCompat1) && addr % __alignof__(LinuxPeCompat1) == 0) {
+ LinuxPeCompat1 *compat = (LinuxPeCompat1 *) ((UINT8 *) dos + addr);
+
+ if (compat->type == 0 || compat->size == 0 || compat->size > size)
+ break;
+
+ if (compat->type == 1 &&
+ compat->size >= sizeof(LinuxPeCompat1) &&
+ compat->machine_type == TARGET_MACHINE_TYPE)
+ return compat->entry_point;
+
+ addr += compat->size;
+ size -= compat->size;
+ }
+
+ return 0;
+}
+
EFI_STATUS pe_alignment_info(
const void *base,
UINT32 *ret_entry_point_address,
assert(base);
assert(ret_entry_point_address);
- assert(ret_size_of_image);
- assert(ret_section_alignment);
dos = (const struct DosFileHeader *) base;
if (!verify_dos(dos))
return EFI_LOAD_ERROR;
pe = (const struct PeFileHeader*) ((const UINT8 *)base + dos->ExeHeader);
- if (!verify_pe(pe))
+ if (!verify_pe(pe, /* allow_compatibility= */ TRUE))
return EFI_LOAD_ERROR;
- *ret_entry_point_address = pe->OptionalHeader.AddressOfEntryPoint;
- *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
- *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
+ UINT32 entry_address = pe->OptionalHeader.AddressOfEntryPoint;
+
+ /* Look for a compat entry point. */
+ if (pe->FileHeader.Machine != TARGET_MACHINE_TYPE) {
+ entry_address = get_compatibility_entry_address(dos, pe);
+ if (entry_address == 0)
+ /* Image type not supported and no compat entry found. */
+ return EFI_UNSUPPORTED;
+ }
+
+ *ret_entry_point_address = entry_address;
+ if (ret_size_of_image)
+ *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
+ if (ret_section_alignment)
+ *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
return EFI_SUCCESS;
}
return EFI_LOAD_ERROR;
pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
- if (!verify_pe(pe))
+ if (!verify_pe(pe, /* allow_compatibility= */ FALSE))
return EFI_LOAD_ERROR;
offset = section_table_offset(dos, pe);
err = handle->Read(handle, &len, &pe);
if (EFI_ERROR(err))
return err;
- if (len != sizeof(pe) || !verify_pe(&pe))
+ if (len != sizeof(pe) || !verify_pe(&pe, /* allow_compatibility= */ FALSE))
return EFI_LOAD_ERROR;
section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);