From c505472939646237ae20c35ff93e31bd17ac414f Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Fri, 3 Jul 2020 22:38:17 +0100 Subject: [PATCH] Support direct 64-bit EFI boot. --- boot/{efiboot.c => efisetup.c} | 42 ++----- boot/header.S | 201 ++++++++++++++++++++++++++++++ boot/startup64.S | 16 ++- build64/Makefile | 21 ++-- build64/ldscripts/memtest_efi.lds | 25 ++++ 5 files changed, 265 insertions(+), 40 deletions(-) rename boot/{efiboot.c => efisetup.c} (93%) create mode 100644 boot/header.S create mode 100644 build64/ldscripts/memtest_efi.lds diff --git a/boot/efiboot.c b/boot/efisetup.c similarity index 93% rename from boot/efiboot.c rename to boot/efisetup.c index 67d9a20..61d2c35 100644 --- a/boot/efiboot.c +++ b/boot/efisetup.c @@ -29,7 +29,7 @@ static efi_guid_t EFI_CONSOLE_OUT_DEVICE_GUID = { 0xd3b36f2c, 0xd551, 0x11d4, {0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }; static efi_guid_t EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = { 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a} }; -static efi_system_table_t *sys_table; +static efi_system_table_t *sys_table = NULL; //------------------------------------------------------------------------------ // Macro Functions @@ -476,34 +476,7 @@ static void set_e820_map(boot_params_t *params) // Public Functions //------------------------------------------------------------------------------ -void efi_handover(efi_handle_t handle, efi_system_table_t *sys_table, boot_params_t *boot_params) __attribute__((noreturn)); - -efi_status_t efiapi efi_pe_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) -{ - // Check if we were booted by the EFI firmware. - if (sys_table->header.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - print_string("bad system table signature\n"); - return EFI_INVALID_PARAMETER; - } - - sys_table = sys_table_arg; - - boot_params_t *boot_params = NULL; - efi_status_t status = alloc_low_memory((void **)&boot_params, sizeof(boot_params_t), 0); - if (status != EFI_SUCCESS) { - print_string("failed to allocate low memory for boot params\n"); - return status; - } - - memset(boot_params, 0, sizeof(boot_params_t)); - - efi_handover(handle, sys_table, boot_params); - // should never return - - return EFI_ABORTED; -} - -boot_params_t *efi_main(efi_handle_t handle, efi_system_table_t *sys_table_arg, boot_params_t *boot_params) +boot_params_t *efi_setup(efi_handle_t handle, efi_system_table_t *sys_table_arg, boot_params_t *boot_params) { efi_status_t status; @@ -513,6 +486,15 @@ boot_params_t *efi_main(efi_handle_t handle, efi_system_table_t *sys_table_arg, goto fail; } + if (boot_params == NULL) { + status = alloc_low_memory((void **)&boot_params, sizeof(boot_params_t), 0); + if (status != EFI_SUCCESS) { + print_string("failed to allocate low memory for boot params\n"); + goto fail; + } + memset(boot_params, 0, sizeof(boot_params_t)); + } + status = set_screen_info(boot_params); if (status != EFI_SUCCESS) { print_string("set_screen_info() failed\n"); @@ -530,7 +512,7 @@ boot_params_t *efi_main(efi_handle_t handle, efi_system_table_t *sys_table_arg, return boot_params; fail: - print_string("efi_main() failed\n"); + print_string("efi_setup() failed\n"); while (1) { __asm__("hlt"); diff --git a/boot/header.S b/boot/header.S new file mode 100644 index 0000000..97ffc7d --- /dev/null +++ b/boot/header.S @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// header.S supports booting directly from a UEFI BIOS or via an intermediate +// bootloader that supports the Linux boot protocol. When booted directly from +// the BIOS, it provides the MS-DOS & PE/COFF headers. When using an intermediate +// bootloader, it provides the first few bytes of the Linux boot header (at the +// end of the boot sector), with the remainder of the header being provided by +// setup.S. +// +// Copyright (C) 2020 Martin Whitaker. +// +// Derived from Linux 5.6 arch/x86/boot/header.S: +// +// Copyright (C) 1991, 1992 Linus Torvalds +// +// Based on bootsect.S and setup.S +// modified by more people than can be counted +// +// Rewritten as a common file by H. Peter Anvin (Apr 2007) + +#define __ASSEMBLY__ + +#include "boot.h" + +# The EFI loader loads the header at ImageBase, so we have to locate the main program +# after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load +# address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code. + +#define IMAGE_BASE 0x200000 +#define BASE_OF_CODE 0x1000 + + .section ".header", "ax", @progbits + .code16 + + .globl boot +boot: + # "MZ", the MS-DOS header signature. + .byte 0x4d + .byte 0x5a + + # In case we are booted by a legacy BIOS, print an error message. + # Fortunately the MS-DOS header translates to harmless instructions. + + ljmp $BOOT_SEG, $(error - boot) +error: + movw %cs, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + xorw %sp, %sp + sti + cld + + movw $error_msg, %si +0: lodsb + andb %al, %al + jz wait + movb $0xe, %ah + movw $7, %bx + int $0x10 + jmp 0b + +wait: + # Allow the user to press a key, then reboot. + xorw %ax, %ax + int $0x16 + int $0x19 + + # int 0x19 should never return. In case it does, invoke the BIOS. + # reset code. + ljmp $0xf000,$0xfff0 + + # The PE header pointer. + .org 0x3c + .long pe_header + +error_msg: + .ascii "This is a UEFI bootable image\r\n" + .ascii "\n" + .asciz "Press any key to reboot\r\n" + +pe_header: + .ascii "PE" + .word 0 + +coff_header: +#ifdef __x86_64__ + .word 0x8664 # Machine (x86-64) +#else + .word 0x14c # Machine (i386) +#endif + .word 1 # NumberOfSections + .long 0 # TimeDateStamp + .long 0 # PointerToSymbolTable + .long 0 # NumberOfSymbols + .word section_table - optional_header # SizeOfOptionalHeader +#ifdef __x86_64__ + .word 0x20f # Characteristics + # IMAGE_FILE_DEBUG_STRIPPED | + # IMAGE_FILE_LOCAL_SYMS_STRIPPED | + # IMAGE_FILE_LINE_NUMS_STRIPPED | + # IMAGE_FILE_EXECUTABLE_IMAGE | + # IMAGE_FILE_RELOCS_STRIPPED +#else + .word 0x30f # Characteristics. + # IMAGE_FILE_32BIT_MACHINE | + # IMAGE_FILE_DEBUG_STRIPPED | + # IMAGE_FILE_LOCAL_SYMS_STRIPPED | + # IMAGE_FILE_LINE_NUMS_STRIPPED | + # IMAGE_FILE_EXECUTABLE_IMAGE | + # IMAGE_FILE_RELOCS_STRIPPED +#endif + +optional_header: +#ifdef __x86_64__ + .word 0x20b # PE32+ format +#else + .word 0x10b # PE32 format +#endif + .byte 0x02 # MajorLinkerVersion + .byte 0x14 # MinorLinkerVersion + + .long _text_size # SizeOfCode + .long 0 # SizeOfInitializedData + .long 0 # SizeOfUninitializedData + + .long BASE_OF_CODE + 0x100 # AddressOfEntryPoint + + .long BASE_OF_CODE # BaseOfCode +#ifndef __x86_64__ + .long 0 # data +#endif + +extra_header_fields: +#ifdef __x86_64__ + .quad IMAGE_BASE # ImageBase +#else + .long IMAGE_BASE # ImageBase +#endif + .long 4096 # SectionAlignment + .long 512 # FileAlignment + .word 0 # MajorOperatingSystemVersion + .word 0 # MinorOperatingSystemVersion + .word 0 # MajorImageVersion + .word 0 # MinorImageVersion + .word 0 # MajorSubsystemVersion + .word 0 # MinorSubsystemVersion + .long 0 # Win32VersionValue + + .long BASE_OF_CODE + _init_size # SizeOfImage + .long 512 # SizeOfHeaders + .long 0 # CheckSum + .word 10 # Subsystem (EFI application) + .word 0 # DllCharacteristics +#ifdef __x86_64__ + .quad 0 # SizeOfStackReserve + .quad 0 # SizeOfStackCommit + .quad 0 # SizeOfHeapReserve + .quad 0 # SizeOfHeapCommit +#else + .long 0 # SizeOfStackReserve + .long 0 # SizeOfStackCommit + .long 0 # SizeOfHeapReserve + .long 0 # SizeOfHeapCommit +#endif + .long 0 # LoaderFlags + .long 0 # NumberOfRvaAndSizes + + # Section table +section_table: + .ascii ".text" + .byte 0 + .byte 0 + .byte 0 + .long _text_size # VirtualSize + .long BASE_OF_CODE # VirtualAddress + .long _text_size # SizeOfRawData + .long _text_start # PointerToRawData + .long 0 # PointerToRelocations + .long 0 # PointerToLineNumbers + .word 0 # NumberOfRelocations + .word 0 # NumberOfLineNumbers + .long 0x60500020 # Characteristics (section flags) + +# Emulate the Linux boot header, to allow loading by intermediate boot loaders. + + .org 497 +setup_sects: + .byte SETUP_SECS +root_flags: + .word 0 +sys_size: + .long _sys_size +ram_size: + .word 0 +vid_mode: + .word 0 +root_dev: + .word 0 +boot_flag: + .word 0xAA55 diff --git a/boot/startup64.S b/boot/startup64.S index 1d7e981..a913be1 100644 --- a/boot/startup64.S +++ b/boot/startup64.S @@ -3,8 +3,8 @@ // startup64.S contains the 64-bit startup code for both the BSP and APs. // It initialises stacks, memory management, and exception handling, clears // the BSS, completes relocation, and finally calls the main application. -// It supports both the 32-bit and 64-bit Linux boot protocols for the first -// boot of the BSP. +// It supports both the 32-bit and 64-bit Linux boot protocols and EFI boot +// for the first boot of the BSP. // // Copyright (C) 2020 Martin Whitaker. // @@ -103,6 +103,16 @@ startup32: .code64 +# The EFI PE32+ boot entry point. + + .org 0x100 + .globl efi_boot +efi_boot: + movq %rcx, %rdi # the EFI image handle + movq %rdx, %rsi # the EFI system table pointer + movq $0, %rdx # the boot params pointer (0 = not yet allocated) + jmp efi_handover + # The Linux 64-bit boot entry point. .org 0x200 @@ -123,7 +133,7 @@ startup64: .globl efi_handover efi_handover: andq $~0xf, %rsp - call efi_main + call efi_setup # Save the boot params pointer. diff --git a/build64/Makefile b/build64/Makefile index 3bff39e..b5291e2 100644 --- a/build64/Makefile +++ b/build64/Makefile @@ -44,10 +44,11 @@ APP_OBJS = app/badram.o \ app/interrupt.o \ app/main.o -OBJS = boot/startup.o boot/efiboot.o $(SYS_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) +OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) -all: memtest.bin +all: memtest.bin memtest.efi +-include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) @@ -64,7 +65,7 @@ boot/%.s: ../boot/%.S ../boot/boot.h @mkdir -p boot $(CC) -E -traditional -I../boot -o $@ $< -boot/efiboot.o: ../boot/efiboot.c +boot/efisetup.o: ../boot/efisetup.c @mkdir -p boot $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) @@ -101,14 +102,20 @@ memtest_shared.bin: memtest_shared memtest.bin: memtest_shared.bin boot/bootsect.o boot/setup.o ldscripts/memtest_bin.lds $(LD) -T ldscripts/memtest_bin.lds boot/bootsect.o boot/setup.o -b binary memtest_shared.bin -o memtest.bin +memtest.efi: memtest_shared.bin boot/header.o boot/setup.o ldscripts/memtest_efi.lds + $(LD) -T ldscripts/memtest_efi.lds boot/header.o boot/setup.o -b binary memtest_shared.bin -o memtest.efi + memtest.img: memtest.bin dd if=/dev/zero of=memtest.img bs=1474560 count=1 dd if=memtest.bin of=memtest.img bs=1474560 conv=notrunc -iso: memtest.img - @mkdir -p iso/boot - genisoimage -b memtest.img -c boot/boot.catalog -V "PCMemTest-64" -o memtest.iso iso memtest.img - @rm -rf iso +iso: memtest.img memtest.efi + @mkdir -p iso/boot iso/EFI/BOOT + cp memtest.img iso/boot/memtest.img + cp memtest.efi iso/EFI/BOOT/bootx64.efi + xorrisofs -pad -R -J -volid PCMemTest64 -graft-points -hide-rr-moved \ + -b /boot/memtest.img --efi-boot /EFI/BOOT/bootx64.efi \ + -o ./memtest.iso /boot=./iso/boot /EFI=./iso/EFI clean: rm -rf boot system lib tests app *.iso memtest* iso diff --git a/build64/ldscripts/memtest_efi.lds b/build64/ldscripts/memtest_efi.lds new file mode 100644 index 0000000..d1a7605 --- /dev/null +++ b/build64/ldscripts/memtest_efi.lds @@ -0,0 +1,25 @@ +OUTPUT_FORMAT("binary") +OUTPUT_ARCH(i386:x86-64) + +ENTRY(boot); +SECTIONS { + . = 0; + .header : { + *(.header) + } + .setup : { + *(.setup) + } + . = ALIGN(512); + .text : { + _text_start = . ; + *(.data) + . = ALIGN(512); + _text_end = . ; + } + + _text_size = (_text_end - _text_start); + + _sys_size = _text_size >> 4; + _init_size = _text_size; +}