| /* |
| * This file is part of the TianoCoreBoot project. |
| * |
| * Copyright (C) 2013 Google Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <libpayload.h> |
| #include <endian.h> |
| #include <cbfs.h> |
| #include <efi.h> |
| #include <coff.h> |
| |
| #define DXE_CORE_SIZE (256*1024) |
| #define UEFI_STACK_SIZE (128*1024) |
| #define HOB_LIST_SIZE (16*1024) |
| |
| #undef VERBOSE |
| #undef INVENTORY |
| |
| static void print_guid(EFI_GUID *guid) |
| { |
| printf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
| guid->Data1, guid->Data2, guid->Data3, |
| guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], |
| guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); |
| } |
| |
| static void dump_uefi_firmware_volume_header(EFI_FIRMWARE_VOLUME_HEADER *fvh) |
| { |
| printf("Found UEFI firmware volume.\n"); |
| printf(" GUID: "); |
| print_guid(&(fvh->FileSystemGuid)); |
| printf("\n"); |
| printf(" length: 0x%016llx\n", fvh->FvLength); |
| #ifdef VERBOSE |
| printf(" signature: 0x%08x\n", fvh->Signature); |
| printf(" attributes: 0x%08x\n", fvh->Attributes); |
| printf(" header length: 0x%04x\n", fvh->HeaderLength); |
| printf(" checksum: 0x%04x\n", fvh->Checksum); |
| printf(" revision: 0x%02x\n", fvh->Revision); |
| printf(" block map:\n"); |
| int i = 0; |
| EFI_FV_BLOCK_MAP_ENTRY *fbm = &(fvh->FvBlockMap[0]); |
| while (fbm[i].NumBlocks || fbm[i].BlockLength) { |
| printf(" %2d. numblocks = 0x%08x length = 0x%08x\n", |
| i+1, fbm[i].NumBlocks, fbm[i].BlockLength); |
| i++; |
| } |
| #endif |
| printf("\n"); |
| } |
| |
| #ifdef INVENTORY |
| static void dump_uefi_ffs_file_header(EFI_FFS_FILE_HEADER *file) |
| { |
| int size; |
| |
| #ifdef VERBOSE |
| printf("Found FFS file:\n GUID: "); |
| #endif |
| print_guid(&(file->Name)); |
| #ifdef VERBOSE |
| printf("\n integrity check: %02x %02x\n", |
| file->IntegrityCheck.Checksum.Header, |
| file->IntegrityCheck.Checksum.File); |
| printf(" file type: "); |
| #else |
| printf(" "); |
| #endif |
| switch (file->Type) { |
| case EFI_FV_FILETYPE_RAW: printf("raw"); break; |
| case EFI_FV_FILETYPE_FREEFORM: printf("free form"); break; |
| case EFI_FV_FILETYPE_SECURITY_CORE: printf("security core"); break; |
| case EFI_FV_FILETYPE_PEI_CORE: printf("PEIM core"); break; |
| case EFI_FV_FILETYPE_DXE_CORE: printf("DXE core"); break; |
| case EFI_FV_FILETYPE_PEIM: printf("PEIM"); break; |
| case EFI_FV_FILETYPE_DRIVER: printf("driver"); break; |
| case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: printf("combined PEIM driver"); break; |
| case EFI_FV_FILETYPE_APPLICATION: printf("application"); break; |
| case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: printf("firmware volume image"); break; |
| case EFI_FV_FILETYPE_FFS_PAD: printf("FFS pad"); break; |
| default: printf("unknown"); |
| } |
| #ifdef VERBOSE |
| printf("\n"); |
| printf(" attributes: 0x%02x\n", file->Attributes); |
| #endif |
| size = file->Size[0] | (file->Size[1] << 8) | (file->Size[2] << 16); |
| #ifdef VERBOSE |
| printf(" size: 0x%06x\n", size); |
| printf(" state: 0x%02x\n", file->State); |
| #else |
| printf(" (%d bytes)\n", size); |
| #endif |
| } |
| #endif |
| |
| void *load_dxe_core(void *pe, void *target) |
| { |
| dos_header_t *dos_hdr = (dos_header_t *)pe; |
| |
| #if VERBOSE |
| printf("Loading DXE core at %p\n", pe); |
| #endif |
| |
| if (*(uint16_t *)pe != 0x5a4d) { |
| printf("DXE core not a PE binary.\n"); |
| return NULL; |
| } |
| |
| #ifdef VERBOSE |
| printf("e_lfanew = 0x%08x\n", dos_hdr->e_lfanew); |
| #endif |
| |
| coff_header_t *coff_hdr = (coff_header_t *)(pe + dos_hdr->e_lfanew); |
| #ifdef VERBOSE |
| printf("Machine: %x\n", coff_hdr->Machine); |
| printf("NumberOfSections: %x\n", coff_hdr->NumberOfSections); |
| printf("TimeDateStamp: %x\n", coff_hdr->TimeDateStamp); |
| printf("PointerToSymbolTable: %x\n", coff_hdr->PointerToSymbolTable); |
| printf("NumberOfSymbols: %x\n", coff_hdr->NumberOfSymbols); |
| printf("SizeOfOptionalHeader: %x\n", coff_hdr->SizeOfOptionalHeader); |
| printf("Characteristics: %x\n", coff_hdr->Characteristics); |
| #endif |
| if (coff_hdr->Machine != 0x14c) { |
| printf("Only x86 supported right now.\n"); |
| return NULL; |
| } |
| |
| /* Right after the coff header */ |
| pe_opt_header_t *pe_hdr = (pe_opt_header_t *)(&coff_hdr[1]); |
| if (pe_hdr->signature != 267) { |
| printf("No valid PE opt header\n"); |
| return NULL; |
| } |
| |
| #ifdef VERBOSE |
| printf("\n"); |
| printf("MajorLinkerVersion: %x\n", pe_hdr->MajorLinkerVersion); |
| printf("MinorLinkerVersion: %x\n", pe_hdr->MinorLinkerVersion); |
| printf("SizeOfCode: %x\n", pe_hdr->SizeOfCode); |
| printf("SizeOfInitializedData: %x\n", pe_hdr->SizeOfInitializedData); |
| printf("SizeOfUninitializedData: %x\n", pe_hdr->SizeOfUninitializedData); |
| printf("AddressOfEntryPoint: %x\n", pe_hdr->AddressOfEntryPoint); |
| printf("BaseOfCode: %x\n", pe_hdr->BaseOfCode); |
| printf("BaseOfData: %x\n", pe_hdr->BaseOfData); |
| printf("ImageBase: %x\n", pe_hdr->ImageBase); |
| printf("SectionAlignment: %x\n", pe_hdr->SectionAlignment); |
| printf("FileAlignment: %x\n", pe_hdr->FileAlignment); |
| printf("MajorOSVersion: %x\n", pe_hdr->MajorOSVersion); |
| printf("MinorOSVersion: %x\n", pe_hdr->MinorOSVersion); |
| printf("MajorImageVersion: %x\n", pe_hdr->MajorImageVersion); |
| printf("MinorImageVersion: %x\n", pe_hdr->MinorImageVersion); |
| printf("MajorSubsystemVersion: %x\n", pe_hdr->MajorSubsystemVersion); |
| printf("MinorSubsystemVersion: %x\n", pe_hdr->MinorSubsystemVersion); |
| printf("Reserved: %x\n", pe_hdr->Reserved); |
| printf("SizeOfImage: %x\n", pe_hdr->SizeOfImage); |
| printf("SizeOfHeaders: %x\n", pe_hdr->SizeOfHeaders); |
| printf("Checksum: %x\n", pe_hdr->Checksum); |
| printf("Subsystem: %x\n", pe_hdr->Subsystem); |
| printf("DLLCharacteristics: %x\n", pe_hdr->DLLCharacteristics); |
| printf("SizeOfStackReserve: %x\n", pe_hdr->SizeOfStackReserve); |
| printf("SizeOfStackCommit: %x\n", pe_hdr->SizeOfStackCommit); |
| printf("SizeOfHeapReserve: %x\n", pe_hdr->SizeOfHeapReserve); |
| printf("SizeOfHeapCommit: %x\n", pe_hdr->SizeOfHeapCommit); |
| printf("LoaderFlags: %x\n", pe_hdr->LoaderFlags); |
| printf("NumberOfRvaAndSizes: %x\n", pe_hdr->NumberOfRvaAndSizes); |
| #endif |
| |
| if(pe_hdr->Subsystem != 0xb) { |
| printf("Not an EFI binary.\n"); |
| return NULL; |
| } |
| |
| int i; |
| #ifdef VERBOSE |
| for (i = 0; i < pe_hdr->NumberOfRvaAndSizes; i++) { |
| if (!pe_hdr->DataDirectory[i].Size) |
| continue; |
| printf("Data Directory %d\n", i+1); |
| printf(" VirtualAddress %x\n", pe_hdr->DataDirectory[i].VirtualAddress); |
| printf(" Size %x\n", pe_hdr->DataDirectory[i].Size); |
| } |
| #endif |
| |
| pe_section_t *sections = (pe_section_t *)(&pe_hdr[1]); |
| |
| int offset = 0; |
| |
| for (i = 0; i < coff_hdr->NumberOfSections; i++) { |
| int j; |
| printf(" Section %d: ", i); |
| for (j = 0; j < 8; j++) |
| printf("%c", sections[i].SectionName[j] ? sections[i].SectionName[j] : ' '); |
| |
| printf(" size=%08x rva=%08x in file=%08x/%08x flags=%08x\n", |
| sections[i].Size, sections[i].RVA, sections[i].PhysicalSizeOnDisk, |
| sections[i].PhysicalLocationOnDisk, sections[i].SectionFlags); |
| |
| if (!strncmp((char *)sections[i].SectionName, ".text", 6)) { |
| // .text section |
| // size=157a0 rva=240 size on disk=157a0 location on disk=240 flags=60000020 |
| memcpy(target, pe + sections[i].PhysicalLocationOnDisk, |
| sections[i].PhysicalSizeOnDisk); |
| offset = sections[i].RVA; |
| } else |
| if (!strncmp((char *)sections[i].SectionName, ".data", 6)) { |
| // .data section |
| // size=6820 rva=159e0 size on disk=6820 location on disk=159e0 flags=c0000040 |
| memcpy(target + sections[i].RVA - offset, pe + sections[i].PhysicalLocationOnDisk, |
| sections[i].PhysicalSizeOnDisk); |
| } else |
| if (!strncmp((char *)sections[i].SectionName, ".reloc", 7)) { |
| // .reloc section |
| // section 2: .reloc |
| // size=1080 rva=1c200 size on disk=1080 location on disk=1c200 flags=42000040 |
| relocation_t *reloc = (relocation_t *) |
| (pe + sections[i].PhysicalLocationOnDisk); |
| while (reloc && reloc->SizeOfBlock) { |
| #ifdef VERBOSE |
| printf("Relocation Block Virtual %08x Size %08x\n", |
| reloc->VirtualAddress, reloc->SizeOfBlock); |
| #endif |
| for (i = sizeof(relocation_t); i < reloc->SizeOfBlock; i+= 2) { |
| uint16_t r = *(uint16_t *)((void *)reloc + i); |
| switch (r>>12) { |
| case 3: |
| #ifdef VERBOSE |
| printf(" HIGHLOW %08x\n", |
| reloc->VirtualAddress + (r & 0xfff)); |
| #endif |
| *(uint32_t *)(target - offset + reloc->VirtualAddress + (r & 0xfff)) |
| += (unsigned long)target - offset; |
| break; |
| case 0: |
| #ifdef VERBOSE |
| printf(" ABSOLUTE %08x\n", r & 0xfff); |
| #endif |
| break; |
| default: |
| printf("Unknown relocation type %x\n", r); |
| return NULL; |
| } |
| } |
| |
| reloc = (relocation_t *)(((void *)reloc) + reloc->SizeOfBlock); |
| } |
| } else |
| if (!strncmp((char *)sections[i].SectionName, ".debug", 7)) { |
| // debug section, silently ignored. |
| } else { |
| printf("section type "); |
| for (j = 0; j < 8; j++) |
| if (sections[i].SectionName[j]) |
| printf("%c", sections[i].SectionName[j]); |
| printf(" unknown. ignored.\n"); |
| } |
| } |
| |
| return (target + pe_hdr->AddressOfEntryPoint - offset); |
| } |
| |
| void start_dxe_core(void *entry, void *stack, void *hoblist) |
| { |
| printf("\nJumping to DXE core at %p\n", entry); |
| asm volatile( |
| "movl %1, %%esp\n" |
| "pushl %2\n" |
| "call *%0\n" |
| : : "r"(entry), "r"(stack), "r"(hoblist) : "esp" |
| ); |
| } |
| |
| static const EFI_HOB_HANDOFF_INFO_TABLE HandoffInformationTable = { |
| { EFI_HOB_TYPE_HANDOFF, sizeof(EFI_HOB_HANDOFF_INFO_TABLE), 0 }, |
| EFI_HOB_HANDOFF_TABLE_VERSION, |
| BOOT_WITH_FULL_CONFIGURATION, |
| 0 /* EfiMemoryTop */, |
| 0 /* EfiMemoryBottom */, |
| 0 /* EfiFreeMemoryTop */, |
| 0 /* EfiFreeMemoryBottom */, |
| 0 /* EfiEndOfHobList */ |
| }; |
| |
| static const EFI_HOB_FIRMWARE_VOLUME FirmwareVolume = { |
| { EFI_HOB_TYPE_FV, sizeof(EFI_HOB_FIRMWARE_VOLUME), 0 }, |
| 0 /* BaseAddress */, |
| 0 /* Length */ |
| }; |
| |
| /* 1..n */ |
| static const EFI_HOB_RESOURCE_DESCRIPTOR ResourceDescriptor = { |
| { EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, sizeof(EFI_HOB_RESOURCE_DESCRIPTOR), 0 }, |
| { 0 }, // owner EFI_GUID |
| EFI_RESOURCE_SYSTEM_MEMORY, |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | |
| EFI_RESOURCE_ATTRIBUTE_TESTED, |
| 0, /* PhysicalStart */ |
| 0 /* ResourceLength */ |
| }; |
| |
| static const EFI_HOB_MEMORY_ALLOCATION_MODULE MemoryAllocationModule = { |
| { EFI_HOB_TYPE_MEMORY_ALLOCATION, sizeof(EFI_HOB_MEMORY_ALLOCATION_MODULE), 0 }, |
| { EFI_HOB_MEMORY_ALLOC_MODULE_GUID, 0 /* MemoryBaseAddress */, 0 /* MemoryLength */, EfiBootServicesCode, {0,0,0,0} }, |
| EFI_DXE_FILE_GUID, |
| 0x0 // ADDR EntryPoint |
| }; |
| |
| static const EFI_HOB_CPU Cpu = { |
| { EFI_HOB_TYPE_CPU, sizeof(EFI_HOB_CPU), 0 }, |
| 32, /* SizeOfMemorySpace, u8 */ |
| 16, /* SizeOfIoSpace */ |
| { 0,0,0,0,0,0 } |
| }; |
| |
| static const EFI_HOB_GENERIC_HEADER End = |
| { EFI_HOB_TYPE_END_OF_HOB_LIST, sizeof(EFI_HOB_GENERIC_HEADER), 0 }; |
| |
| |
| static void prepare_handoff_blocks(void *hoblist, EFI_FIRMWARE_VOLUME_HEADER *fvh, |
| unsigned long EfiMemoryTop, unsigned long EfiMemoryBottom, |
| unsigned long EfiFreeMemoryTop, unsigned long EfiFreeMemoryBottom, |
| unsigned long dxecore_base) |
| { |
| void *ptr = hoblist; |
| int i; |
| |
| EFI_HOB_HANDOFF_INFO_TABLE *hit = (EFI_HOB_HANDOFF_INFO_TABLE *)ptr; |
| memcpy(ptr, &HandoffInformationTable, sizeof(HandoffInformationTable)); |
| ptr += sizeof(HandoffInformationTable); |
| |
| EFI_HOB_FIRMWARE_VOLUME *fv = (EFI_HOB_FIRMWARE_VOLUME *)ptr; |
| memcpy(ptr, &FirmwareVolume, sizeof(FirmwareVolume)); |
| ptr += sizeof(FirmwareVolume); |
| |
| for (i = 0; i < lib_sysinfo.n_memranges; i++) { |
| EFI_HOB_RESOURCE_DESCRIPTOR *resource; |
| if (lib_sysinfo.memrange[i].type != CB_MEM_RAM) |
| continue; |
| resource = (EFI_HOB_RESOURCE_DESCRIPTOR *)ptr; |
| memcpy(ptr, &ResourceDescriptor, sizeof(ResourceDescriptor)); |
| ptr += sizeof(ResourceDescriptor); |
| resource->PhysicalStart = lib_sysinfo.memrange[i].base; |
| resource->ResourceLength = lib_sysinfo.memrange[i].size; |
| } |
| |
| EFI_HOB_MEMORY_ALLOCATION_MODULE *allocation = (EFI_HOB_MEMORY_ALLOCATION_MODULE *)ptr; |
| memcpy(ptr, &MemoryAllocationModule, sizeof(MemoryAllocationModule)); |
| ptr += sizeof(MemoryAllocationModule); |
| |
| memcpy(ptr, &Cpu, sizeof(Cpu)); |
| ptr += sizeof(Cpu); |
| |
| memcpy(ptr, &End, sizeof(End)); |
| ptr += sizeof(Cpu); |
| |
| /* Handoff Information Table HOB */ |
| hit->EfiMemoryTop = EfiMemoryTop; |
| hit->EfiMemoryBottom = EfiMemoryBottom; |
| hit->EfiFreeMemoryTop = EfiFreeMemoryTop; |
| hit->EfiFreeMemoryBottom = EfiFreeMemoryBottom; |
| hit->EfiEndOfHobList = (unsigned long)ptr; |
| |
| /* Firmware Volume HOB */ |
| fv->BaseAddress = (unsigned long)fvh; |
| fv->Length = fvh->FvLength; |
| |
| allocation->MemoryAllocationHeader.MemoryBaseAddress = dxecore_base; |
| allocation->MemoryAllocationHeader.MemoryLength = DXE_CORE_SIZE; |
| } |
| |
| int main(void) |
| { |
| int i; |
| struct cbfs_file *file; |
| void *tiano; |
| unsigned long long ram_seg_base = 0, ram_seg_size = 0; |
| EFI_FIRMWARE_VOLUME_HEADER *fvh = NULL; |
| EFI_PEI_HOB_POINTERS hoblist; |
| EFI_COMMON_SECTION_HEADER *dxecore = NULL; |
| |
| printf("\nTiano Core Loader v1.0\n"); |
| printf("Copyright (C) 2013 Google Inc. All rights reserved.\n\n"); |
| |
| printf("Memory Map (%d entries):\n", lib_sysinfo.n_memranges); |
| for (i = 0; i < lib_sysinfo.n_memranges; i++) { |
| printf(" %d. %016llx - %016llx [%02x]\n", i + 1, |
| lib_sysinfo.memrange[i].base, |
| lib_sysinfo.memrange[i].base + |
| lib_sysinfo.memrange[i].size - 1, |
| lib_sysinfo.memrange[i].type); |
| |
| /* Look for the last chunk of memory below 4G */ |
| if (lib_sysinfo.memrange[i].type == CB_MEM_RAM && |
| lib_sysinfo.memrange[i].base < 0xffffffff) { |
| ram_seg_base = lib_sysinfo.memrange[i].base; |
| ram_seg_size = lib_sysinfo.memrange[i].size; |
| } |
| } |
| printf("\n"); |
| |
| if (!ram_seg_base || ram_seg_size < (1024*1024)) { |
| printf("No usable RAM found.\n"); |
| halt(); |
| } |
| |
| /* Find the end of our memory block, align to 4K */ |
| unsigned long memory = (ram_seg_base + ram_seg_size) & 0xfffff000; |
| |
| /* 256K for DXE core. It's 116K on my system but you never know. */ |
| memory -= DXE_CORE_SIZE; |
| unsigned long dxecore_base = memory; |
| memory -= UEFI_STACK_SIZE; |
| unsigned long uefi_stack = memory; |
| |
| memory -= 1024*1024; // FIXME this should go away |
| unsigned long free_memory = memory; // FIXME this should go away |
| |
| memory -= HOB_LIST_SIZE; |
| unsigned long hoblist_base = memory; |
| |
| printf("DXE code: %08lx\n", dxecore_base); |
| printf("DXE stack: %08lx\n", uefi_stack); |
| printf("HOB list: %08lx\n\n", hoblist_base); |
| |
| /* Find UEFI firmware volume in CBFS */ |
| file = cbfs_find("fallback/tianocore.fd"); |
| if (!file) { |
| printf("Could not find fallback/tianocore.fd in CBFS.\n"); |
| halt(); |
| } |
| |
| tiano = CBFS_SUBHEADER(file); |
| while (tiano < (void *)CBFS_SUBHEADER(file) + ntohl(file->len)) { |
| /* Verify UEFI firmware volume consistency */ |
| fvh = (EFI_FIRMWARE_VOLUME_HEADER *)tiano; |
| if (fvh->Signature != 0x4856465f) { |
| printf("Not an UEFI firmware volume.\n"); |
| halt(); |
| } |
| |
| /* Dump UEFI firmware volume header */ |
| dump_uefi_firmware_volume_header(fvh); |
| |
| /* Dump UEFI firmware file headers */ |
| for (i = fvh->HeaderLength; i < fvh->FvLength;) { |
| int size; |
| EFI_FFS_FILE_HEADER *ffs; |
| |
| ffs = (EFI_FFS_FILE_HEADER *)(tiano + i); |
| |
| size = ffs->Size[0] | (ffs->Size[1] << 8) | (ffs->Size[2] << 16); |
| if (size == 0xffffff) |
| break; |
| #ifdef INVENTORY |
| printf("%08x - ", i); |
| dump_uefi_ffs_file_header(ffs); |
| #endif |
| |
| if (ffs->Type == EFI_FV_FILETYPE_DXE_CORE) { |
| dxecore = (EFI_COMMON_SECTION_HEADER *)&ffs[1]; |
| #ifndef INVENTORY |
| break; |
| #endif |
| } |
| |
| i = ALIGN(i + size, 8); |
| } |
| |
| tiano += fvh->FvLength; |
| #ifdef INVENTORY |
| printf("\n"); |
| #endif |
| } |
| |
| /* Prepare Hand Off Blocks */ |
| prepare_handoff_blocks((void *)hoblist_base, fvh, |
| // FIXME memory top, memory bottom |
| (ram_seg_base + ram_seg_size) & 0xfffff000, ram_seg_base, |
| // FIXME free memory top, free memory bottom |
| uefi_stack, free_memory, |
| dxecore_base); |
| hoblist.Raw = (void *)hoblist_base; |
| |
| if (!dxecore) { |
| printf("No DXE core found.\n"); |
| halt(); |
| } |
| |
| printf("Found DXE core at %p\n", &dxecore[1]); |
| #ifdef VERBOSE |
| int size = dxecore->Size[0] | (dxecore->Size[1] << 8) | (dxecore->Size[2] << 16); |
| printf(" size = %d, type = %x\n", size, dxecore->Type); |
| #endif |
| |
| void *pe = (void *)&dxecore[1]; |
| |
| void *entry; |
| entry = load_dxe_core(pe, (void *)dxecore_base); |
| |
| if (!entry) { |
| printf("Could not load DXE code.\n"); |
| halt(); |
| } |
| |
| start_dxe_core(entry, (void *)(uefi_stack + UEFI_STACK_SIZE - 4), hoblist.Raw); |
| |
| printf("The end.\n"); |
| halt(); |
| |
| return 0; |
| } |
| |
| PAYLOAD_INFO(name, "TianoCoreBoot"); |
| PAYLOAD_INFO(listname, "Tiano Core"); |
| PAYLOAD_INFO(desc, "Tiano Core Loader"); |