From 18aefbc5cc075617b00ffefba70029541e18fd1a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 25 Aug 2015 19:00:48 -0400 Subject: drivers/firmware: Make efi/esrt.c driver explicitly non-modular The Kconfig for this driver is currently hidden with: config EFI_ESRT bool ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. We leave some tags like MODULE_AUTHOR for documentation purposes. We don't replace module.h with init.h since the file already has that. Cc: Peter Jones Cc: linux-efi@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Matt Fleming --- drivers/firmware/efi/esrt.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index a5b95d61ae71..22c5285f7705 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -450,22 +449,10 @@ err: esrt = NULL; return error; } +device_initcall(esrt_sysfs_init); -static void __exit esrt_sysfs_exit(void) -{ - pr_debug("esrt-sysfs: unloading.\n"); - cleanup_entry_list(); - kset_unregister(esrt_kset); - sysfs_remove_group(esrt_kobj, &esrt_attr_group); - kfree(esrt); - esrt = NULL; - kobject_del(esrt_kobj); - kobject_put(esrt_kobj); -} - -module_init(esrt_sysfs_init); -module_exit(esrt_sysfs_exit); - +/* MODULE_AUTHOR("Peter Jones "); MODULE_DESCRIPTION("EFI System Resource Table support"); MODULE_LICENSE("GPL"); +*/ -- cgit v1.2.3 From 12dd00e83fb83fa41dcc965cdd1e1627494348cb Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Wed, 26 Aug 2015 14:24:56 +0100 Subject: efi/x86: Move efi=debug option parsing to core fed6cefe3b6e ("x86/efi: Add a "debug" option to the efi= cmdline") adds the DBG flag, but does so for x86 only. Move this early param parsing to core code. Signed-off-by: Leif Lindholm Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel Cc: Mark Salter Cc: Borislav Petkov Signed-off-by: Matt Fleming --- arch/x86/platform/efi/efi.c | 2 -- drivers/firmware/efi/efi.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 2f61fcddcddb..4d0ff0f27a99 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -961,8 +961,6 @@ static int __init arch_parse_efi_cmdline(char *str) if (parse_option_str(str, "old_map")) set_bit(EFI_OLD_MEMMAP, &efi.flags); - if (parse_option_str(str, "debug")) - set_bit(EFI_DBG, &efi.flags); return 0; } diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 16c4928e36af..f0372a022c86 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -63,6 +63,9 @@ static int __init parse_efi_cmdline(char *str) return -EINVAL; } + if (parse_option_str(str, "debug")) + set_bit(EFI_DBG, &efi.flags); + if (parse_option_str(str, "noruntime")) disable_runtime = true; -- cgit v1.2.3 From 7968c0e338085eba0ee2f0e0b0d833057a966679 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Wed, 26 Aug 2015 14:24:58 +0100 Subject: efi/arm64: Clean up efi_get_fdt_params() interface As we now have a common debug infrastructure between core and arm64 efi, drop the bit of the interface passing verbose output flags around. Signed-off-by: Leif Lindholm Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel Cc: Mark Salter Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Matt Fleming --- arch/arm64/kernel/efi.c | 2 +- drivers/firmware/efi/efi.c | 6 ++---- include/linux/efi.h | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/firmware') diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 612ad5ec1d2e..ab5eeb63e2ca 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -201,7 +201,7 @@ void __init efi_init(void) struct efi_fdt_params params; /* Grab UEFI information placed in FDT by stub */ - if (!efi_get_fdt_params(¶ms, efi_enabled(EFI_DBG))) + if (!efi_get_fdt_params(¶ms)) return; efi_system_table = params.system_table; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index f0372a022c86..a0a0469e2869 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -492,7 +492,6 @@ static __initdata struct { }; struct param_info { - int verbose; int found; void *params; }; @@ -523,21 +522,20 @@ static int __init fdt_find_uefi_params(unsigned long node, const char *uname, else *(u64 *)dest = val; - if (info->verbose) + if (efi_enabled(EFI_DBG)) pr_info(" %s: 0x%0*llx\n", dt_params[i].name, dt_params[i].size * 2, val); } return 1; } -int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose) +int __init efi_get_fdt_params(struct efi_fdt_params *params) { struct param_info info; int ret; pr_info("Getting EFI parameters from FDT:\n"); - info.verbose = verbose; info.found = 0; info.params = params; diff --git a/include/linux/efi.h b/include/linux/efi.h index 26ca9e2fd30e..4677d8a1bfd0 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -902,7 +902,7 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); extern void efi_get_time(struct timespec *now); extern void efi_reserve_boot_services(void); -extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); +extern int efi_get_fdt_params(struct efi_fdt_params *params); extern struct efi_memory_map memmap; extern struct kobject *efi_kobj; -- cgit v1.2.3 From 8be4432eb629a2762bad9173d6b06e6ea9c0dfcb Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Thu, 27 Aug 2015 02:11:19 +0900 Subject: efi: Add EFI_MEMORY_MORE_RELIABLE support to efi_md_typeattr_format() UEFI spec 2.5 introduces new Memory Attribute Definition named EFI_MEMORY_MORE_RELIABLE. This patch adds this new attribute support to efi_md_typeattr_format(). Signed-off-by: Taku Izumi Cc: Tony Luck Cc: Ard Biesheuvel Cc: Xishi Qiu Cc: KAMEZAWA Hiroyuki Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index a0a0469e2869..278d4d88d250 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -591,12 +591,13 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | - EFI_MEMORY_RUNTIME)) + EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) snprintf(pos, size, "|attr=0x%016llx]", (unsigned long long)attr); else - snprintf(pos, size, "|%3s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", + snprintf(pos, size, "|%3s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", attr & EFI_MEMORY_RUNTIME ? "RUN" : "", + attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", attr & EFI_MEMORY_XP ? "XP" : "", attr & EFI_MEMORY_RP ? "RP" : "", attr & EFI_MEMORY_WP ? "WP" : "", -- cgit v1.2.3 From bf924863c9445174c6e118f723dc477e2b6ccc7e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 9 Sep 2015 10:08:15 +0200 Subject: efi: Add support for UEFIv2.5 Properties table Version 2.5 of the UEFI spec introduces a new configuration table called the 'EFI Properties table'. Currently, it is only used to convey whether the Memory Protection feature is enabled, which splits PE/COFF images into separate code and data memory regions. Signed-off-by: Ard Biesheuvel Cc: Leif Lindholm Acked-by: Dave Young Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi.c | 30 ++++++++++++++++-------------- include/linux/efi.h | 13 +++++++++++++ 2 files changed, 29 insertions(+), 14 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 278d4d88d250..c297d78f50fd 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -26,20 +26,21 @@ #include struct efi __read_mostly efi = { - .mps = EFI_INVALID_TABLE_ADDR, - .acpi = EFI_INVALID_TABLE_ADDR, - .acpi20 = EFI_INVALID_TABLE_ADDR, - .smbios = EFI_INVALID_TABLE_ADDR, - .smbios3 = 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, - .fw_vendor = EFI_INVALID_TABLE_ADDR, - .runtime = EFI_INVALID_TABLE_ADDR, - .config_table = EFI_INVALID_TABLE_ADDR, - .esrt = EFI_INVALID_TABLE_ADDR, + .mps = EFI_INVALID_TABLE_ADDR, + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, + .smbios3 = 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, + .fw_vendor = EFI_INVALID_TABLE_ADDR, + .runtime = EFI_INVALID_TABLE_ADDR, + .config_table = EFI_INVALID_TABLE_ADDR, + .esrt = EFI_INVALID_TABLE_ADDR, + .properties_table = EFI_INVALID_TABLE_ADDR, }; EXPORT_SYMBOL(efi); @@ -365,6 +366,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, + {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table}, {NULL_GUID, NULL, NULL}, }; diff --git a/include/linux/efi.h b/include/linux/efi.h index 4677d8a1bfd0..d6a9bee755f2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -596,6 +596,9 @@ void efi_native_runtime_setup(void); #define DEVICE_TREE_GUID \ EFI_GUID( 0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 ) +#define EFI_PROPERTIES_TABLE_GUID \ + EFI_GUID( 0x880aaca3, 0x4adc, 0x4a04, 0x90, 0x79, 0xb7, 0x47, 0x34, 0x08, 0x25, 0xe5 ) + typedef struct { efi_guid_t guid; u64 table; @@ -809,6 +812,15 @@ typedef struct _efi_file_io_interface { #define EFI_FILE_MODE_WRITE 0x0000000000000002 #define EFI_FILE_MODE_CREATE 0x8000000000000000 +typedef struct { + u32 version; + u32 length; + u64 memory_protection_attribute; +} efi_properties_table_t; + +#define EFI_PROPERTIES_TABLE_VERSION 0x00010000 +#define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA 0x1 + #define EFI_INVALID_TABLE_ADDR (~0UL) /* @@ -831,6 +843,7 @@ extern struct efi { unsigned long runtime; /* runtime table */ unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ + unsigned long properties_table; /* properties table */ efi_get_time_t *get_time; efi_set_time_t *set_time; efi_get_wakeup_time_t *get_wakeup_time; -- cgit v1.2.3 From a1041713349d0b823b492d7b4ea4325d0b5666db Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2015 07:29:34 -0700 Subject: efi: Introduce EFI_NX_PE_DATA bit and set it from properties table UEFI v2.5 introduces a runtime memory protection feature that splits PE/COFF runtime images into separate code and data regions. Since this may require special handling by the OS, allocate a EFI_xxx bit to keep track of whether this feature is currently active or not. Signed-off-by: Ard Biesheuvel Cc: Leif Lindholm Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi.c | 18 ++++++++++++++++++ include/linux/efi.h | 1 + 2 files changed, 19 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index c297d78f50fd..31fc864eb037 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -426,6 +426,24 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, } pr_cont("\n"); set_bit(EFI_CONFIG_TABLES, &efi.flags); + + /* Parse the EFI Properties table if it exists */ + if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { + efi_properties_table_t *tbl; + + tbl = early_memremap(efi.properties_table, sizeof(*tbl)); + if (tbl == NULL) { + pr_err("Could not map Properties table!\n"); + return -ENOMEM; + } + + if (tbl->memory_protection_attribute & + EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) + set_bit(EFI_NX_PE_DATA, &efi.flags); + + early_memunmap(tbl, sizeof(*tbl)); + } + return 0; } diff --git a/include/linux/efi.h b/include/linux/efi.h index d6a9bee755f2..fa5106c2f9f5 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -973,6 +973,7 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_PARAVIRT 6 /* Access is via a paravirt interface */ #define EFI_ARCH_1 7 /* First arch-specific bit */ #define EFI_DBG 8 /* Print additional debug info at runtime */ +#define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */ #ifdef CONFIG_EFI /* -- cgit v1.2.3 From 9ac4d5ab3e7ae6f485501cb6bf3965da34ac1bac Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 28 Sep 2015 01:44:16 +0100 Subject: efi: Auto-load the efi-pstore module efi-pstore should be auto-loaded on EFI systems, same as efivars. Signed-off-by: Ben Hutchings Cc: Matthew Garrett Cc: Lee, Chun-Yi Cc: Ard Biesheuvel Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-pstore.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index e992abc5ef26..c8d794c58479 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -400,3 +400,4 @@ module_exit(efivars_pstore_exit); MODULE_DESCRIPTION("EFI variable backend for pstore"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:efivars"); -- cgit v1.2.3 From 0f96a99dab366333439e110d6ad253bc7c557c09 Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Wed, 30 Sep 2015 23:01:56 +0900 Subject: efi: Add "efi_fake_mem" boot option This patch introduces new boot option named "efi_fake_mem". By specifying this parameter, you can add arbitrary attribute to specific memory range. This is useful for debugging of Address Range Mirroring feature. For example, if "efi_fake_mem=2G@4G:0x10000,2G@0x10a0000000:0x10000" is specified, the original (firmware provided) EFI memmap will be updated so that the specified memory regions have EFI_MEMORY_MORE_RELIABLE attribute (0x10000): efi: mem36: [Conventional Memory| | | | | | |WB|WT|WC|UC] range=[0x0000000100000000-0x00000020a0000000) (129536MB) efi: mem36: [Conventional Memory| |MR| | | | |WB|WT|WC|UC] range=[0x0000000100000000-0x0000000180000000) (2048MB) efi: mem37: [Conventional Memory| | | | | | |WB|WT|WC|UC] range=[0x0000000180000000-0x00000010a0000000) (61952MB) efi: mem38: [Conventional Memory| |MR| | | | |WB|WT|WC|UC] range=[0x00000010a0000000-0x0000001120000000) (2048MB) efi: mem39: [Conventional Memory| | | | | | |WB|WT|WC|UC] range=[0x0000001120000000-0x00000020a0000000) (63488MB) And you will find that the following message is output: efi: Memory: 4096M/131455M mirrored memory Signed-off-by: Taku Izumi Cc: Tony Luck Cc: Xishi Qiu Cc: Kamezawa Hiroyuki Cc: Ard Biesheuvel Signed-off-by: Matt Fleming --- Documentation/kernel-parameters.txt | 15 +++ arch/x86/kernel/setup.c | 4 +- drivers/firmware/efi/Kconfig | 22 ++++ drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/fake_mem.c | 238 ++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 6 + 6 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/efi/fake_mem.c (limited to 'drivers/firmware') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1d6f0459cd7b..cd5312f24981 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1092,6 +1092,21 @@ bytes respectively. Such letter suffixes can also be entirely omitted. you are really sure that your UEFI does sane gc and fulfills the spec otherwise your board may brick. + efi_fake_mem= nn[KMG]@ss[KMG]:aa[,nn[KMG]@ss[KMG]:aa,..] [EFI; X86] + Add arbitrary attribute to specific memory range by + updating original EFI memory map. + Region of memory which aa attribute is added to is + from ss to ss+nn. + If efi_fake_mem=2G@4G:0x10000,2G@0x10a0000000:0x10000 + is specified, EFI_MEMORY_MORE_RELIABLE(0x10000) + attribute is added to range 0x100000000-0x180000000 and + 0x10a0000000-0x1120000000. + + Using this parameter you can do debugging of EFI memmap + related feature. For example, you can do debugging of + Address Range Mirroring feature even if your box + doesn't support it. + eisa_irq_edge= [PARISC,HW] See header of drivers/parisc/eisa.c. diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 80f874bf999e..e3ed628f7db4 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1104,8 +1104,10 @@ void __init setup_arch(char **cmdline_p) memblock_set_current_limit(ISA_END_ADDRESS); memblock_x86_fill(); - if (efi_enabled(EFI_BOOT)) + if (efi_enabled(EFI_BOOT)) { + efi_fake_memmap(); efi_find_mirror(); + } /* * The EFI specification says that boot service code won't be called diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 54071c148340..1de6f0ed5077 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -52,6 +52,28 @@ config EFI_RUNTIME_MAP See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. +config EFI_FAKE_MEMMAP + bool "Enable EFI fake memory map" + depends on EFI && X86 + default n + help + Saying Y here will enable "efi_fake_mem" boot option. + By specifying this parameter, you can add arbitrary attribute + to specific memory range by updating original (firmware provided) + EFI memmap. + This is useful for debugging of EFI memmap related feature. + e.g. Address Range Mirroring feature. + +config EFI_MAX_FAKE_MEM + int "maximum allowable number of ranges in efi_fake_mem boot option" + depends on EFI_FAKE_MEMMAP + range 1 128 + default 8 + help + Maximum allowable number of ranges in efi_fake_mem boot option. + Ranges can be set up to this value using comma-separated list. + The default value is 8. + config EFI_PARAMS_FROM_FDT bool help diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 6fd3da938717..c24f00569acb 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_UEFI_CPER) += cper.o obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o obj-$(CONFIG_EFI_STUB) += libstub/ +obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c new file mode 100644 index 000000000000..32bcb14df2c8 --- /dev/null +++ b/drivers/firmware/efi/fake_mem.c @@ -0,0 +1,238 @@ +/* + * fake_mem.c + * + * Copyright (C) 2015 FUJITSU LIMITED + * Author: Taku Izumi + * + * This code introduces new boot option named "efi_fake_mem" + * By specifying this parameter, you can add arbitrary attribute to + * specific memory range by updating original (firmware provided) EFI + * memmap. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include + +#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM + +struct fake_mem { + struct range range; + u64 attribute; +}; +static struct fake_mem fake_mems[EFI_MAX_FAKEMEM]; +static int nr_fake_mem; + +static int __init cmp_fake_mem(const void *x1, const void *x2) +{ + const struct fake_mem *m1 = x1; + const struct fake_mem *m2 = x2; + + if (m1->range.start < m2->range.start) + return -1; + if (m1->range.start > m2->range.start) + return 1; + return 0; +} + +void __init efi_fake_memmap(void) +{ + u64 start, end, m_start, m_end, m_attr; + int new_nr_map = memmap.nr_map; + efi_memory_desc_t *md; + u64 new_memmap_phy; + void *new_memmap; + void *old, *new; + int i; + + if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP)) + return; + + /* count up the number of EFI memory descriptor */ + for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) { + md = old; + start = md->phys_addr; + end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; + + for (i = 0; i < nr_fake_mem; i++) { + /* modifying range */ + m_start = fake_mems[i].range.start; + m_end = fake_mems[i].range.end; + + if (m_start <= start) { + /* split into 2 parts */ + if (start < m_end && m_end < end) + new_nr_map++; + } + if (start < m_start && m_start < end) { + /* split into 3 parts */ + if (m_end < end) + new_nr_map += 2; + /* split into 2 parts */ + if (end <= m_end) + new_nr_map++; + } + } + } + + /* allocate memory for new EFI memmap */ + new_memmap_phy = memblock_alloc(memmap.desc_size * new_nr_map, + PAGE_SIZE); + if (!new_memmap_phy) + return; + + /* create new EFI memmap */ + new_memmap = early_memremap(new_memmap_phy, + memmap.desc_size * new_nr_map); + if (!new_memmap) { + memblock_free(new_memmap_phy, memmap.desc_size * new_nr_map); + return; + } + + for (old = memmap.map, new = new_memmap; + old < memmap.map_end; + old += memmap.desc_size, new += memmap.desc_size) { + + /* copy original EFI memory descriptor */ + memcpy(new, old, memmap.desc_size); + md = new; + start = md->phys_addr; + end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; + + for (i = 0; i < nr_fake_mem; i++) { + /* modifying range */ + m_start = fake_mems[i].range.start; + m_end = fake_mems[i].range.end; + m_attr = fake_mems[i].attribute; + + if (m_start <= start && end <= m_end) + md->attribute |= m_attr; + + if (m_start <= start && + (start < m_end && m_end < end)) { + /* first part */ + md->attribute |= m_attr; + md->num_pages = (m_end - md->phys_addr + 1) >> + EFI_PAGE_SHIFT; + /* latter part */ + new += memmap.desc_size; + memcpy(new, old, memmap.desc_size); + md = new; + md->phys_addr = m_end + 1; + md->num_pages = (end - md->phys_addr + 1) >> + EFI_PAGE_SHIFT; + } + + if ((start < m_start && m_start < end) && m_end < end) { + /* first part */ + md->num_pages = (m_start - md->phys_addr) >> + EFI_PAGE_SHIFT; + /* middle part */ + new += memmap.desc_size; + memcpy(new, old, memmap.desc_size); + md = new; + md->attribute |= m_attr; + md->phys_addr = m_start; + md->num_pages = (m_end - m_start + 1) >> + EFI_PAGE_SHIFT; + /* last part */ + new += memmap.desc_size; + memcpy(new, old, memmap.desc_size); + md = new; + md->phys_addr = m_end + 1; + md->num_pages = (end - m_end) >> + EFI_PAGE_SHIFT; + } + + if ((start < m_start && m_start < end) && + (end <= m_end)) { + /* first part */ + md->num_pages = (m_start - md->phys_addr) >> + EFI_PAGE_SHIFT; + /* latter part */ + new += memmap.desc_size; + memcpy(new, old, memmap.desc_size); + md = new; + md->phys_addr = m_start; + md->num_pages = (end - md->phys_addr + 1) >> + EFI_PAGE_SHIFT; + md->attribute |= m_attr; + } + } + } + + /* swap into new EFI memmap */ + efi_unmap_memmap(); + memmap.map = new_memmap; + memmap.phys_map = (void *)new_memmap_phy; + memmap.nr_map = new_nr_map; + memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size; + set_bit(EFI_MEMMAP, &efi.flags); + + /* print new EFI memmap */ + efi_print_memmap(); +} + +static int __init setup_fake_mem(char *p) +{ + u64 start = 0, mem_size = 0, attribute = 0; + int i; + + if (!p) + return -EINVAL; + + while (*p != '\0') { + mem_size = memparse(p, &p); + if (*p == '@') + start = memparse(p+1, &p); + else + break; + + if (*p == ':') + attribute = simple_strtoull(p+1, &p, 0); + else + break; + + if (nr_fake_mem >= EFI_MAX_FAKEMEM) + break; + + fake_mems[nr_fake_mem].range.start = start; + fake_mems[nr_fake_mem].range.end = start + mem_size - 1; + fake_mems[nr_fake_mem].attribute = attribute; + nr_fake_mem++; + + if (*p == ',') + p++; + } + + sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem), + cmp_fake_mem, NULL); + + for (i = 0; i < nr_fake_mem; i++) + pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", + fake_mems[i].attribute, fake_mems[i].range.start, + fake_mems[i].range.end); + + return *p == '\0' ? 0 : -EINVAL; +} + +early_param("efi_fake_mem", setup_fake_mem); diff --git a/include/linux/efi.h b/include/linux/efi.h index fa5106c2f9f5..4d01c1033fce 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -922,6 +922,12 @@ extern struct kobject *efi_kobj; extern int efi_reboot_quirk_mode; extern bool efi_poweroff_required(void); +#ifdef CONFIG_EFI_FAKE_MEMMAP +extern void __init efi_fake_memmap(void); +#else +static inline void efi_fake_memmap(void) { } +#endif + /* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc(m, md) \ for ((md) = (m)->map; \ -- cgit v1.2.3