diff --git a/src/arch/x86/interface/pcbios/int13con.c b/src/arch/x86/interface/pcbios/int13con.c index 925228874..7372540b6 100644 --- a/src/arch/x86/interface/pcbios/int13con.c +++ b/src/arch/x86/interface/pcbios/int13con.c @@ -298,3 +298,7 @@ struct console_driver int13con __console_driver = { .disabled = CONSOLE_DISABLED, .usage = CONSOLE_INT13, }; + +/* Request a log partition from genfsimg */ +__asm__ ( ".globl ipxe_oem_info_disklog\n\t" + ".equ ipxe_oem_info_disklog, 0x0001\n\t" ); diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S index 19d30141b..a2b188a2b 100644 --- a/src/arch/x86/prefix/lkrnprefix.S +++ b/src/arch/x86/prefix/lkrnprefix.S @@ -11,6 +11,37 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .globl _lkrn_start _lkrn_start: +/***************************************************************************** + * + * OEM information + * + * For UEFI, we (ab)use the PE format OEM information field to + * communicate information to external processing utilities such as + * genfsimg. + * + * Since these fields lie in an unused location in a bzImage kernel + * header (and since hybrid PE/bzImage is already a well-known file + * format used for dual-mode BIOS/UEFI kernels), we choose to use the + * same field locations for BIOS. + * + */ + + .org 0x24 +oem_id: + .word 0x18ae +oem_info: + .word 0 + +#if __x86_64__ +#define R_X86_16 R_X86_64_16 +#else +#define R_X86_16 R_386_16 +#endif + + /* Allow linked-in objects to request a log partition from genfsimg */ + .weak ipxe_oem_info_disklog + .reloc oem_info, R_X86_16, ipxe_oem_info_disklog + /***************************************************************************** * * Kernel header diff --git a/src/include/compiler.h b/src/include/compiler.h index f6d0aa67d..13c1a1996 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -166,6 +166,31 @@ /** @} */ +/** + * @defgroup ipxenote Macros to provide ELF notes + * @{ + */ + +/* Construct an iPXE-specific ELF note */ +#define IPXE_NOTE( type ) \ + __asm__ ( ".section \".note.ipxe\", \"\", " \ + _S2 ( ASM_TCHAR ) "note\n\t" \ + /* Owner name length */ \ + ".long 4\n\t" \ + /* Content length */ \ + ".long 0\n\t" \ + /* Type */ \ + ".long " _S2 ( _C2 ( IPXE_NOTE_, type ) ) \ + "\n\t" \ + /* Owner name */ \ + ".ascii \"iPXE\"\n\t" \ + ".previous\n\t" ) + +/** Build will use a disk-based console log, if present */ +#define IPXE_NOTE_DISKLOG 0x18aed109 + +/** @} */ + /** * @defgroup objmacros Macros to provide or require explicit objects * @{ diff --git a/src/scripts/efi.lds b/src/scripts/efi.lds index d5b2d7567..f7e6abf28 100644 --- a/src/scripts/efi.lds +++ b/src/scripts/efi.lds @@ -119,6 +119,16 @@ SECTIONS { } _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" ); + /* + * iPXE-specific note sections + * + */ + + .note : { + *(.note.ipxe) + *(.note.ipxe.*) + } + /* * Dispose of the comment and note sections to make the link map * easier to read diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 9fd7c27ea..39f614d1b 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -56,6 +56,7 @@ #define Elf_Ehdr Elf32_Ehdr #define Elf_Phdr Elf32_Phdr #define Elf_Shdr Elf32_Shdr +#define Elf_Nhdr Elf32_Nhdr #define Elf_Sym Elf32_Sym #define Elf_Addr Elf32_Addr #define Elf_Rel Elf32_Rel @@ -72,6 +73,7 @@ #define Elf_Ehdr Elf64_Ehdr #define Elf_Phdr Elf64_Phdr #define Elf_Shdr Elf64_Shdr +#define Elf_Nhdr Elf64_Nhdr #define Elf_Sym Elf64_Sym #define Elf_Addr Elf64_Addr #define Elf_Rel Elf64_Rel @@ -243,6 +245,15 @@ /** Number of data directory entries */ #define NUMBER_OF_DIRECTORY_ENTRIES 8 +/** Build requested a console log partition */ +#define IPXE_NOTE_DISKLOG 0x18aed109 + +/** OEM identifier used for iPXE */ +#define IPXE_OEM_ID 0x18ae + +/** OEM information: console log partition requested */ +#define IPXE_OEM_INFO_DISKLOG 0x0001 + struct elf_file { void *data; size_t len; @@ -272,6 +283,7 @@ struct pe_header { static struct pe_header efi_pe_header = { .dos = { .e_magic = EFI_IMAGE_DOS_SIGNATURE, + .e_oemid = IPXE_OEM_ID, .e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ), }, .nt = { @@ -315,6 +327,8 @@ struct options { unsigned int subsystem; /** Create hybrid BIOS/UEFI binary */ int hybrid; + /** Use disk-based console log */ + int disklog; }; /** @@ -998,6 +1012,53 @@ static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr, } } +/** + * Process note records + * + * @v elf ELF file + * @v shdr ELF section header + * @v opts Options + */ +static void process_notes ( struct elf_file *elf, const Elf_Shdr *shdr, + struct options *opts ) { + const struct { + Elf_Nhdr hdr; + char name[4]; + } __attribute__ (( packed )) *note; + static const char name[4] = { 'i', 'P', 'X', 'E' }; + size_t remaining; + + /* Process each note */ + note = ( elf->data + shdr->sh_offset ); + remaining = shdr->sh_size; + while ( remaining ) { + + /* Sanity check */ + if ( ( remaining < sizeof ( *note ) ) || + ( note->hdr.n_namesz != sizeof ( name ) ) || + ( note->hdr.n_descsz != 0 ) || + ( memcmp ( note->name, name, sizeof ( name ) ) != 0 ) ) { + eprintf ( "Invalid iPXE note\n" ); + exit ( 1 ); + } + + /* Handle note type */ + switch ( note->hdr.n_type ) { + case IPXE_NOTE_DISKLOG: + opts->disklog = 1; + break; + default: + eprintf ( "Unrecognised iPXE note type %#x\n", + note->hdr.n_type ); + exit ( 1 ); + } + + /* Move to next note */ + note = ( ( ( void * ) note ) + sizeof ( *note ) ); + remaining -= sizeof ( *note ); + } +} + /** * Create relocations section * @@ -1273,12 +1334,21 @@ static void elf2pe ( const char *elf_name, const char *pe_name, /* Process .rela relocations */ process_relocs ( &elf, shdr, sizeof ( Elf_Rela ), &pe_reltab, opts ); + + } else if ( shdr->sh_type == SHT_NOTE ) { + + /* Process .note records */ + process_notes ( &elf, shdr, opts ); } } /* Update image base address */ update_image_base ( &pe_header, pe_sections, pe_reltab ); + /* Update OEM information */ + if ( opts->disklog ) + pe_header.dos.e_oeminfo |= IPXE_OEM_INFO_DISKLOG; + /* Create the .reloc section */ *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); next_pe_section = &(*next_pe_section)->next; diff --git a/src/util/genfsimg b/src/util/genfsimg index 54e9d4354..dada6d2bd 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -97,6 +97,22 @@ efi_boot_arch() { esac } +# Check if binary wants a log partition +# +wants_disklog() { + local FILENAME + local OEMID + local OEMINFO + local FLAG + + FILENAME="${1}" + + OEMID=$(get_word "${FILENAME}" 0x24) + OEMINFO=$(get_word "${FILENAME}" 0x26) + FLAG=$(( OEMINFO & 0x0001 )) + [ "${OEMID}" = "18ae" -a "${FLAG}" -ne "0" ] +} + # Find syslinux file # find_syslinux_file() { @@ -214,6 +230,7 @@ case "${OUTFILE}" in BIOSDIR="${ISODIR}" SYSLINUXCFG="${ISODIR}/isolinux.cfg" FATPART= + LOGPART= ;; *.sdsk) ISOIMG= @@ -221,6 +238,7 @@ case "${OUTFILE}" in BIOSDIR="${FATDIR}" SYSLINUXCFG="${FATDIR}/syslinux.cfg" FATPART= + LOGPART= ;; *) ISOIMG= @@ -228,6 +246,7 @@ case "${OUTFILE}" in BIOSDIR="${FATDIR}" SYSLINUXCFG="${FATDIR}/syslinux.cfg" FATPART="4" + LOGPART="3" ;; esac @@ -237,6 +256,9 @@ cat >"${MTOOLSRC}" <