aboutsummaryrefslogtreecommitdiffstats
path: root/src/arch/ia64/core/efi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/ia64/core/efi.c')
-rw-r--r--src/arch/ia64/core/efi.c1026
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(&notes, 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(&notes.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(&notes, 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 &notes.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)&notes->nv5_cmdline;
+ bp.efi_systab = notes->nf3_systab;
+ bp.efi_memmap = (unsigned long)&notes->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;
+}