Eric Shelton
2013-May-24 16:28 UTC
Re: [REBASE ver 2] xen: patches (against Linux kernel) for supporting efi
On Fri, May 24, 2013 at 10:24 AM, Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> wrote:> On Thu, May 23, 2013 at 06:04:19AM -0400, Eric Shelton wrote: >> Please find attached and below a rebased version of the patches to the >> Linux kernel needed to get a Linux-based dom0 to come up under >> xen.efi, which were last posted by Tang on Feb 23, 2012. The provided >> patch was made against Linux 3.9.3, but should either apply against or >> be easily backported to older kernel versions (for example, only 2 >> hunks fail against 3.7.10). I tried to take into account Jan''s >> comments offered for the Feb 8, 2012 ver 1 of the patches. > > Hey Eric! > >> >> Using the provided patch, I am able to successfully do a native EFI >> boot using xen.efi on a 2012 MacBook Air, with refind kicking off the >> boot. I have noticed no difference from a non-Xen boot; KDE and wifi >> work normally, for example. A non-Xen boot of the patched 3.9.3 >> kernel works fine as well. The below xen.cfg was used: >> >> video=gfx-1366x768x32 >> options=debug console=vga loglvl=all guest_loglvl=all e820-verbose=1 >> iommu=debug,verbose console_timestamps=true font=8x16 >> kernel=393test.efi ro root=/dev/sda3 debug ignore_loglevel >> earlyprintk=xen i8042.noaux > > Sweet!Thanks. My MacBook seems to be happier when all of its OSes boot under EFI (for example, suspend to RAM seems to be working again in OS X).>> In order of importance, three items are outstanding: >> 1) The attached code only works on 64-bit systems, due to setting the >> EFI_64BIT in xen_efi_probe(). It looks like a new hypercall is needed >> or an existing hypercall needs to be added to check whether the EFI >> BIOS is 32- or 64-bit (see comment in code). >> 2) efivars is not tested >> 3) XEN_FW_EFI_PCI_ROM hypercall was added in the interim, but is unused in dom0 > > Right, that is needed for FrameBuffer handover right? > > Are MacBook''s the only ones that do this?I am not making use of the XEN_FW_EFI_PCI_ROM hypercall, and the provided patch does nothing with it. I am just noting that this new hypercall was added since the original Feb 2012 patchset. What you suggest sounds right, but it is certainly not needed to get my MacBook running - xen gets the efi console running, efifb comes up fine, and then the handover to the i915 driver is good as well. I have X working just fine on my system. The experience is pretty much the same as a non-Xen EFI boot.>> >> I apologize for the attached patch not being split up into 5 pieces as >> previously presented, and for not observing the formalities for patch >> presentation (which is why I also include an attachment). I hope that >> someone with a little more experience and time can address the above >> items (particularly #1) and get it incorporated into the kernel this >> time around. > > Cc-ing Daniel here. He is right now focusing on making Xen working > well with GRUB2-EFI and then focusing on making the Linux kernel EFI > work with Xen properly. This will be a good starting pointI have updated the patch as described below in view of Jan''s comments yesterday. This is about as far as I think I will be able to take it. Adaptation of the patch to 3.10-r2 is trivial. ------------- Please find attached a revised rebased version of the patches to the Linux kernel needed to get a Linux-based dom0 to come up under xen.efi, which were previously posted by Tang Liang on Feb 23, 2012. The provided patch is against Linux 3.9.3, but should be easily adapted for other kernel versions (for example, there is 1 hunk for 3.10-rc2 and 2 hunks for 3.7.10 to be tweaked). I tried to take into account Jan''s comments offered for the Feb 8, 2012 ver 1 of the patches and also for my first posting of the rebased patch. Using the provided patch, I am able to successfully do a native EFI boot using xen.efi on a 2012 MacBook Air, with refind kicking off the boot. Under 3.9.3 I have noticed no difference from a non-Xen boot; KDE and wifi work normally, for example. A non-Xen boot of the patched 3.9.3 kernel works fine as well. I ran into a couple of minor issues under 3.10-rc2 (occasional USB keyboard dropout, i915 fails under X), which I imagine are tied to the instability of the bleeding edge code base. The below xen.cfg was used: video=gfx-1366x768x32 options=debug console=vga loglvl=all guest_loglvl=all e820-verbose=1 iommu=debug,verbose console_timestamps=true font=8x16 kernel=393test.efi ro root=/dev/sda3 debug ignore_loglevel earlyprintk=xen i8042.noaux There are a few outstanding items: 1) Reboot does not work. When booting under EFI, the kernel expects EFI to handle the reboot. I think the patch is making the correct hypercall, but nothing happens. Reboots just fine in a non-Xen EFI environment. 2) efivars is not tested - I am not making use of this facility, so I am not of much help. 3) XEN_FW_EFI_PCI_ROM hypercall was added in the interim, but nothing is done with it in my patch. Presumably there is some code running around that uses it... I apologize for the attached patch not being split up into 5 pieces as previously presented (it turns out two were essentially mooted by intervening changes in the kernel code), and for not observing the formalities for patch presentation (which is why I also include an attachment). I hope that someone with a little more experience and time can address the above items and get it incorporated into the kernel this time around. Signed-off-by: Eric Shelton <eshelton@pobox.com> On May 23, 2013, Jan Beulich wrote:> > It > > sounds like the patch as presented, minus any comments suggesting the > > need for determining if there is a 32-bit EFI, is good on this point, > > since it always sets the EFI_64BIT bit. > > Really it should probably set this bit simply cased upon the > CONFIG_64BIT value.xen.c is modified to only set this bit on 64-bit systems. An additional parameter is added to efi_config_init() so that dom0, whether 32- or 64-bit, will always treat the configuration table entries as 64-bit entries. On February 23, 2012, Tang Liang wrote:> Hi > > The following patches introduce and implement efi support in dom0. > The efi memory is owned by Xen and efi run-time service can not be called > directly in dom0, so a new efi driver is needed by Xen efi. > These patches are based on v3.3.0-rc2+. > > Descriptions for these patches: > > The efi public functions are changed to function pointers in efi_init_funcs > struct. They act as efi generic functions as default. > As a benefit from this change, we can register xen efi init func. > > In order to add xen efi video support, it is required to add xen-efi''s > new video type(XEN_VGATYPE_EFI_LFB) case handler in the function xen_init_vga > and set the video type to VIDEO_TYPE_EFI to enable efi video mode. > > I have tested this patch on Dell Opti 790. > > Xen efi boot support is added by Jan Beulich, more detail information can be > gotten from the url: > http://wiki.xen.org/xenwiki/XenParavirtOps, search "efi" in the page. > > The example of config file for efi boot: > kernel=vmlinuz-3.3.0-rc2+ root=xx ro console=tty0 > ramdisk=initramfs-3.3.0-rc2+.img > video=gfx-x.0 > > The detailed test which i have done: > First, Check efifb driver work well or not and check the kernel messesge ro > see the follow info: > [ 0.576705] efifb: probing for efifb > [ 0.577357] efifb: framebuffer at 0xd0000000, mapped to 0xffffc90005800000, using 3752k, total 65472k > [ 0.577360] efifb: mode is 800x600x32, linelength=3200, pages=1 > [ 0.577362] efifb: scrolling: redraw > [ 0.577364] efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0 > > Second, Check efi systab and variable is work well or not. > cat the information in /sys/firmware/efi to check the efi systab and variable > is right or not. > > Third, Run Linux firmware testing tools which is downloaded from this Url. > http://linuxfirmwarekit.org/download.php > > Jan Beulich (1): > EFI: add efi driver for Xen efi > > Tang Liang (4): > EFI: Provide registration for efi_init.. etc efi public function > EFI: seperate get efi table info code to single function > Xen efi: Add xen efi enabled detect > Xen vga: add the xen efi video mode support > > arch/x86/platform/efi/Makefile | 2 +- > arch/x86/platform/efi/efi-xen.c | 460 ++++++++++++++++++++++++++++++++++++++ > arch/x86/platform/efi/efi.c | 63 +++++- > arch/x86/xen/enlighten.c | 3 + > arch/x86/xen/vga.c | 7 + > include/linux/efi.h | 14 +- > include/xen/interface/platform.h | 122 ++++++++++ > include/xen/interface/xen.h | 1 + > 8 files changed, 665 insertions(+), 7 deletions(-) > > Thanks > Liang.=========== PATCH BELOW ========== diff -urN a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c --- a/arch/x86/platform/efi/efi.c 2013-05-24 01:35:38.303880422 -0400 +++ b/arch/x86/platform/efi/efi.c 2013-05-24 14:12:49.456000000 -0400 @@ -95,6 +95,29 @@ } EXPORT_SYMBOL(efi_enabled); +static void efi_init_generic(void); +static void efi_late_init_generic(void); + +static void efi_enter_virtual_mode_generic(void); +static u32 efi_mem_type_generic(unsigned long phys_addr); +static u64 efi_mem_attributes_generic(unsigned long phys_addr); +static void efi_reserve_boot_services_generic(void); +static void efi_free_boot_services_generic(void); +static int efi_memblock_x86_reserve_range_generic(void); + +static const struct efi_init_funcs __initconst efi_generic_funcs = { + .init = efi_init_generic, + .late_init = efi_late_init_generic, + .reserve_boot_services = efi_reserve_boot_services_generic, + .free_boot_services = efi_free_boot_services_generic, + .enter_virtual_mode = efi_enter_virtual_mode_generic, + .mem_type = efi_mem_type_generic, + .mem_attributes = efi_mem_attributes_generic, + .x86_reserve_range = efi_memblock_x86_reserve_range_generic +}; + +const struct efi_init_funcs __initdata *efi_override_funcs &efi_generic_funcs; + static bool __initdata disable_runtime = false; static int __init setup_noefi(char *arg) { @@ -443,7 +466,7 @@ sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); } -int __init efi_memblock_x86_reserve_range(void) +int __init efi_memblock_x86_reserve_range_generic(void) { unsigned long pmap; @@ -475,6 +498,8 @@ void *p; int i; + if (memmap.map == NULL) return; + for (p = memmap.map, i = 0; p < memmap.map_end; p += memmap.desc_size, i++) { @@ -488,7 +513,7 @@ } #endif /* EFI_DEBUG */ -void __init efi_reserve_boot_services(void) +static void __init efi_reserve_boot_services_generic(void) { void *p; @@ -529,7 +554,7 @@ } } -void __init efi_free_boot_services(void) +void __init efi_free_boot_services_generic(void) { void *p; @@ -644,20 +669,17 @@ return 0; } -static int __init efi_config_init(u64 tables, int nr_tables) +int __init efi_config_init(u64 tables, int nr_tables, + int entrySz, struct efi *efi_t) { void *config_tables, *tablep; - int i, sz; + int i; - if (efi_enabled(EFI_64BIT)) - sz = sizeof(efi_config_table_64_t); - else - sz = sizeof(efi_config_table_32_t); /* * Let''s see what config tables the firmware passed to us. */ - config_tables = early_ioremap(tables, nr_tables * sz); + config_tables = early_ioremap(tables, nr_tables * entrySz); if (config_tables == NULL) { pr_err("Could not map Configuration table!\n"); return -ENOMEM; @@ -665,7 +687,7 @@ tablep = config_tables; pr_info(""); - for (i = 0; i < efi.systab->nr_tables; i++) { + for (i = 0; i < nr_tables; i++) { efi_guid_t guid; unsigned long table; @@ -679,7 +701,7 @@ pr_cont("\n"); pr_err("Table located above 4GB, disabling EFI.\n"); early_iounmap(config_tables, - efi.systab->nr_tables * sz); + nr_tables * entrySz); return -EINVAL; } #endif @@ -688,33 +710,33 @@ table = ((efi_config_table_32_t *)tablep)->table; } if (!efi_guidcmp(guid, MPS_TABLE_GUID)) { - efi.mps = table; + efi_t->mps = table; pr_cont(" MPS=0x%lx ", table); } else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) { - efi.acpi20 = table; + efi_t->acpi20 = table; pr_cont(" ACPI 2.0=0x%lx ", table); } else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) { - efi.acpi = table; + efi_t->acpi = table; pr_cont(" ACPI=0x%lx ", table); } else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) { - efi.smbios = table; + efi_t->smbios = table; pr_cont(" SMBIOS=0x%lx ", table); #ifdef CONFIG_X86_UV } else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) { - efi.uv_systab = table; + efi_t->uv_systab = table; pr_cont(" UVsystab=0x%lx ", table); #endif } else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) { - efi.hcdp = table; + efi_t->hcdp = table; pr_cont(" HCDP=0x%lx ", table); } else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) { - efi.uga = table; + efi_t->uga = table; pr_cont(" UGA=0x%lx ", table); } - tablep += sz; + tablep += entrySz; } pr_cont("\n"); - early_iounmap(config_tables, efi.systab->nr_tables * sz); + early_iounmap(config_tables, nr_tables * entrySz); return 0; } @@ -770,11 +792,11 @@ return 0; } -void __init efi_init(void) +void __init efi_init_generic(void) { efi_char16_t *c16; char vendor[100] = "unknown"; - int i = 0; + int sz, i = 0; void *tmp; struct setup_data *data; struct efi_var_bootdata *efi_var_data; @@ -830,7 +852,13 @@ efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) + if (efi_enabled(EFI_64BIT)) + sz = sizeof(efi_config_table_64_t); + else + sz = sizeof(efi_config_table_32_t); + + if (efi_config_init(efi.systab->tables, efi.systab->nr_tables, + sz, &efi)) return; set_bit(EFI_CONFIG_TABLES, &x86_efi_facility); @@ -865,9 +893,11 @@ #endif } -void __init efi_late_init(void) +void __init efi_late_init_generic(void) { +#ifdef CONFIG_ACPI_BGRT efi_bgrt_init(); +#endif } void __init efi_set_executable(efi_memory_desc_t *md, bool executable) @@ -947,7 +977,7 @@ * This enables the runtime services to be called without having to * thunk back into physical mode for every invocation. */ -void __init efi_enter_virtual_mode(void) +void __init efi_enter_virtual_mode_generic(void) { efi_memory_desc_t *md, *prev_md = NULL; efi_status_t status; @@ -1065,12 +1095,12 @@ efi.get_variable = virt_efi_get_variable; efi.get_next_variable = virt_efi_get_next_variable; efi.set_variable = virt_efi_set_variable; - efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; efi.reset_system = virt_efi_reset_system; - efi.set_virtual_address_map = NULL; efi.query_variable_info = virt_efi_query_variable_info; efi.update_capsule = virt_efi_update_capsule; efi.query_capsule_caps = virt_efi_query_capsule_caps; + efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; + efi.set_virtual_address_map = NULL; if (__supported_pte_mask & _PAGE_NX) runtime_code_page_mkexec(); @@ -1080,7 +1110,7 @@ /* * Convenience functions to obtain memory types and attributes */ -u32 efi_mem_type(unsigned long phys_addr) +u32 __init efi_mem_type_generic(unsigned long phys_addr) { efi_memory_desc_t *md; void *p; @@ -1098,7 +1128,7 @@ return 0; } -u64 efi_mem_attributes(unsigned long phys_addr) +u64 __init efi_mem_attributes_generic(unsigned long phys_addr) { efi_memory_desc_t *md; void *p; @@ -1113,6 +1143,68 @@ return 0; } +void __init efi_init_function_register(const struct efi_init_funcs *funcs) +{ + efi_override_funcs = funcs; +} + +void __init efi_init(void) +{ + if (efi_override_funcs->init) + { + efi_override_funcs->init(); + } + else + { + memmap.map = NULL; /* Xen case */ + } +} + +void __init efi_late_init(void) +{ + if (efi_override_funcs->late_init) + efi_override_funcs->late_init(); +} + +void __init efi_reserve_boot_services(void) +{ + if (efi_override_funcs->reserve_boot_services) + efi_override_funcs->reserve_boot_services(); +} + +void __init efi_free_boot_services(void) +{ + if (efi_override_funcs->free_boot_services) + efi_override_funcs->free_boot_services(); +} + +void __init efi_enter_virtual_mode(void) +{ + if (efi_override_funcs->enter_virtual_mode) + efi_override_funcs->enter_virtual_mode(); +} + +u32 __init efi_mem_type(unsigned long phys_addr) +{ + if (efi_override_funcs->mem_type) + return efi_override_funcs->mem_type(phys_addr); + return EFI_INVALID_TYPE; +} + +u64 __init efi_mem_attributes(unsigned long phys_addr) +{ + if (efi_override_funcs->mem_attributes) + return efi_override_funcs->mem_attributes(phys_addr); + return EFI_INVALID_ATTRIBUTE; +} + +int __init efi_memblock_x86_reserve_range(void) +{ + if (efi_override_funcs->x86_reserve_range) + return efi_override_funcs->x86_reserve_range(); + return 0; +} + /* * Some firmware has serious problems when using more than 50% of the EFI * variable store, i.e. it triggers bugs that can brick machines. Ensure that diff -urN a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile --- a/arch/x86/platform/efi/Makefile 2013-05-24 01:35:38.303880422 -0400 +++ b/arch/x86/platform/efi/Makefile 2013-05-24 01:38:44.875872649 -0400 @@ -1,2 +1,5 @@ obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o +ifdef CONFIG_XEN +obj-$(CONFIG_EFI) += xen.o +endif obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o diff -urN a/arch/x86/platform/efi/xen.c b/arch/x86/platform/efi/xen.c --- a/arch/x86/platform/efi/xen.c 1969-12-31 19:00:00.000000000 -0500 +++ b/arch/x86/platform/efi/xen.c 2013-05-24 14:17:27.080000000 -0400 @@ -0,0 +1,445 @@ +/* + * Xen EFI (Extensible Firmware Interface) support functions + * Based on related efforts in SLE and SUSE trees + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999-2002 Hewlett-Packard Co. + * David Mosberger-Tang <davidm@hpl.hp.com> + * Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 2005-2008 Intel Co. + * Fenghua Yu <fenghua.yu@intel.com> + * Bibo Mao <bibo.mao@intel.com> + * Chandramouli Narayanan <mouli@linux.intel.com> + * Huang Ying <ying.huang@intel.com> + * Copyright (C) 2011 Novell Co. + * Jan Beulic <JBeulich@suse.com> + * Copyright (C) 2011-2012 Oracle Co. + * Liang Tang <liang.tang@oracle.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/efi.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/time.h> + +#include <asm/setup.h> +#include <asm/efi.h> +#include <asm/time.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> +#include <asm/x86_init.h> + +#include <xen/interface/platform.h> +#include <xen/interface/sched.h> +#include <asm/xen/hypercall.h> + +#define PFX "EFI: " + +#define call (op.u.efi_runtime_call) +#define DECLARE_CALL(what) \ + struct xen_platform_op op; \ + op.cmd = XENPF_efi_runtime_call; \ + call.function = XEN_EFI_##what; \ + call.misc = 0 + +static void register_xen_efi_function(void); + +static efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) +{ + int err; + DECLARE_CALL(get_time); + + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + if (tm) { + BUILD_BUG_ON(sizeof(*tm) != sizeof(call.u.get_time.time)); + memcpy(tm, &call.u.get_time.time, sizeof(*tm)); + } + + if (tc) { + tc->resolution = call.u.get_time.resolution; + tc->accuracy = call.u.get_time.accuracy; + tc->sets_to_zero = !!(call.misc & + XEN_EFI_GET_TIME_SET_CLEARS_NS); + } + + return call.status; +} + +static efi_status_t xen_efi_set_time(efi_time_t *tm) +{ + DECLARE_CALL(set_time); + + BUILD_BUG_ON(sizeof(*tm) != sizeof(call.u.set_time)); + memcpy(&call.u.set_time, tm, sizeof(*tm)); + + return HYPERVISOR_dom0_op(&op) ? EFI_UNSUPPORTED : call.status; +} + +static efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, + efi_bool_t *pending, + efi_time_t *tm) +{ + int err; + DECLARE_CALL(get_wakeup_time); + + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + if (tm) { + BUILD_BUG_ON(sizeof(*tm) != sizeof(call.u.get_wakeup_time)); + memcpy(tm, &call.u.get_wakeup_time, sizeof(*tm)); + } + + if (enabled) + *enabled = !!(call.misc & XEN_EFI_GET_WAKEUP_TIME_ENABLED); + + if (pending) + *pending = !!(call.misc & XEN_EFI_GET_WAKEUP_TIME_PENDING); + + return call.status; +} + +static efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) +{ + DECLARE_CALL(set_wakeup_time); + + BUILD_BUG_ON(sizeof(*tm) != sizeof(call.u.set_wakeup_time)); + if (enabled) + call.misc = XEN_EFI_SET_WAKEUP_TIME_ENABLE; + if (tm) + memcpy(&call.u.set_wakeup_time, tm, sizeof(*tm)); + else + call.misc |= XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY; + + return HYPERVISOR_dom0_op(&op) ? EFI_UNSUPPORTED : call.status; +} + +static efi_status_t xen_efi_get_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 *attr, + unsigned long *data_size, + void *data) +{ + int err; + DECLARE_CALL(get_variable); + + set_xen_guest_handle(call.u.get_variable.name, name); + BUILD_BUG_ON(sizeof(*vendor) !+ sizeof(call.u.get_variable.vendor_guid)); + memcpy(&call.u.get_variable.vendor_guid, vendor, sizeof(*vendor)); + call.u.get_variable.size = *data_size; + set_xen_guest_handle(call.u.get_variable.data, data); + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + *data_size = call.u.get_variable.size; + *attr = call.misc; /* misc in struction is U32 variable*/ + + return call.status; +} + +static efi_status_t xen_efi_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *vendor) +{ + int err; + DECLARE_CALL(get_next_variable_name); + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + call.u.get_next_variable_name.size = *name_size; + set_xen_guest_handle(call.u.get_next_variable_name.name, name); + BUILD_BUG_ON(sizeof(*vendor) !+ sizeof(call.u.get_next_variable_name.vendor_guid)); + memcpy(&call.u.get_next_variable_name.vendor_guid, vendor, + sizeof(*vendor)); + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + *name_size = call.u.get_next_variable_name.size; + memcpy(vendor, &call.u.get_next_variable_name.vendor_guid, + sizeof(*vendor)); + + return call.status; +} + +static efi_status_t xen_efi_set_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 attr, + unsigned long data_size, + void *data) +{ + DECLARE_CALL(set_variable); + + set_xen_guest_handle(call.u.set_variable.name, name); + call.misc = attr; + BUILD_BUG_ON(sizeof(*vendor) !+ sizeof(call.u.set_variable.vendor_guid)); + memcpy(&call.u.set_variable.vendor_guid, vendor, sizeof(*vendor)); + call.u.set_variable.size = data_size; + set_xen_guest_handle(call.u.set_variable.data, data); + + return HYPERVISOR_dom0_op(&op) ? EFI_UNSUPPORTED : call.status; +} + +static efi_status_t xen_efi_query_variable_info(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + int err; + DECLARE_CALL(query_variable_info); + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + *storage_space = call.u.query_variable_info.max_store_size; + *remaining_space = call.u.query_variable_info.remain_store_size; + *max_variable_size = call.u.query_variable_info.max_size; + + return call.status; +} + +static efi_status_t xen_efi_get_next_high_mono_count(u32 *count) +{ + int err; + DECLARE_CALL(get_next_high_monotonic_count); + + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + *count = call.misc; + + return call.status; +} + +static efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list) +{ + DECLARE_CALL(update_capsule); + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + set_xen_guest_handle(call.u.update_capsule.capsule_header_array, + capsules); + call.u.update_capsule.capsule_count = count; + call.u.update_capsule.sg_list = sg_list; + + return HYPERVISOR_dom0_op(&op) ? EFI_UNSUPPORTED : call.status; +} + +static efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type) +{ + int err; + DECLARE_CALL(query_capsule_capabilities); + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + set_xen_guest_handle(call.u.query_capsule_capabilities. + capsule_header_array, capsules); + call.u.query_capsule_capabilities.capsule_count = count; + + err = HYPERVISOR_dom0_op(&op); + if (err) + return EFI_UNSUPPORTED; + + *max_size = call.u.query_capsule_capabilities.max_capsule_size; + *reset_type = call.u.query_capsule_capabilities.reset_type; + + return call.status; +} + +#undef DECLARE_CALL +#undef call + +static void xen_efi_reset_system(int reset_type, + efi_status_t status, + unsigned long data_size, + efi_char16_t *data) +{ + struct sched_shutdown r = { .reason = SHUTDOWN_reboot }; + + if (HYPERVISOR_sched_op(SCHEDOP_shutdown, &r)) + BUG(); +} + +static const struct efi __read_mostly efi_xen = { + .systab = NULL, + .mps = EFI_INVALID_TABLE_ADDR, + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, + .sal_systab = EFI_INVALID_TABLE_ADDR, + .boot_info = EFI_INVALID_TABLE_ADDR, + .hcdp = EFI_INVALID_TABLE_ADDR, + .uga = EFI_INVALID_TABLE_ADDR, + .uv_systab = EFI_INVALID_TABLE_ADDR, + .get_time = xen_efi_get_time, + .set_time = xen_efi_set_time, + .get_wakeup_time = xen_efi_get_wakeup_time, + .set_wakeup_time = xen_efi_set_wakeup_time, + .get_variable = xen_efi_get_variable, + .get_next_variable = xen_efi_get_next_variable, + .set_variable = xen_efi_set_variable, + .query_variable_info = xen_efi_query_variable_info, + .update_capsule = xen_efi_update_capsule, + .query_capsule_caps = xen_efi_query_capsule_caps, + .get_next_high_mono_count = xen_efi_get_next_high_mono_count, + .set_virtual_address_map = NULL, + .reset_system = xen_efi_reset_system +}; + +void __init xen_efi_probe(void) +{ + static struct xen_platform_op __initdata op = { + .cmd = XENPF_firmware_info, + .u.firmware_info = { + .type = XEN_FW_EFI_INFO, + .index = XEN_FW_EFI_CONFIG_TABLE + } + }; + + if (HYPERVISOR_dom0_op(&op) == 0) { + set_bit(EFI_BOOT, &x86_efi_facility); + /* since hypervisor calls the runtime, 32/64 bit + * EFI/kernel mismatch is not an issue. Set to + * match kernel so runtime calls will be made */ +#ifdef CONFIG_64BIT + set_bit(EFI_64BIT, &x86_efi_facility); +#endif + + register_xen_efi_function(); + } +} + + +static void __init efi_init_xen(void) +{ + efi_char16_t c16[100]; + char vendor[ARRAY_SIZE(c16)] = "unknown"; + int ret, i; + struct xen_platform_op op; + union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; + + efi = efi_xen; + op.cmd = XENPF_firmware_info; + op.u.firmware_info.type = XEN_FW_EFI_INFO; + + /* + * Show what we know for posterity + */ + op.u.firmware_info.index = XEN_FW_EFI_VENDOR; + info->vendor.bufsz = sizeof(c16); + set_xen_guest_handle(info->vendor.name, c16); + ret = HYPERVISOR_dom0_op(&op); + if (!ret) { + for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) + vendor[i] = c16[i]; + vendor[i] = ''\0''; + } else + pr_err("Could not get the firmware vendor!\n"); + + op.u.firmware_info.index = XEN_FW_EFI_VERSION; + ret = HYPERVISOR_dom0_op(&op); + if (!ret) + pr_info("EFI-xen v%u.%.02u by %s\n", + info->version >> 16, + info->version & 0xffff, vendor); + else + pr_err("Could not get EFI revision!\n"); + + op.u.firmware_info.index = XEN_FW_EFI_RT_VERSION; + ret = HYPERVISOR_dom0_op(&op); + if (!ret) + efi.runtime_version = info->version; + else + pr_warn(PFX "Could not get runtime services revision.\n"); + set_bit(EFI_RUNTIME_SERVICES, &x86_efi_facility); + + /* + * Let''s see what config tables the firmware passed to us. + */ + op.u.firmware_info.index = XEN_FW_EFI_CONFIG_TABLE; + if (HYPERVISOR_dom0_op(&op)) + BUG(); + + if (efi_config_init(info->cfg.addr, info->cfg.nent, + sizeof(efi_config_table_64_t), &efi)) + panic("Could not init EFI Configuration Tables!\n"); + set_bit(EFI_CONFIG_TABLES, &x86_efi_facility); + + /* the EFI memory info is digested by the hypervisor and + * supplied to dom0 via E820 entries */ + set_bit(EFI_MEMMAP, &x86_efi_facility); + + set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility); /* not checked */ + + /* NOTE: efi.c only does this for CONFIG_X86_32 */ + x86_platform.get_wallclock = efi_get_time; + x86_platform.set_wallclock = efi_set_rtc_mmss; +} + +/* + * Convenience functions to obtain memory types and attributes + */ +static u32 efi_mem_type_xen(unsigned long phys_addr) +{ + struct xen_platform_op op; + union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; + + op.cmd = XENPF_firmware_info; + op.u.firmware_info.type = XEN_FW_EFI_INFO; + op.u.firmware_info.index = XEN_FW_EFI_MEM_INFO; + info->mem.addr = phys_addr; + info->mem.size = 0; + return HYPERVISOR_dom0_op(&op) ? 0 : info->mem.type; +} + +static u64 efi_mem_attributes_xen(unsigned long phys_addr) +{ + struct xen_platform_op op; + union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; + + op.cmd = XENPF_firmware_info; + op.u.firmware_info.type = XEN_FW_EFI_INFO; + op.u.firmware_info.index = XEN_FW_EFI_MEM_INFO; + info->mem.addr = phys_addr; + info->mem.size = 0; + return HYPERVISOR_dom0_op(&op) ? 0 : info->mem.attr; +} + +static const struct efi_init_funcs __initconst xen_efi_funcs = { + .init = efi_init_xen, + .late_init = NULL, + .reserve_boot_services = NULL, + .free_boot_services = NULL, + .enter_virtual_mode = NULL, + .mem_type = efi_mem_type_xen, + .mem_attributes = efi_mem_attributes_xen, + .x86_reserve_range = NULL +}; + +static void __init register_xen_efi_function(void) +{ + efi_init_function_register(&xen_efi_funcs); +} diff -urN a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c --- a/arch/x86/xen/enlighten.c 2013-05-24 01:35:38.303880422 -0400 +++ b/arch/x86/xen/enlighten.c 2013-05-24 01:37:56.543874663 -0400 @@ -31,6 +31,7 @@ #include <linux/pci.h> #include <linux/gfp.h> #include <linux/memblock.h> +#include <linux/efi.h> #include <xen/xen.h> #include <xen/events.h> @@ -1532,6 +1533,8 @@ xen_setup_runstate_info(0); + if (xen_initial_domain()) + xen_efi_probe(); /* Start the world */ #ifdef CONFIG_X86_32 i386_start_kernel(); diff -urN a/include/linux/efi.h b/include/linux/efi.h --- a/include/linux/efi.h 2013-05-24 01:35:38.299880422 -0400 +++ b/include/linux/efi.h 2013-05-24 01:37:20.311876172 -0400 @@ -84,7 +84,10 @@ #define EFI_PAL_CODE 13 #define EFI_MAX_MEMORY_TYPE 14 +#define EFI_INVALID_TYPE 0xffffffff + /* Attribute values: */ +#define EFI_INVALID_ATTRIBUTE ((u64)0x0000000000000000ULL) /* invalid attribute*/ #define EFI_MEMORY_UC ((u64)0x0000000000000001ULL) /* uncached */ #define EFI_MEMORY_WC ((u64)0x0000000000000002ULL) /* write-coalescing */ #define EFI_MEMORY_WT ((u64)0x0000000000000004ULL) /* write-through */ @@ -554,6 +557,17 @@ efi_set_virtual_address_map_t *set_virtual_address_map; } efi; +struct efi_init_funcs { + void (*init)(void); + void (*late_init)(void); + void (*reserve_boot_services)(void); + void (*free_boot_services)(void); + void (*enter_virtual_mode)(void); + u32 (*mem_type)(unsigned long phys_addr); + u64 (*mem_attributes)(unsigned long phys_addr); + int (*x86_reserve_range)(void); +}; + static inline int efi_guidcmp (efi_guid_t left, efi_guid_t right) { @@ -591,13 +605,17 @@ extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); -extern int __init efi_uart_console_only (void); +extern int efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); extern unsigned long efi_get_time(void); extern int efi_set_rtc_mmss(unsigned long nowtime); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; +extern void efi_init_function_register(const struct efi_init_funcs *funcs); +extern int efi_config_init(u64 tables, int nr_tables, + int entrySz, struct efi *efi_t); +extern void xen_efi_probe(void); /** * efi_range_is_wc - check the WC bit on an address range @@ -621,7 +639,7 @@ } #ifdef CONFIG_EFI_PCDP -extern int __init efi_setup_pcdp_console(char *); +extern int efi_setup_pcdp_console(char *); #endif /* diff -urN a/include/xen/interface/platform.h b/include/xen/interface/platform.h --- a/include/xen/interface/platform.h 2013-05-24 01:35:38.299880422 -0400 +++ b/include/xen/interface/platform.h 2013-05-24 01:36:23.607878534 -0400 @@ -108,10 +108,111 @@ }; DEFINE_GUEST_HANDLE_STRUCT(xenpf_platform_quirk_t); +#define XENPF_efi_runtime_call 49 +#define XEN_EFI_get_time 1 +#define XEN_EFI_set_time 2 +#define XEN_EFI_get_wakeup_time 3 +#define XEN_EFI_set_wakeup_time 4 +#define XEN_EFI_get_next_high_monotonic_count 5 +#define XEN_EFI_get_variable 6 +#define XEN_EFI_set_variable 7 +#define XEN_EFI_get_next_variable_name 8 +#define XEN_EFI_query_variable_info 9 +#define XEN_EFI_query_capsule_capabilities 10 +#define XEN_EFI_update_capsule 11 + +struct xenpf_efi_runtime_call { + uint32_t function; + /* + * This field is generally used for per sub-function flags (defined + * below), except for the XEN_EFI_get_next_high_monotonic_count case, + * where it holds the single returned value. + */ + uint32_t misc; + unsigned long status; + union { +#define XEN_EFI_GET_TIME_SET_CLEARS_NS 0x00000001 + struct { + struct xenpf_efi_time { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + uint32_t ns; + int16_t tz; + uint8_t daylight; + } time; + uint32_t resolution; + uint32_t accuracy; + } get_time; + + struct xenpf_efi_time set_time; + +#define XEN_EFI_GET_WAKEUP_TIME_ENABLED 0x00000001 +#define XEN_EFI_GET_WAKEUP_TIME_PENDING 0x00000002 + struct xenpf_efi_time get_wakeup_time; + +#define XEN_EFI_SET_WAKEUP_TIME_ENABLE 0x00000001 +#define XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY 0x00000002 + struct xenpf_efi_time set_wakeup_time; + +#define XEN_EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define XEN_EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define XEN_EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 + struct { + GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */ + unsigned long size; + GUEST_HANDLE(void) data; + struct xenpf_efi_guid { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; + } vendor_guid; + } get_variable, set_variable; + + struct { + unsigned long size; + GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */ + struct xenpf_efi_guid vendor_guid; + } get_next_variable_name; + + struct { + uint32_t attr; + uint64_t max_store_size; + uint64_t remain_store_size; + uint64_t max_size; + } query_variable_info; + + struct { + GUEST_HANDLE(void) capsule_header_array; + unsigned long capsule_count; + uint64_t max_capsule_size; + unsigned int reset_type; + } query_capsule_capabilities; + + struct { + GUEST_HANDLE(void) capsule_header_array; + unsigned long capsule_count; + uint64_t sg_list; /* machine address */ + } update_capsule; + } u; +}; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_efi_runtime_call); + #define XENPF_firmware_info 50 #define XEN_FW_DISK_INFO 1 /* from int 13 AH=08/41/48 */ #define XEN_FW_DISK_MBR_SIGNATURE 2 /* from MBR offset 0x1b8 */ #define XEN_FW_VBEDDC_INFO 3 /* from int 10 AX=4f15 */ +#define XEN_FW_EFI_INFO 4 /* from EFI */ +#define XEN_FW_EFI_VERSION 0 +#define XEN_FW_EFI_CONFIG_TABLE 1 +#define XEN_FW_EFI_VENDOR 2 +#define XEN_FW_EFI_MEM_INFO 3 +#define XEN_FW_EFI_RT_VERSION 4 +#define XEN_FW_EFI_PCI_ROM 5 #define XEN_FW_KBD_SHIFT_FLAGS 5 /* Int16, Fn02: Get keyboard shift flags. */ struct xenpf_firmware_info { /* IN variables. */ @@ -143,6 +244,36 @@ /* must refer to 128-byte buffer */ GUEST_HANDLE(uchar) edid; } vbeddc_info; /* XEN_FW_VBEDDC_INFO */ + union xenpf_efi_info { + uint32_t version; + struct { + uint64_t addr; /* EFI_CONFIGURATION_TABLE */ + uint32_t nent; + } cfg; + struct { + uint32_t revision; + uint32_t bufsz; /* input, in bytes */ + GUEST_HANDLE(void) name; + /* UCS-2/UTF-16 string */ + } vendor; + struct { + uint64_t addr; + uint64_t size; + uint64_t attr; + uint32_t type; + } mem; + struct { + /* IN variables */ + uint16_t segment; + uint8_t bus; + uint8_t devfn; + uint16_t vendor; + uint16_t devid; + /* OUT variables */ + uint64_t address; + xen_ulong_t size; + } pci_rom; + } efi_info; /* XEN_FW_EFI_INFO */ uint8_t kbd_shift_flags; /* XEN_FW_KBD_SHIFT_FLAGS */ } u; @@ -361,6 +492,7 @@ struct xenpf_read_memtype read_memtype; struct xenpf_microcode_update microcode; struct xenpf_platform_quirk platform_quirk; + struct xenpf_efi_runtime_call efi_runtime_call; struct xenpf_firmware_info firmware_info; struct xenpf_enter_acpi_sleep enter_acpi_sleep; struct xenpf_change_freq change_freq; diff -urN a/init/main.c b/init/main.c --- a/init/main.c 2013-05-24 01:35:38.299880422 -0400 +++ b/init/main.c 2013-05-24 01:37:36.655875491 -0400 @@ -634,10 +634,12 @@ acpi_early_init(); /* before LAPIC and SMP init */ sfi_init_late(); +#ifdef CONFIG_X86 if (efi_enabled(EFI_RUNTIME_SERVICES)) { efi_late_init(); efi_free_boot_services(); } +#endif ftrace_init(); _______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel