From patchwork Sat Jun 28 01:25:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roy Franz X-Patchwork-Id: 32652 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ob0-f199.google.com (mail-ob0-f199.google.com [209.85.214.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 3F90C200B9 for ; Sat, 28 Jun 2014 01:27:38 +0000 (UTC) Received: by mail-ob0-f199.google.com with SMTP id va2sf32860255obc.10 for ; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:date:message-id:in-reply-to :references:cc:subject:precedence:list-id:list-unsubscribe:list-post :list-help:list-subscribe:mime-version:sender:errors-to :x-original-sender:x-original-authentication-results:mailing-list :list-archive:content-type:content-transfer-encoding; bh=EOWjo9WVBliiMdZe+vuJ+2syePa1qPOpz25DItn2e6c=; b=dQDJy8cQiMCD4QJjHrVGtox2SVQcxUSh6dt7ljCOj8rU3uN/ppGOw1mZUgURSdt5fx SwMhdVqo9MJSE10w8pTM4/c5qm/4d0LXs2IefCCUIOUEnWfDxl5K5ivx+/Ud1MQ1haxD Whaa79PBGfEDHlZ/SoYsc2b+rJ10AOkegWp96ycdT8Lr7IEPLjKVFvhz61CiPkxzBJFh M125DqsKfyEpMZHxVWBlcOWgro4PSdHlG4impE16ypKjwN7dNkCvCTssHAxys+e4NFnH lnxEml0QpfcW/4K+q+4Gv+yge5mmoP1KhfgbfVmda7/KDxepNzqmnCQNjmy/ADpVbKik B4XA== X-Gm-Message-State: ALoCoQnqZ3mehUsH67wZgIPnKMkBxjS/WfFG8KUx2nCKw2q7sJS6pVoDD/y4lf8AtGooFBOIvLC/ X-Received: by 10.50.73.197 with SMTP id n5mr2761469igv.1.1403918857903; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.98.11 with SMTP id n11ls860560qge.11.gmail; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) X-Received: by 10.220.17.199 with SMTP id t7mr17890299vca.1.1403918857788; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) Received: from mail-ve0-f175.google.com (mail-ve0-f175.google.com [209.85.128.175]) by mx.google.com with ESMTPS id vw2si7226564vcb.54.2014.06.27.18.27.37 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 27 Jun 2014 18:27:37 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.128.175 as permitted sender) client-ip=209.85.128.175; Received: by mail-ve0-f175.google.com with SMTP id jx11so6124035veb.34 for ; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) X-Received: by 10.58.30.15 with SMTP id o15mr23131970veh.34.1403918857639; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.221.37.5 with SMTP id tc5csp147852vcb; Fri, 27 Jun 2014 18:27:37 -0700 (PDT) X-Received: by 10.50.142.97 with SMTP id rv1mr16713913igb.13.1403918856933; Fri, 27 Jun 2014 18:27:36 -0700 (PDT) Received: from lists.xen.org (lists.xen.org. [50.57.142.19]) by mx.google.com with ESMTPS id ah3si886547igd.50.2014.06.27.18.27.35 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Fri, 27 Jun 2014 18:27:36 -0700 (PDT) Received-SPF: none (google.com: xen-devel-bounces@lists.xen.org does not designate permitted sender hosts) client-ip=50.57.142.19; Received: from localhost ([127.0.0.1] helo=lists.xen.org) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1X0hPB-0007Wk-P4; Sat, 28 Jun 2014 01:26:13 +0000 Received: from mail6.bemta14.messagelabs.com ([193.109.254.103]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1X0hPA-0007VW-KZ for xen-devel@lists.xen.org; Sat, 28 Jun 2014 01:26:13 +0000 Received: from [193.109.254.147:28700] by server-12.bemta-14.messagelabs.com id DC/98-32179-4B91EA35; Sat, 28 Jun 2014 01:26:12 +0000 X-Env-Sender: roy.franz@linaro.org X-Msg-Ref: server-9.tower-27.messagelabs.com!1403918768!14202034!1 X-Originating-IP: [209.85.220.46] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 6.11.3; banners=-,-,- X-VirusChecked: Checked Received: (qmail 24409 invoked from network); 28 Jun 2014 01:26:10 -0000 Received: from mail-pa0-f46.google.com (HELO mail-pa0-f46.google.com) (209.85.220.46) by server-9.tower-27.messagelabs.com with RC4-SHA encrypted SMTP; 28 Jun 2014 01:26:10 -0000 Received: by mail-pa0-f46.google.com with SMTP id eu11so5331978pac.5 for ; Fri, 27 Jun 2014 18:26:08 -0700 (PDT) X-Received: by 10.68.222.196 with SMTP id qo4mr35228290pbc.14.1403918768313; Fri, 27 Jun 2014 18:26:08 -0700 (PDT) Received: from rfranz-t520.local (c-24-10-97-91.hsd1.ca.comcast.net. [24.10.97.91]) by mx.google.com with ESMTPSA id eh4sm16637918pbc.79.2014.06.27.18.26.07 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 27 Jun 2014 18:26:07 -0700 (PDT) From: Roy Franz To: xen-devel@lists.xen.org, ian.campbell@citrix.com, stefano.stabellini@citrix.com, tim@xen.org, jbeulich@suse.com, keir@xen.org Date: Sat, 28 Jun 2014 02:25:35 +0100 Message-Id: <1403918735-30027-20-git-send-email-roy.franz@linaro.org> X-Mailer: git-send-email 2.0.0 In-Reply-To: <1403918735-30027-1-git-send-email-roy.franz@linaro.org> References: <1403918735-30027-1-git-send-email-roy.franz@linaro.org> Cc: Roy Franz , fu.wei@linaro.org, linaro-uefi@lists.linaro.org Subject: [Xen-devel] [PATCH RFC 19/19] Add EFI stub for ARM64 X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: List-Unsubscribe: , List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: roy.franz@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.128.175 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Archive: This patch adds the real EFI application entry point in head.S, and adds the efi.c file that will contain the EFI stub implementation. The assembly wrapper is responsible for returning the processor state to what XEN expects when starting. The EFI stub processes the XEN EFI configuration file to load the dom0 kernel, ramdisk, etc and constructs a device tree for XEN to use, then transfers control to the normal XEN image entry point. Signed-off-by: Roy Franz --- xen/arch/arm/Makefile | 2 + xen/arch/arm/arm64/head.S | 62 ++++- xen/arch/arm/efi-shared.c | 1 + xen/arch/arm/efi.c | 686 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 750 insertions(+), 1 deletion(-) create mode 120000 xen/arch/arm/efi-shared.c create mode 100644 xen/arch/arm/efi.c diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 63e0460..c3ed74f 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -33,6 +33,8 @@ obj-y += hvm.o obj-y += device.o obj-y += decode.o obj-y += processor.o +obj-y += efi.o +obj-y += efi-shared.o #obj-bin-y += ....o diff --git a/xen/arch/arm/arm64/head.S b/xen/arch/arm/arm64/head.S index 9b88aeb..9882519 100644 --- a/xen/arch/arm/arm64/head.S +++ b/xen/arch/arm/arm64/head.S @@ -24,6 +24,8 @@ #include #include #include +#include +#include #define PT_PT 0xf7f /* nG=1 AF=1 SH=11 AP=01 NS=1 ATTR=111 T=1 P=1 */ #define PT_MEM 0xf7d /* nG=1 AF=1 SH=11 AP=01 NS=1 ATTR=111 T=0 P=1 */ @@ -100,7 +102,6 @@ */ .global start -efi_stub_entry: /* Dummy symbol so we can compile before actual stub added */ start: #ifdef CONFIG_EFI_STUB /* @@ -667,6 +668,65 @@ GLOBAL(lookup_processor_type) mov x0, #0 ret + + +ENTRY(efi_stub_entry) + stp x29, x30, [sp, #-32]! + + /* + * Call efi_entry to do the real work. + * x0 and x1 are already set up by firmware. + * + * unsigned long efi_entry(EFI_HANDLE handle, + * EFI_SYSTEM_TABLE *sys_table); + */ + + bl efi_entry + cmp x0, EFI_STUB_ERROR + b.eq efi_load_fail + + /* + * efi_entry() will return here with device tree address in x0. + * Save value in register which is preserved by __flush_dcache_all. + */ + mov x20, x0 + + /* Turn off Dcache and MMU */ + mrs x0, CurrentEL + cmp x0, #PSR_MODE_EL2t + ccmp x0, #PSR_MODE_EL2h, #0x4, ne + b.ne 1f + mrs x0, sctlr_el2 + bic x0, x0, #1 << 0 // clear SCTLR.M + bic x0, x0, #1 << 2 // clear SCTLR.C + msr sctlr_el2, x0 + isb + b 2f +1: + mrs x0, sctlr_el1 + bic x0, x0, #1 << 0 // clear SCTLR.M + bic x0, x0, #1 << 2 // clear SCTLR.C + msr sctlr_el1, x0 + isb +2: +/* bl __flush_dcache_all */ /* RFRANZ_TODO */ + + /* Jump to XEN entry point */ + mov x0, x20 + mov x1, xzr + mov x2, xzr + mov x3, xzr + b real_start + +efi_load_fail: + mov x0, #EFI_LOAD_ERROR + ldp x29, x30, [sp], #32 + ret + +ENDPROC(efi_stub_entry) + + + /* * Local variables: * mode: ASM diff --git a/xen/arch/arm/efi-shared.c b/xen/arch/arm/efi-shared.c new file mode 120000 index 0000000..096ee3a --- /dev/null +++ b/xen/arch/arm/efi-shared.c @@ -0,0 +1 @@ +../x86/efi/efi-shared.c \ No newline at end of file diff --git a/xen/arch/arm/efi.c b/xen/arch/arm/efi.c new file mode 100644 index 0000000..d13e718 --- /dev/null +++ b/xen/arch/arm/efi.c @@ -0,0 +1,686 @@ +#include "efi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EFI_PAGE_SIZE != PAGE_SIZE +# error Cannot use xen/pfn.h here! +#endif +#include +#include +#include + +/* + * This define and the code associated with it is a temporary, and will + * be replaced by changes to the XEN kernel to use the EFI memory map + * that is passed in the device tree instead of the normal FDT memory map. + * The XEN kernel changes are largely indpendent of the stub, so this code + * has been added to allow review and some testing of the stub code while + * the XEN kernel code is being developed. + */ +#define ADD_EFI_MEMORY_TO_FDT + +#define DEVICE_TREE_GUID \ +{0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}} + +extern CHAR16 __initdata newline[]; +extern SIMPLE_TEXT_OUTPUT_INTERFACE *__initdata StdOut; +extern SIMPLE_TEXT_OUTPUT_INTERFACE *__initdata StdERR; +extern EFI_BOOT_SERVICES *__initdata efi_bs; + + +static EFI_HANDLE __initdata efi_ih; + +static struct file __initdata cfg; +static struct file __initdata kernel; +static struct file __initdata ramdisk; +static struct file __initdata dtb; + +static unsigned long mmap_size; +static EFI_MEMORY_DESCRIPTOR *mmap_ptr; + +static void *new_fdt; + +#ifdef ADD_EFI_MEMORY_TO_FDT +static int IsLinuxReservedRegion(int MemoryType) +{ + switch ( MemoryType ) + { + case EfiMemoryMappedIO: + case EfiMemoryMappedIOPortSpace: + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + case EfiUnusableMemory: + case EfiACPIReclaimMemory: + case EfiACPIMemoryNVS: + case EfiLoaderData: + return 1; + default: + return 0; + } +} + + +static EFI_STATUS __init efi_process_memory_map(EFI_MEMORY_DESCRIPTOR *map, + unsigned long mmap_size, + unsigned long desc_size, + void *fdt) +{ + int Index; + int err; + + EFI_MEMORY_DESCRIPTOR *desc_ptr = map; + + /* Go through the list and add the reserved region to the Device Tree */ + for ( Index = 0; Index < (mmap_size / desc_size); Index++ ) + { + if ( IsLinuxReservedRegion(desc_ptr->Type) ) + { + err = fdt_add_mem_rsv(fdt, desc_ptr->PhysicalStart, desc_ptr->NumberOfPages * EFI_PAGE_SIZE); + if ( err != 0 ) + PrintStr(L"Warning: Fail to add 'memreserve'\n\n"); + } + desc_ptr = NextMemoryDescriptor(desc_ptr, desc_size); + } + + return EFI_SUCCESS; + +} +#endif + +static EFI_STATUS __init efi_get_memory_map(EFI_SYSTEM_TABLE *sys_table_arg, + EFI_MEMORY_DESCRIPTOR **map, + unsigned long *mmap_size, + unsigned long *desc_size, + UINT32 *desc_ver, + unsigned long *key_ptr) +{ + EFI_MEMORY_DESCRIPTOR *m = NULL; + EFI_STATUS status; + unsigned long key; + u32 desc_version; + + *mmap_size = sizeof(*m) * 32; +again: + /* + * Add an additional 2 efi_memory_desc_t because we're doing an + * allocation which may be in a new descriptor region, and this + * may create additional entries. + */ + *mmap_size += 2 * sizeof(*m); + status = sys_table_arg->BootServices->AllocatePool(EfiLoaderData, + *mmap_size, (void **)&m); + if ( status != EFI_SUCCESS ) + goto fail; + + *desc_size = 0; + key = 0; + status = sys_table_arg->BootServices->GetMemoryMap(mmap_size, m, + &key, desc_size, &desc_version); + if ( status == EFI_BUFFER_TOO_SMALL ) + { + sys_table_arg->BootServices->FreePool(m); + goto again; + } + + if ( status != EFI_SUCCESS ) + sys_table_arg->BootServices->FreePool(m); + + if ( key_ptr && status == EFI_SUCCESS ) + *key_ptr = key; + if ( desc_ver && status == EFI_SUCCESS ) + *desc_ver = desc_version; + +fail: + *map = m; + return status; +} + + +static void __init *lookup_fdt_config_table(EFI_SYSTEM_TABLE *sys_table) +{ + const EFI_GUID fdt_guid = DEVICE_TREE_GUID; + EFI_CONFIGURATION_TABLE *tables; + void *fdt; + int i; + + tables = sys_table->ConfigurationTable; + fdt = NULL; + + for ( i = 0; i < sys_table->NumberOfTableEntries; i++ ) if ( match_guid(&tables[i].VendorGuid, &fdt_guid) == 0 ) + { + fdt = tables[i].VendorTable; + break; + } + return fdt; +} + +/* + * Get (or set if not present) the #addr-cells and #size cells + * properties of the chose node. We need to know these to properly + * construct the address ranges used to describe the files loaded + * by the stub. + */ +static int setup_chosen_node(void *fdt, int *addr_cells, int *size_cells) +{ + int node; + const struct fdt_property *prop; + int len; + uint32_t val; + + if (!fdt || !addr_cells || !size_cells) + return -1; + + + /* locate chosen node, which is where we add XEN module info. */ + node = fdt_subnode_offset(fdt, 0, "chosen"); + if (node < 0) + { + node = fdt_add_subnode(fdt, 0, "chosen"); + if (node < 0) + return node; + } + + /* Get or set #address-cells and #size-cells */ + prop = fdt_get_property(fdt, node, "#address-cells", &len); + if (!prop) + { + PrintStr(L"No #address-cells in chosen node, setting to 2\r\n"); + val = cpu_to_fdt32(2); + if (fdt_setprop(fdt, node, "#address-cells", &val, sizeof(val))) + return -1; + *addr_cells = 2; + } + else + *addr_cells = fdt32_to_cpu(*((uint32_t *)prop->data)); + + prop = fdt_get_property(fdt, node, "#size-cells", &len); + if (!prop) + { + PrintStr(L"No #size-cells in chosen node, setting to 2\r\n"); + val = cpu_to_fdt32(2); + if (fdt_setprop(fdt, node, "#size-cells", &val, sizeof(val))) + return -1; + *size_cells = 2; + } + else + *size_cells = fdt32_to_cpu(*((uint32_t *)prop->data)); + + /* + * Make sure ranges is empty if it exists, otherwise create empty ranges + * property. + */ + prop = fdt_get_property(fdt, node, "ranges", &len); + if (!prop) + { + PrintStr(L"No ranges in chosen node, creating empty\r\n"); + val = cpu_to_fdt32(2); + if (fdt_setprop(fdt, node, "#size-cells", &val, 0)) + return -1; + } + else + { + if (fdt32_to_cpu(prop->len)) + { + PrintStr(L"Non-empty ranges in chosen node, aborting\r\n"); + return -1; + } + } + return node; +} + + +/* + * Set a single 'reg' property taking into account the + * configured addr and size cell sizes. + */ +static int fdt_set_reg(void *fdt, int node, int addr_cells, int size_cells, + uint64_t addr, uint64_t len) +{ + uint8_t data[16]; /* at most 2 64 bit words */ + void *p = data; + + /* Make sure that the values provided can be represented in + * the reg property. + */ + if (addr_cells == 1 && (addr >> 32)) + return -1; + if (size_cells == 1 && (len >> 32)) + return -1; + + if (addr_cells == 1) + { + *(uint32_t *)p = cpu_to_fdt32(addr); + p += sizeof(uint32_t); + } + else if (addr_cells == 2) + { + *(uint64_t *)p = cpu_to_fdt64(addr); + p += sizeof(uint64_t); + } + else + return -1; + + + if (size_cells == 1) + { + *(uint32_t *)p = cpu_to_fdt32(len); + p += sizeof(uint32_t); + } + else if (size_cells == 2) + { + *(uint64_t *)p = cpu_to_fdt64(len); + p += sizeof(uint64_t); + } + else + return -1; + + return(fdt_setprop(fdt, node, "reg", data, p - (void *)data)); +} + +/* + * Add the FDT nodes for the standard EFI information, which consist + * of the System table address, the address of the final EFI memory map, + * and memory map information. + */ +static EFI_STATUS fdt_add_uefi_nodes(EFI_SYSTEM_TABLE *sys_table, void *fdt, + EFI_MEMORY_DESCRIPTOR *memory_map, + unsigned long map_size, unsigned long desc_size, + u32 desc_ver) +{ + int node; + int status; + u32 fdt_val32; + u64 fdt_val64; + +#ifndef ADD_EFI_MEMORY_TO_FDT + int prev; + /* + * Delete any memory nodes present. The EFI memory map is the only + * memory description provided to XEN. + */ + prev = 0; + for (;;) + { + const char *type; + int len; + + node = fdt_next_node(fdt, prev, NULL); + if (node < 0) + break; + + type = fdt_getprop(fdt, node, "device_type", &len); + if (type && strncmp(type, "memory", len) == 0) + { + fdt_del_node(fdt, node); + continue; + } + + prev = node; + } +#endif + + node = fdt_subnode_offset(fdt, 0, "chosen"); + if ( node < 0 ) + { + node = fdt_add_subnode(fdt, 0, "chosen"); + if ( node < 0 ) + { + status = node; /* node is error code when negative */ + goto fdt_set_fail; + } + } + + /* Add FDT entries for EFI runtime services in chosen node. */ + node = fdt_subnode_offset(fdt, 0, "chosen"); + fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table); + status = fdt_setprop(fdt, node, "linux,uefi-system-table", + &fdt_val64, sizeof(fdt_val64)); + if ( status ) + goto fdt_set_fail; + + fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", + &fdt_val64, sizeof(fdt_val64)); + if ( status ) + goto fdt_set_fail; + + fdt_val32 = cpu_to_fdt32(map_size); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", + &fdt_val32, sizeof(fdt_val32)); + if ( status ) + goto fdt_set_fail; + + fdt_val32 = cpu_to_fdt32(desc_size); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", + &fdt_val32, sizeof(fdt_val32)); + if ( status ) + goto fdt_set_fail; + + fdt_val32 = cpu_to_fdt32(desc_ver); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", + &fdt_val32, sizeof(fdt_val32)); + if ( status ) + goto fdt_set_fail; + + return EFI_SUCCESS; + +fdt_set_fail: + if ( status == -FDT_ERR_NOSPACE ) + return EFI_BUFFER_TOO_SMALL; + + return EFI_LOAD_ERROR; +} + + + +/* + * Allocates new memory for a larger FDT, and frees existing memory if + * struct file size is non-zero. Updates file struct with new memory + * address/size for later freeing. If fdtfile.ptr is NULL, an empty FDT + * is created. + */ +static void __init *fdt_increase_size(struct file *fdtfile, int add_size) +{ + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS fdt_addr; + int fdt_size; + int pages; + void *new_fdt; + + + if ( fdtfile->ptr ) + fdt_size = fdt_totalsize(fdtfile->ptr); + else + { + /* RFRANZ_TODO - missing fdt_create_empty_tree() in libfdt. */ + return NULL; + } + + pages = PFN_UP(fdt_size) + PFN_UP(add_size); + status = efi_bs->AllocatePages(AllocateAnyPages, EfiLoaderData, + pages, &fdt_addr); + + if ( status != EFI_SUCCESS ) + return NULL; + + new_fdt = (void *)fdt_addr; + + if ( fdt_open_into(dtb.ptr, new_fdt, pages * EFI_PAGE_SIZE) ) + return NULL; + + /* + * Now that we have the new FDT allocated and copied, free the + * original and update the struct file so that the error handling + * code will free it. If the original FDT came from a configuration + * table, we don't own that memory and can't free it. + */ + if ( dtb.size ) + efi_bs->FreePages(dtb.addr, PFN_UP(dtb.size)); + + /* Update 'file' info for new memory so we clean it up on error exits */ + dtb.addr = fdt_addr; + dtb.size = pages * EFI_PAGE_SIZE; + return new_fdt; +} + + +/* + * Allocate a new FDT with enough space for EFI and XEN related updates, + * populating with content from a FDT specified in the configuration file + * or configuration table if present. If neither is available, create an + * empty FDT. + */ +static void __init *create_new_fdt(EFI_SYSTEM_TABLE *SystemTable, + EFI_FILE_HANDLE dir_handle, struct file *cfgfile, + const char *section) +{ + union string name = { NULL }; + + /* load dtb from config file or configuration table */ + name.s = get_value(cfgfile, section, "dtb"); + if ( name.s ) + { + truncate_string(name.s); + read_file(dir_handle, s2w(&name), &dtb); + efi_bs->FreePool(name.w); + } + else + { + /* Get DTB from configuration table. */ + dtb.ptr = lookup_fdt_config_table(SystemTable); + if ( dtb.ptr ) + { + /* Set dtb.size to zero so config table memory is not freed. */ + dtb.size = 0; + } + else + { + /* + * No FDT was provided, so create an empty one to populate with + * the EFI and module related params. + * + * RFRANZ_TODO: libfdt in XEN is missing fdt_create_empty_tree() + * likely need libfdt update to support this. + */ + blexit(L"No FDT specified."); + } + } + + + /* + * Allocate space for new FDT, making sure we have enough space + * for the fields we are adding, so we don't have to deal + * with increasing the size again later, which complicates + * things. Use the size of the configuration file as an uppper + * bound on how much size can be added based on configuration + * file contents. + */ + return fdt_increase_size(&dtb, cfg.size + EFI_PAGE_SIZE); +} + + +#define COMPAT_BUF_SIZE 500 /* FDT string buffer size. */ +unsigned long efi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_GUID loaded_image_guid = LOADED_IMAGE_PROTOCOL; + EFI_LOADED_IMAGE *loaded_image; + EFI_FILE_HANDLE dir_handle; + EFI_STATUS status; + union string section = { NULL }, cmdline = { NULL }, name = { NULL }; + CHAR16 * file_name,*cfg_file_name = NULL,*image_name = NULL; + bool_t base_video = 0; + int node; + int chosen; + int addr_len, size_len; + char *options; + char compat_buf[COMPAT_BUF_SIZE]; + int compat_len = 0; + unsigned long desc_size; + UINT32 desc_ver = 0; + unsigned long map_key = 0; + + efi_ih = ImageHandle; + efi_bs = SystemTable->BootServices; + StdOut = SystemTable->ConOut; + + /* Check if we were booted by the EFI firmware */ + if ( SystemTable->Hdr.Signature != EFI_SYSTEM_TABLE_SIGNATURE ) + goto fail; + + /* Get loaded image protocol */ + status = efi_bs->HandleProtocol(ImageHandle, &loaded_image_guid, + (void **)&loaded_image); + if ( status != EFI_SUCCESS ) + blexit(L"ERROR - no loaded image protocol\r\n"); + + PrintStr(L"Xen " __stringify(XEN_VERSION)"." __stringify(XEN_SUBVERSION) + XEN_EXTRAVERSION " (c/s " XEN_CHANGESET ") EFI loader\r\n"); + + if ( (unsigned long)loaded_image->ImageBase & ((1 << 20) - 1) ) + blexit(L"Xen must be loaded at a 2MByte boundary."); + + /* Get the file system interface. */ + dir_handle = get_parent_handle(loaded_image, &file_name); + + handle_cmdline(loaded_image, &cfg_file_name, &base_video, &image_name, + §ion.w, &cmdline.w); + + if ( cmdline.w ) + w2s(&cmdline); + + /* Open and read config file */ + read_config_file(&dir_handle, &cfg, cfg_file_name, + §ion, file_name); + + new_fdt = create_new_fdt(SystemTable, dir_handle, &cfg, section.s); + if ( !new_fdt ) + blexit(L"Unable to create new FDT\r\n"); + + chosen = setup_chosen_node(new_fdt, &addr_len, &size_len); + if ( chosen < 0 ) + blexit(L"Unable to setup chosen node\r\n"); + + + name.s = get_value(&cfg, section.s, "kernel"); + if ( !name.s ) + blexit(L"No Dom0 kernel image specified."); + options = truncate_string(name.s); + if ( options ) + fdt_setprop_string(new_fdt, chosen, "xen,dom0-bootargs", options); + s2w(&name); + read_file(dir_handle, name.w, &kernel); + + node = fdt_add_subnode(new_fdt, chosen, "kernel"); + if ( node < 0 ) + blexit(L"Error adding dom0 FDT node."); + + compat_len = 0; + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, "xen,linux-zimage") + 1; + if ( compat_len > COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, "xen,multiboot-module") + 1; + if ( compat_len > COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + if ( fdt_setprop(new_fdt, node, "compatible", compat_buf, compat_len) < 0 ) + blexit(L"unable to set compatible property."); + fdt_set_reg(new_fdt, node, addr_len, size_len, kernel.addr, kernel.size); + efi_bs->FreePool(name.w); + + + name.s = get_value(&cfg, section.s, "ramdisk"); + if ( name.s ) + { + truncate_string(name.s); + read_file(dir_handle, s2w(&name), &ramdisk); + + node = fdt_add_subnode(new_fdt, chosen, "ramdisk"); + if ( node < 0 ) + blexit(L"Error adding ramdisk FDT node."); + + compat_len = 0; + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, "xen,linux-initrd") + 1; + if ( compat_len > COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, "xen,multiboot-module") + 1; + if ( compat_len > COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + if ( fdt_setprop(new_fdt, node, "compatible", compat_buf, compat_len) < 0 ) + blexit(L"unable to set compatible property."); + fdt_set_reg(new_fdt, node, addr_len, size_len, ramdisk.addr, ramdisk.size); + efi_bs->FreePool(name.w); + } + + + /* + * cmdline has remaining options from EFI command line. Prepend these + * to the options from the configuration file. Put the image name at + * the beginning of the bootargs. + * + */ + if ( image_name ) + { + name.w = image_name; + w2s(&name); + } + else + name.s = "xen"; + + compat_len = 0; + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, "%s", name.s); + if ( compat_len >= COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + if ( cmdline.s ) + { + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, " %s", cmdline.s); + if ( compat_len >= COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + } + name.s = get_value(&cfg, section.s, "options"); + if ( name.s ) + { + compat_len += snprintf(compat_buf + compat_len, COMPAT_BUF_SIZE - compat_len, " %s", name.s); + if ( compat_len >= COMPAT_BUF_SIZE ) + blexit(L"FDT string overflow"); + } + + + if ( fdt_setprop_string(new_fdt, chosen, "xen,xen-bootargs", compat_buf) < 0 ) + blexit(L"unable to set xen,xen-bootargs property."); + + status = efi_get_memory_map(SystemTable, &mmap_ptr, &mmap_size, &desc_size, &desc_ver, &map_key); + if ( status != EFI_SUCCESS ) + blexit(L"unable to get EFI memory map"); + +#ifdef ADD_EFI_MEMORY_TO_FDT + efi_process_memory_map(mmap_ptr, mmap_size, desc_size, new_fdt); +#endif + + status = fdt_add_uefi_nodes(SystemTable, new_fdt, mmap_ptr, mmap_size, desc_size, desc_ver); + if ( status != EFI_SUCCESS ) + { + if ( status == EFI_BUFFER_TOO_SMALL ) + PrintStr(L"ERROR: FDT buffer too small\r\n"); + blexit(L"Unable to create new FDT with UEFI nodes"); + } + + status = efi_bs->ExitBootServices(ImageHandle, map_key); + if ( status != EFI_SUCCESS ) + blexit(L"Unable to exit boot services."); + + return((unsigned long)new_fdt); + + +fail: + blexit(L"ERROR: Unable to start XEN\r\n"); +} + + +void __init noreturn blexit(const CHAR16 *str) +{ + if ( str ) + PrintStr((CHAR16 *)str); + PrintStr(newline); + + if ( cfg.addr ) + efi_bs->FreePages(cfg.addr, PFN_UP(cfg.size)); + if ( kernel.addr ) + efi_bs->FreePages(kernel.addr, PFN_UP(kernel.size)); + if ( ramdisk.addr ) + efi_bs->FreePages(ramdisk.addr, PFN_UP(ramdisk.size)); + if ( dtb.addr && dtb.size ) + efi_bs->FreePages(dtb.addr, PFN_UP(dtb.size)); + if ( mmap_ptr ) + efi_bs->FreePool(mmap_ptr); + + efi_bs->Exit(efi_ih, EFI_SUCCESS, 0, NULL); + unreachable(); /* not reached */ +}