diff options
Diffstat (limited to 'src/arch/ia64/core/efi.c')
-rw-r--r-- | src/arch/ia64/core/efi.c | 1026 |
1 files changed, 1026 insertions, 0 deletions
diff --git a/src/arch/ia64/core/efi.c b/src/arch/ia64/core/efi.c new file mode 100644 index 00000000..97e585fc --- /dev/null +++ b/src/arch/ia64/core/efi.c @@ -0,0 +1,1026 @@ +#include "efi/efi.h" +#include "etherboot.h" +#include "elf.h" +#include "sal.h" +#include "pal.h" + +#warning "Place a declaration of lookup_efi_nic somewhere useful" +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index); + +#warning "Place the declaraction of __call someplace more appropriate\n" +extern EFI_STATUS __call(void *,...); + +/* Keep 16M free in case EFI needs to allocate some memory. + * In the worst case this is only 1/8 the memory on an Itanium. + */ +#define EFI_RESERVE_LOW_PAGES ((8*1024*1024)/EFI_PAGE_SIZE) +#define EFI_RESERVE_HIGH_PAGES ((8*1024*1024)/EFI_PAGE_SIZE) + +struct console_info { + uint16_t num_cols; + uint16_t num_rows; + uint16_t orig_x; + uint16_t orig_y; +}; + +struct efi_mem_map { + uint64_t map_size; + uint64_t map_key; + uint64_t descriptor_size; + uint32_t descriptor_version; + uint32_t pad; + EFI_MEMORY_DESCRIPTOR map[64]; +}; + +struct efi_info { + int flags; +#define READ_SYSTAB 1 +#define READ_FPSWA 2 +#define READ_MEMMAP 4 +#define READ_CONINFO 8 + EFI_SYSTEM_TABLE *systab; + void *fpswa; + struct efi_mem_map mem_map; + struct console_info coninfo; +}; + + +unsigned long io_base; + +/* local globals */ +static struct efi_info efi_info; +static EFI_HANDLE etherboot_handle; +static EFI_BOOT_SERVICES *boot_services; +static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; +static SIMPLE_INPUT_INTERFACE *conin; +static void *mps_table; +static void *acpi20_table; +static void *smbios_table; +static void *nii_table; + +/* local functions */ + +static EFI_STATUS efi_locate_handle( + EFI_LOCATE_SEARCH_TYPE search_type, + EFI_GUID *protocol, void *search_key, + UINTN *buffer_size, EFI_HANDLE *buffer) +{ + if (!boot_services) + return EFI_NOT_FOUND; + return __call(boot_services->LocateHandle, + search_type, protocol, search_key, buffer_size, buffer); +} + +static EFI_STATUS efi_handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->HandleProtocol, handle, protocol, interface); +} + +static EFI_STATUS efi_locate_device_path(EFI_GUID *protocol, EFI_DEVICE_PATH **device_path, + EFI_HANDLE *device) +{ + if (!boot_services) + return EFI_NOT_FOUND; + return __call(boot_services->LocateDevicePath, protocol, device_path, device); +} + +static EFI_STATUS efi_allocate_pages(EFI_ALLOCATE_TYPE type, EFI_MEMORY_TYPE memory_type, + UINTN pages, EFI_PHYSICAL_ADDRESS *memory) +{ + if (!boot_services) + return EFI_OUT_OF_RESOURCES; + return __call(boot_services->AllocatePages, + type, memory_type, pages, memory); +} + +static EFI_STATUS efi_free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN pages) +{ + if(pages == 0) + return EFI_SUCCESS; + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->FreePages, memory, pages); +} + +static EFI_STATUS efi_get_memory_map(UINTN *map_size, EFI_MEMORY_DESCRIPTOR *map, + UINTN *map_key, UINTN *descriptor_size, UINT32 *descriptor_version) +{ + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->GetMemoryMap, + map_size, map, map_key, descriptor_size, descriptor_version); +} + +static EFI_STATUS efi_free_pool(void *buffer) +{ + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->FreePool, buffer); +} +static EFI_STATUS efi_stall(UINTN microseconds) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->Stall, microseconds); +} + +static EFI_STATUS efi_set_watchdog_timer( + UINTN timeout, UINT64 watchdog_code, UINTN data_size, CHAR16 *watchdog_data) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->SetWatchdogTimer, + timeout, watchdog_code, data_size, watchdog_data); +} + + +static void efi_exit_boot_services(struct efi_mem_map *map) +{ + EFI_STATUS status; + if (!boot_services) + return; + status = __call(boot_services->ExitBootServices, + etherboot_handle, map->map_key); + if (status != EFI_SUCCESS) { + printf("ExitBootServices failed: %lx\n", status); + } + conout = 0; + conin = 0; + boot_services = 0; +} + +static void efi_free_memory(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *tail; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + tail = next_desc(map->map, map->map_size); + for(desc = map->map; desc < tail; desc = next_desc(desc, map->descriptor_size)) { + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS start, end; + UINTN pages; + int may_free; + + start = desc->PhysicalStart; + pages = desc->NumberOfPages; + end = start + pages * EFI_PAGE_SIZE; + + + may_free = 0; + /* The only canidates are Loader Code and Data */ + if ((desc->Type == EfiLoaderData) || + (desc->Type == EfiLoaderCode)) + may_free = 1; + + /* Don't free anything etherboot lives in */ + if ((may_free) && + (start < virt_to_phys(_end)) && + (end > virt_to_phys(_text))) + may_free = 0; + + /* Continue if it is not memory we want to free */ + if (!may_free) + continue; + + status = efi_free_pages(start, pages); + if (status != EFI_SUCCESS) { + printf("free_pages: %lx\n", status); + } + } +#undef next_desc +} + +static void read_efi_mem_map(struct efi_mem_map *map) +{ + EFI_STATUS status; + map->map_size = sizeof(map->map); + status = efi_get_memory_map( + &map->map_size, map->map, &map->map_key, + &map->descriptor_size, &map->descriptor_version); + if (status != EFI_SUCCESS) { + printf("read_efi_mem_map failed: %lx\n", status); + map->map_size = 0; + } + /* map->descriptor_size should only grow larger */ + /* map->descriptor_version should only increase and retain + * a backward compatible format. + */ +} + +#if 0 +static const char *efi_mem_type_name(uint32_t type) +{ + const char *type_name; + if (type == EfiReservedMemoryType) + type_name = "EfiReservedMemoryType "; + else if (type == EfiLoaderCode) + type_name = "EfiLoaderCode "; + else if (type == EfiLoaderData) + type_name = "EfiLoaderData "; + else if (type == EfiBootServicesCode) + type_name = "EfiBootServicesCode "; + else if (type == EfiBootServicesData) + type_name = "EfiBootServicesData "; + else if (type == EfiRuntimeServicesCode) + type_name = "EfiRuntimeServicesCode "; + else if (type == EfiRuntimeServicesData) + type_name = "EfiRuntimeServicesData "; + else if (type == EfiConventionalMemory) + type_name = "EfiConventionalMemory "; + else if (type == EfiUnusableMemory) + type_name = "EfiUnusableMemory "; + else if (type == EfiACPIReclaimMemory) + type_name = "EfiACPIReclaimMemory "; + else if (type == EfiACPIMemoryNVS) + type_name = "EfiACPIMemoryNVS "; + else if (type == EfiMemoryMappedIO) + type_name = "EfiMemoryMappedIO "; + else if (type == EfiMemoryMappedIOPortSpace) + type_name = "EfiMemoryMappedIOPortSpace"; + else if (type == EfiPalCode) + type_name = "EfiPalCode "; + else + type_name = "???? "; + return type_name; +} + +static void print_efi_mem_map(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + const char *mem_type; + unsigned long start, end, virt, virt_end; + uint64_t attr; + mem_type = efi_mem_type_name(desc->Type); + start = desc->PhysicalStart; + end = start + desc->NumberOfPages*EFI_PAGE_SIZE; + virt = desc->VirtualStart; + virt_end = virt + desc->NumberOfPages*EFI_PAGE_SIZE; + attr = desc->Attribute; + printf( "mem: %hhx %s @ %#lx-%#lx", + desc->Type, mem_type, start, end); + if (attr & EFI_MEMORY_UC) + printf("UC "); + if (attr & EFI_MEMORY_WC) + printf("WC "); + if (attr & EFI_MEMORY_WT) + printf("WT "); + if (attr & EFI_MEMORY_WB) + printf("WB "); + if (attr & EFI_MEMORY_UCE) + printf("UCE "); + + if (attr & EFI_MEMORY_WP) + printf("WP "); + if (attr & EFI_MEMORY_RP) + printf("RP "); + if (attr & EFI_MEMORY_XP) + printf("XP "); + + if (attr & EFI_MEMORY_RUNTIME) + printf("RUNTIME "); + + printf("\n"); + } +#undef next_desc +} +#endif + +static void efi_allocate_memory(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + unsigned long low_free, high_free; + +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + /* Find out how much memory is free */ + low_free = high_free = 0; + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + unsigned long start, middle, end; + if (desc->Type != EfiConventionalMemory) + continue; + start = desc->PhysicalStart; + end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE); + if (start < 0x100000000UL) { + if (end > 0x100000000UL) { + middle = 0x10000000UL; + } else { + middle = end; + } + } else { + middle = start; + } + + low_free += (middle - start)/EFI_PAGE_SIZE; + high_free += (end - middle)/EFI_PAGE_SIZE; + } + /* O.k. Now allocate all of the conventional memory, reserving only a tiny + * fraction for efi. + */ + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS address; + UINTN pages; + unsigned long start, middle, end; + unsigned long low_pages, high_pages; + if (desc->Type != EfiConventionalMemory) + continue; + start = desc->PhysicalStart; + end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE); + if (start < 0x100000000UL) { + if (end > 0x100000000UL) { + middle = 0x10000000UL; + } else { + middle = end; + } + } else { + middle = start; + } + low_pages = (middle - start)/EFI_PAGE_SIZE; + high_pages = (end - middle)/EFI_PAGE_SIZE; + if (low_pages && (low_free > EFI_RESERVE_LOW_PAGES)) { + address = start; + pages = low_pages; + if ((low_free - pages) < EFI_RESERVE_LOW_PAGES) { + pages = low_free - EFI_RESERVE_LOW_PAGES; + } + status = efi_allocate_pages( + AllocateAddress, EfiLoaderData, pages, &address); + if (status != EFI_SUCCESS) { + printf("allocate_pages @%lx for %ld pages failed: %ld\n", + desc->PhysicalStart, pages, status); + } + low_free -= pages; + } + if (high_pages && (high_free > EFI_RESERVE_HIGH_PAGES)) { + address = middle; + pages = high_pages; + if ((high_free - pages) < EFI_RESERVE_HIGH_PAGES) { + pages = high_free - EFI_RESERVE_HIGH_PAGES; + } + status = efi_allocate_pages( + AllocateAddress, EfiLoaderData, pages, &address); + if (status != EFI_SUCCESS) { + printf("allocate_pages @%lx for %ld pages failed: %ld\n", + desc->PhysicalStart, pages, status); + } + high_free -= pages; + } + } +#undef next_desc +} + +static void set_io_base(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + + io_base = ia64_get_kr0(); /* Default to ar.kr0 */ + +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + if (desc->Type == EfiMemoryMappedIOPortSpace) { + io_base = desc->PhysicalStart; + break; + } + } +#undef next_desc +} + +#define MAX_EFI_DEVICES 32 +static void efi_stop_nics(void) +{ + static EFI_GUID simple_net_protocol = EFI_SIMPLE_NETWORK_PROTOCOL; + EFI_SIMPLE_NETWORK *simple; + EFI_STATUS status; + EFI_HANDLE handles[MAX_EFI_DEVICES]; + EFI_HANDLE handle; + UINTN devices; + unsigned i; + + if (!boot_services) + return; + + devices = sizeof(handles); + status = efi_locate_handle( + ByProtocol, &simple_net_protocol, 0, &devices, handles); + if (status != EFI_SUCCESS) + return; + devices /= sizeof(handles[0]); + for(i = 0; i < devices; i++) { + void *that; + handle = handles[i]; + status = efi_handle_protocol(handle, &simple_net_protocol, &that); + if (status != EFI_SUCCESS) + continue; + simple = that; + if ((simple->Mode->State == EfiSimpleNetworkInitialized)) { + status = __call(simple->Shutdown, simple); + status = __call(simple->Stop, simple); + } + else if (simple->Mode->State == EfiSimpleNetworkStarted) { + status = __call(simple->Stop, simple); + } + } +} + +static void efi_get_coninfo(struct console_info *info) +{ + EFI_STATUS status; + UINTN cols, rows; + + /* Initialize with some silly safe values */ + info->num_cols = 80; + info->num_rows = 24; + info->orig_x = 0; + info->orig_y = 0; + + status = EFI_UNSUPPORTED; + if (conout) { + status = __call(conout->QueryMode, conout, conout->Mode->Mode, &cols, &rows); + if (status) { + printf("QueryMode Failed cannout get console parameters: %ld\n", status); + } else { + info->num_cols = cols; + info->num_rows = rows; + info->orig_x = conout->Mode->CursorColumn; + info->orig_y = conout->Mode->CursorRow; + } + } +} + +static void *efi_get_fpswa(void) +{ + static EFI_GUID fpswa_protocol = FPSWA_PROTOCOL; + EFI_STATUS status; + EFI_HANDLE fpswa_handle; + UINTN devices; + void *result; + + /* The FPSWA is the Floating Point Software Assist driver, + * to some extent it makes sense but it has one large flaw. + * It fails to install an EFI Configuration table, so the + * OS does not need assistance from the bootloader to find it. + */ + devices = sizeof(fpswa_handle); + status = efi_locate_handle( + ByProtocol, &fpswa_protocol, 0, &devices, &fpswa_handle); + if (status != EFI_SUCCESS) + return 0; + + status = efi_handle_protocol( + fpswa_handle, &fpswa_protocol, &result); + if (status != EFI_SUCCESS) + return 0; + + return result; +} + + +/* Exported functions */ + + +void arch_main(in_call_data_t *data, va_list params) +{ + EFI_STATUS status; + unsigned char *note, *end; + + /* IA64 doesn't have an in_call() implementation; start.S + * passes in this parameter directly on the stack instead of + * as part of the in_call_data_t structure or the parameter + * list. params is unusable: don't attempt to access it. + */ + struct Elf_Bhdr *ptr = (struct Elf_Bhdr *)data; + + memset(&efi_info, 0, sizeof(efi_info)); + note = ((char *)bhdr) + sizeof(*bhdr); + end = ((char *)bhdr) + bhdr->b_size; + if (bhdr->b_signature != 0x0E1FB007) { + printf("Bad bhdr(%lx) signature(%x)!\n", + (unsigned long) bhdr, bhdr->b_signature); + note = end = 0; + } + while(note < end) { + Elf_Nhdr *hdr; + unsigned char *n_name, *n_desc, *next; + hdr = (Elf_Nhdr *)note; + n_name = note + sizeof(*hdr); + n_desc = n_name + ((hdr->n_namesz + 3) & ~3); + next = n_desc + ((hdr->n_descsz + 3) & ~3); + if (next > end) + break; +#if 0 + printf("n_type: %x n_name(%d): n_desc(%d): \n", + hdr->n_type, hdr->n_namesz, hdr->n_descsz); +#endif + if ((hdr->n_namesz == 10) && + (memcmp(n_name, "Etherboot", 10) == 0)) { + switch(hdr->n_type) { + case EB_IA64_IMAGE_HANDLE: + { + uint64_t *handlep = (void *)n_desc; + etherboot_handle = (EFI_HANDLE)(*handlep); + break; + } + case EB_IA64_SYSTAB: + { + uint64_t *systabp = (void *)n_desc; + efi_info.systab = (void *)(*systabp); + efi_info.flags |= READ_SYSTAB; + break; + } + case EB_IA64_FPSWA: + { + uint64_t*fpswap = (void *)n_desc; + efi_info.fpswa = (void *)(*fpswap); + efi_info.flags |= READ_FPSWA; + break; + } + case EB_IA64_CONINFO: + { + struct console_info *coninfop = (void *)n_desc; + efi_info.coninfo = *coninfop; + efi_info.flags |= READ_CONINFO; + break; + } + case EB_IA64_MEMMAP: + { + struct efi_mem_map *mem_mapp = (void *)n_desc; + efi_info.mem_map = *mem_mapp; + efi_info.flags |= READ_MEMMAP; + break; + } + default: + break; + } + } + note = next; + } + if (!(efi_info.flags & READ_SYSTAB)) { + printf("No EFI systab\n"); + return; + } + + /* If I have an efi memory map assume ExitBootServices has been called. + */ +#warning "FIXME see if there is a better test for boot services still being active " + printf("FIXME Develop a better test for boot services still being active\n"); + if (!(efi_info.flags & READ_MEMMAP)) { + conout = efi_info.systab->ConOut; + conin = efi_info.systab->ConIn; + boot_services = efi_info.systab->BootServices; + } + + if (!(efi_info.flags & READ_CONINFO)) { + efi_info.flags |= READ_CONINFO; + efi_get_coninfo(&efi_info.coninfo); + } + if (!(efi_info.flags & READ_FPSWA)) { + efi_info.flags |= READ_FPSWA; + efi_info.fpswa = efi_get_fpswa(); + } + if (!(efi_info.flags & READ_MEMMAP)) { + efi_info.flags |= READ_MEMMAP; + read_efi_mem_map(&efi_info.mem_map); + /* Allocate all of the memory efi can spare */ + efi_allocate_memory(&efi_info.mem_map); + /* Now refresh the memory map */ + read_efi_mem_map(&efi_info.mem_map); + } + /* Get the io_base for legacy i/o */ + set_io_base(&efi_info.mem_map); + + /* Attempt to disable the watchdog timer.. + * Nothing useful can be done if this fails, so ignore the return code. + */ + status = efi_set_watchdog_timer(0, 1, 0, 0); + + /* Shutdown efi network drivers so efi doesn't get too confused */ + efi_stop_nics(); + + if (efi_info.systab) { + static const EFI_GUID mps_table_guid = MPS_TABLE_GUID; + static const EFI_GUID acpi20_table_guid = ACPI_20_TABLE_GUID; + static const EFI_GUID smbios_table_guid = SMBIOS_TABLE_GUID; + static const EFI_GUID sal_system_table_guid = SAL_SYSTEM_TABLE_GUID; + static const EFI_GUID nii_table_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + EFI_SYSTEM_TABLE *systab; + unsigned i; + systab = efi_info.systab; + for(i = 0; i < systab->NumberOfTableEntries; i++) { + EFI_GUID *guid; + void *table = systab->ConfigurationTable[i].VendorTable; + guid = &systab->ConfigurationTable[i].VendorGuid; + +#if 0 + printf("GUID: %x-%hx-%hx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx Table: %lx\n", + 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], + table); +#endif + + if (memcmp(guid, &mps_table_guid, 16) == 0) { + mps_table = table; + } + if (memcmp(guid, &acpi20_table_guid, 16) == 0) { + acpi20_table = table; + } + if (memcmp(guid, &smbios_table_guid, 16) == 0) { + smbios_table = table; + } + if (memcmp(guid, &sal_system_table_guid, 16) == 0) { + parse_sal_system_table(table); + } + if (memcmp(guid, &nii_table_guid, 16) == 0) { + nii_table = table; + } + } + } +} + +void arch_on_exit(int status __unused) +{ + if (!boot_services) + return; + read_efi_mem_map(&efi_info.mem_map); + efi_free_memory(&efi_info.mem_map); +} + +void arch_relocate_to(unsigned long addr) +{ + EFI_PHYSICAL_ADDRESS address, end; + UINTN pages; + EFI_STATUS status; + + if (!boot_services) + return; + + /* Find the efi pages where the new etherboot will sit */ + address = addr & ~(EFI_PAGE_SIZE -1); + end = (addr + (_end - _text) + EFI_PAGE_SIZE -1) & ~EFI_PAGE_SIZE; + pages = (end - address)/EFI_PAGE_SIZE; + + /* Reallocate the memory for the new copy of etherboot as LoaderCode */ + status = efi_free_pages(address, pages); + if (status != EFI_SUCCESS) { + printf("efi_free_pages failed!: %lx\n", status); + return; + } + status = efi_allocate_pages(AllocateAddress, EfiLoaderCode, pages, &address); + if (status != EFI_SUCCESS) { + printf("efi_allocate_pages failed! %lx\n", status); + return; + } +} + + +struct meminfo meminfo; +void get_memsizes(void) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + struct efi_mem_map *map; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + + map = &efi_info.mem_map; + end = next_desc(map->map, map->map_size); + + meminfo.map_count = 0; + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + uint64_t start, size, end; + unsigned long mem_k; + + start = desc->PhysicalStart; + size = desc->NumberOfPages*EFI_PAGE_SIZE; + end = start + size; + + if ((desc->Type != EfiLoaderCode) && + (desc->Type != EfiLoaderData)) { + continue; + } + + meminfo.map[meminfo.map_count].addr = start; + meminfo.map[meminfo.map_count].size = size; + meminfo.map[meminfo.map_count].type = E820_RAM; + meminfo.map_count++; + + end >>= 10; + mem_k = end; + if (end & 0xFFFFFFFF00000000ULL) { + mem_k = 0xFFFFFFFF; + } + /* Set the base basememsize */ + if ((mem_k <= 640) && (meminfo.basememsize <= mem_k)) { + meminfo.basememsize = mem_k; + } + /* Set the total memsize */ + if ((mem_k >= 1024) && (meminfo.memsize <= (mem_k - 1024))) { + meminfo.memsize = mem_k - 1024; + } + if (meminfo.map_count == E820MAX) + break; + } +#undef next_desc +} + + +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index) +{ + static EFI_GUID protocol = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + EFI_HANDLE handles[MAX_EFI_DEVICES]; + EFI_STATUS status; + UINTN devices; + void *that; + + if (!boot_services) + return 0; + if (index < 0) { + return 0; + } + devices = sizeof(handles); + status = efi_locate_handle( + ByProtocol, &protocol, 0, &devices, handles); + if (status != EFI_SUCCESS) + return 0; + devices /= sizeof(handles[0]); + if (index >= devices) + return 0; + status = efi_handle_protocol(handles[index], &protocol, &that); + if (status != EFI_SUCCESS) + return 0; + return that; +} + +#if defined(CONSOLE_FIRMWARE) +void console_putc(int c) +{ + CHAR16 str[2]; + if (!conout) + return; + str[0] = c; + str[1] = 0; + __call(conout->OutputString, conout, str); +} + +static int efi_have_key = 0; +static int efi_key; +int console_ischar(void) +{ + EFI_STATUS status; + EFI_INPUT_KEY new_key; + if (!conin) + return 0; + if (efi_have_key) { + return 1; + } + status = __call(conin->ReadKeyStroke, conin, &new_key); + if (status == EFI_SUCCESS) { + if ((new_key.UnicodeChar >= 0) && (new_key.UnicodeChar < 0x7f)) { + efi_have_key = 1; + efi_key = new_key.UnicodeChar; + } + else if (new_key.ScanCode == 0x17) { + efi_have_key = 1; + efi_key = K_ESC; + } + } + return efi_have_key; +} + +int console_getc(void) +{ + if (efi_have_key) { + efi_have_key = 0; + } + + return efi_key; +} +#endif /* CONSOLE_FIRMWARE */ + +#define NAME "Etherboot" +#define FIRMWARE "EFI" + +#define SZ(X) ((sizeof(X)+3) & ~3) +#define CP(D,S) (memcpy(&(D), &(S), sizeof(S))) + + +struct elf_notes { + /* CAREFUL this structure is carefully arranged to avoid + * alignment problems. + */ + /* The note header */ + struct Elf_Bhdr hdr; + + /* First the Fixed sized entries that must be well aligned */ + + /* Insert a nop record so the next record is 64bit aligned */ + Elf_Nhdr nf0; + + /* Pointer to bootp data */ + Elf_Nhdr nf1; + char nf1_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf1_bootp_data; + + /* Pointer to ELF header */ + Elf_Nhdr nf2; + char nf2_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf2_header; + + /* The EFI systab pointer */ + Elf_Nhdr nf3; + char nf3_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf3_systab; + + /* The FPSWA pointer */ + Elf_Nhdr nf4; + char nf4_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf4_fpswa; + + /* The memory map */ + Elf_Nhdr nf5; + char nf5_name[SZ(EB_PARAM_NOTE)]; + struct efi_mem_map nf5_map; + + /* The console info, silly but elilo passes it... */ + Elf_Nhdr nf6; + char nf6_name[SZ(EB_PARAM_NOTE)]; + struct console_info nf6_coninfo; + + /* Then the variable sized data string data where alignment does not matter */ + + /* The bootloader name */ + Elf_Nhdr nv1; + char nv1_desc[SZ(NAME)]; + /* The bootloader version */ + Elf_Nhdr nv2; + char nv2_desc[SZ(VERSION)]; + /* The firmware type */ + Elf_Nhdr nv3; + char nv3_desc[SZ(FIRMWARE)]; + /* Name of the loaded image */ + Elf_Nhdr nv4; + char nv4_loaded_image[128]; + /* An empty command line */ + Elf_Nhdr nv5; + char nv5_cmdline[SZ("")]; +}; + +#define ELF_NOTE_COUNT (6+5) + +static struct elf_notes notes; +struct Elf_Bhdr *prepare_boot_params(void *header) +{ + /* Shutdown the boot services */ + if (boot_services) { + efi_get_coninfo(&efi_info.coninfo); + read_efi_mem_map(&efi_info.mem_map); + efi_exit_boot_services(&efi_info.mem_map); + } + + memset(¬es, 0, sizeof(notes)); + notes.hdr.b_signature = 0x0E1FB007; + notes.hdr.b_size = sizeof(notes); + notes.hdr.b_checksum = 0; + notes.hdr.b_records = ELF_NOTE_COUNT; + + /* Initialize the fixed length entries. */ + + /* Align the fixed length entries to a 64bit boundary */ + notes.nf0.n_namesz = 0; + notes.nf0.n_descsz = 0; + notes.nf0.n_type = EBN_NOP; + + notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data); + notes.nf1.n_type = EB_BOOTP_DATA; + CP(notes.nf1_name, EB_PARAM_NOTE); + notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR); + + notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf2.n_descsz = sizeof(notes.nf2_header); + notes.nf2.n_type = EB_HEADER; + CP(notes.nf2_name, EB_PARAM_NOTE); + notes.nf2_header = virt_to_phys(header); + + notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf3.n_descsz = sizeof(notes.nf3_systab); + notes.nf3.n_type = EB_IA64_SYSTAB; + CP(notes.nf3_name, EB_PARAM_NOTE); + notes.nf3_systab = (unsigned long)efi_info.systab; + + notes.nf4.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf4.n_descsz = sizeof(notes.nf4_fpswa); + notes.nf4.n_type = EB_IA64_FPSWA; + CP(notes.nf4_name, EB_PARAM_NOTE); + notes.nf4_fpswa = (unsigned long)efi_info.fpswa; + + notes.nf5.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf5.n_descsz = sizeof(notes.nf5_map); + notes.nf5.n_type = EB_IA64_MEMMAP; + CP(notes.nf5_name, EB_PARAM_NOTE); + notes.nf5_map = efi_info.mem_map; + + notes.nf6.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf6.n_descsz = sizeof(notes.nf6_coninfo); + notes.nf6.n_type = EB_IA64_CONINFO; + CP(notes.nf6_name, EB_PARAM_NOTE); + notes.nf6_coninfo = efi_info.coninfo; + + /* Initialize the variable length entries */ + notes.nv1.n_namesz = 0; + notes.nv1.n_descsz = sizeof(NAME); + notes.nv1.n_type = EBN_BOOTLOADER_NAME; + CP(notes.nv1_desc, NAME); + + notes.nv2.n_namesz = 0; + notes.nv2.n_descsz = sizeof(VERSION); + notes.nv2.n_type = EBN_BOOTLOADER_VERSION; + CP(notes.nv2_desc, VERSION); + + notes.nv3.n_namesz = 0; + notes.nv3.n_descsz = sizeof(FIRMWARE); + notes.nv3.n_type = EBN_FIRMWARE_TYPE; + CP(notes.nv3_desc, FIRMWARE); + + /* Attempt to pass the name of the loaded image */ + notes.nv4.n_namesz = 0; + notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image); + notes.nv4.n_type = EBN_LOADED_IMAGE; + memcpy(¬es.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image)); + + /* Pass an empty command line for now */ + notes.nv5.n_namesz = 0; + notes.nv5.n_descsz = sizeof(""); + notes.nv5.n_type = EBN_COMMAND_LINE; + CP(notes.nv5_cmdline, ""); + + notes.hdr.b_checksum = ipchksum(¬es, sizeof(notes)); + /* Like UDP invert a 0 checksum to show that a checksum is present */ + if (notes.hdr.b_checksum == 0) { + notes.hdr.b_checksum = 0xffff; + } + + return ¬es.hdr; +} + +int elf_start(unsigned long machine __unused, unsigned long entry, unsigned long params) +{ + struct elf_notes *notes; + int result; + /* Since we can do both be polite and also pass the linux + * ia64_boot_param table. + */ + static struct ia64_boot_param { + uint64_t command_line; /* physical address of command line arguments */ + uint64_t efi_systab; /* physical address of EFI system table */ + uint64_t efi_memmap; /* physical address of EFI memory map */ + uint64_t efi_memmap_size; /* size of EFI memory map */ + uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */ + uint32_t efi_memdesc_version; /* memory descriptor version */ + struct { + uint16_t num_cols; /* number of columns on console output device */ + uint16_t num_rows; /* number of rows on console output device */ + uint16_t orig_x; /* cursor's x position */ + uint16_t orig_y; /* cursor's y position */ + } console_info; + uint64_t fpswa; /* physical address of the fpswa interface */ + uint64_t initrd_start; + uint64_t initrd_size; + } bp; + + notes = phys_to_virt(params); + /* If I don't have notes don't attempt to start the image */ + if (notes == 0) { + return -2; + } + + bp.command_line = (unsigned long)¬es->nv5_cmdline; + bp.efi_systab = notes->nf3_systab; + bp.efi_memmap = (unsigned long)¬es->nf5_map.map; + bp.efi_memmap_size = notes->nf5_map.map_size; + bp.efi_memdesc_size = notes->nf5_map.descriptor_size; + bp.efi_memdesc_version = notes->nf5_map.descriptor_version; + bp.console_info.num_cols = notes->nf6_coninfo.num_cols; + bp.console_info.num_rows = notes->nf6_coninfo.num_rows; + bp.console_info.orig_x = notes->nf6_coninfo.orig_x; + bp.console_info.orig_y = notes->nf6_coninfo.orig_y; + bp.fpswa = notes->nf4_fpswa; + bp.initrd_start = 0; + bp.initrd_size = 0; + + + asm volatile( + ";;\n\t" + "mov r28=%2\n\t" + "mov out0=%3\n\t" + "br.call.sptk.few rp=%1\n\t" + "mov %0=r8\n\t" + : "=r" (result) + : "b"(entry), "r"(&bp),"r"(params) + ); + return result; +} |