From ac09c5f43cf613939850cc38d7a34ae6556016ba Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 30 Jan 2019 11:19:37 +0100 Subject: [PATCH 01/13] x86/boot: Build the command line parsing code unconditionally Just drop the three-item ifdeffery and build it in unconditionally. Early cmdline parsing is needed more often than not. Signed-off-by: Borislav Petkov Cc: bhe@redhat.com Cc: hpa@zytor.com Cc: indou.takao@jp.fujitsu.com Cc: kasong@redhat.com Cc: keescook@chromium.org Cc: mingo@redhat.com Cc: msys.mizuma@gmail.com Cc: tglx@linutronix.de Cc: x86@kernel.org Link: https://lkml.kernel.org/r/20190130112238.GB18383@zn.tnic --- arch/x86/boot/compressed/cmdline.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/x86/boot/compressed/cmdline.c b/arch/x86/boot/compressed/cmdline.c index af6cda0b7900..f1add5d85da9 100644 --- a/arch/x86/boot/compressed/cmdline.c +++ b/arch/x86/boot/compressed/cmdline.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "misc.h" -#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE || CONFIG_X86_5LEVEL - static unsigned long fs; static inline void set_fs(unsigned long seg) { @@ -30,5 +28,3 @@ int cmdline_find_option_bool(const char *option) { return __cmdline_find_option_bool(get_cmd_line_ptr(), option); } - -#endif From de50ce20cd05da4d1a7e5709a12fc23bc0b66be9 Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:44 +0800 Subject: [PATCH 02/13] x86/boot: Copy kstrtoull() to boot/string.c Copy kstrtoull() and the other necessary functions from lib/kstrtox.c to boot/string.c so that code in boot/ can use kstrtoull() and the old simple_strtoull() can gradually be phased out. Using div_u64() from math64.h directly will cause the dividend to be handled as a 64-bit value and cause the infamous __divdi3 linker error due to gcc trying to use its library function for the 64-bit division. Therefore, separate the dividend into an upper and lower part. [ bp: Rewrite commit message. ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: bhe@redhat.com Cc: caoj.fnst@cn.fujitsu.com Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: kasong@redhat.com Cc: Kees Cook Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-2-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/string.c | 141 +++++++++++++++++++++++++++++++++++++++++ arch/x86/boot/string.h | 1 + 2 files changed, 142 insertions(+) diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c index c4428a176973..315a67b8896b 100644 --- a/arch/x86/boot/string.c +++ b/arch/x86/boot/string.c @@ -13,10 +13,14 @@ */ #include +#include +#include #include #include "ctype.h" #include "string.h" +#define KSTRTOX_OVERFLOW (1U << 31) + /* * Undef these macros so that the functions that we provide * here will have the correct names regardless of how string.h @@ -187,3 +191,140 @@ char *strchr(const char *s, int c) return NULL; return (char *)s; } + +static inline u64 __div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + union { + u64 v64; + u32 v32[2]; + } d = { dividend }; + u32 upper; + + upper = d.v32[1]; + d.v32[1] = 0; + if (upper >= divisor) { + d.v32[1] = upper / divisor; + upper %= divisor; + } + asm ("divl %2" : "=a" (d.v32[0]), "=d" (*remainder) : + "rm" (divisor), "0" (d.v32[0]), "1" (upper)); + return d.v64; +} + +static inline u64 __div_u64(u64 dividend, u32 divisor) +{ + u32 remainder; + + return __div_u64_rem(dividend, divisor, &remainder); +} + +static inline char _tolower(const char c) +{ + return c | 0x20; +} + +static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) +{ + if (*base == 0) { + if (s[0] == '0') { + if (_tolower(s[1]) == 'x' && isxdigit(s[2])) + *base = 16; + else + *base = 8; + } else + *base = 10; + } + if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') + s += 2; + return s; +} + +/* + * Convert non-negative integer string representation in explicitly given radix + * to an integer. + * Return number of characters consumed maybe or-ed with overflow bit. + * If overflow occurs, result integer (incorrect) is still returned. + * + * Don't you dare use this function. + */ +static unsigned int _parse_integer(const char *s, + unsigned int base, + unsigned long long *p) +{ + unsigned long long res; + unsigned int rv; + + res = 0; + rv = 0; + while (1) { + unsigned int c = *s; + unsigned int lc = c | 0x20; /* don't tolower() this line */ + unsigned int val; + + if ('0' <= c && c <= '9') + val = c - '0'; + else if ('a' <= lc && lc <= 'f') + val = lc - 'a' + 10; + else + break; + + if (val >= base) + break; + /* + * Check for overflow only if we are within range of + * it in the max base we support (16) + */ + if (unlikely(res & (~0ull << 60))) { + if (res > __div_u64(ULLONG_MAX - val, base)) + rv |= KSTRTOX_OVERFLOW; + } + res = res * base + val; + rv++; + s++; + } + *p = res; + return rv; +} + +static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + unsigned long long _res; + unsigned int rv; + + s = _parse_integer_fixup_radix(s, &base); + rv = _parse_integer(s, base, &_res); + if (rv & KSTRTOX_OVERFLOW) + return -ERANGE; + if (rv == 0) + return -EINVAL; + s += rv; + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + *res = _res; + return 0; +} + +/** + * kstrtoull - convert a string to an unsigned long long + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. + */ +int kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + if (s[0] == '+') + s++; + return _kstrtoull(s, base, res); +} diff --git a/arch/x86/boot/string.h b/arch/x86/boot/string.h index 3d78e27077f4..38d8f2f5e47e 100644 --- a/arch/x86/boot/string.h +++ b/arch/x86/boot/string.h @@ -29,4 +29,5 @@ extern unsigned int atou(const char *s); extern unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base); +int kstrtoull(const char *s, unsigned int base, unsigned long long *res); #endif /* BOOT_STRING_H */ From 3c98e71b42a7d0c14639ccb565ea315f3c887f92 Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:45 +0800 Subject: [PATCH 03/13] x86/boot: Add "acpi_rsdp=" early parsing KASLR may randomly choose offsets which are located in movable memory regions resulting in the movable memory becoming immovable. The ACPI SRAT (System/Static Resource Affinity Table) describes memory ranges including ranges of memory provided by hot-added memory devices. In order to access SRAT, one needs the Root System Description Pointer (RSDP) with which to find the Root/Extended System Description Table (R/XSDT) which then contains the system description tables of which SRAT is one of. In case the RSDP address has been passed on the command line (kexec-ing a second kernel) parse it from there. [ bp: Rewrite the commit message and cleanup the code. ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: bhe@redhat.com Cc: caoj.fnst@cn.fujitsu.com Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: Juergen Gross Cc: kasong@redhat.com Cc: Kees Cook Cc: "Kirill A. Shutemov" Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: Tom Lendacky Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-3-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/compressed/acpi.c | 32 ++++++++++++++++++++++++++++++++ arch/x86/boot/compressed/misc.h | 3 +++ 2 files changed, 35 insertions(+) create mode 100644 arch/x86/boot/compressed/acpi.c diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c new file mode 100644 index 000000000000..bacfc4ea35ac --- /dev/null +++ b/arch/x86/boot/compressed/acpi.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +#define BOOT_CTYPE_H +#include "misc.h" +#include "error.h" +#include "../string.h" + +#ifdef CONFIG_ACPI + +/* + * Max length of 64-bit hex address string is 19, prefix "0x" + 16 hex + * digits, and '\0' for termination. + */ +#define MAX_ADDR_LEN 19 + +static acpi_physical_address get_acpi_rsdp(void) +{ + acpi_physical_address addr = 0; + +#ifdef CONFIG_KEXEC + char val[MAX_ADDR_LEN] = { }; + int ret; + + ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN); + if (ret < 0) + return 0; + + if (kstrtoull(val, 16, &addr)) + return 0; +#endif + return addr; +} +#endif /* CONFIG_ACPI */ diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index a1d5918765f3..764ad50c0119 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -25,6 +25,9 @@ #include #include +#define BOOT_CTYPE_H +#include + #define BOOT_BOOT_H #include "../ctype.h" From 33f0df8d843deb9ec24116dcd79a40ca0ea8e8a9 Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:46 +0800 Subject: [PATCH 04/13] x86/boot: Search for RSDP in the EFI tables The immovable memory ranges information in the SRAT table is necessary to fix the issue of KASLR not paying attention to movable memory regions when selecting the offset. Therefore, SRAT needs to be parsed. Depending on the boot: KEXEC/EFI/BIOS, the methods to compute RSDP are different. When booting from EFI, the EFI table points to the RSDP. So iterate over the EFI system tables in order to find the RSDP. [ bp: - Heavily massage commit message - Trim comments - Move the CONFIG_ACPI ifdeffery into the Makefile. ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: bhe@redhat.com Cc: caoj.fnst@cn.fujitsu.com Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: kasong@redhat.com Cc: Kees Cook Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-4-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/compressed/acpi.c | 81 ++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index bacfc4ea35ac..5559fde1c0fe 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -4,7 +4,8 @@ #include "error.h" #include "../string.h" -#ifdef CONFIG_ACPI +#include +#include /* * Max length of 64-bit hex address string is 19, prefix "0x" + 16 hex @@ -29,4 +30,80 @@ static acpi_physical_address get_acpi_rsdp(void) #endif return addr; } -#endif /* CONFIG_ACPI */ + +/* Search EFI system tables for RSDP. */ +static acpi_physical_address efi_get_rsdp_addr(void) +{ + acpi_physical_address rsdp_addr = 0; + +#ifdef CONFIG_EFI + efi_system_table_t *systab; + struct efi_info *ei; + bool efi_64; + int size, i; + char *sig; + + ei = &boot_params->efi_info; + sig = (char *)&ei->efi_loader_signature; + + if (!strncmp(sig, EFI64_LOADER_SIGNATURE, 4)) { + efi_64 = true; + } else if (!strncmp(sig, EFI32_LOADER_SIGNATURE, 4)) { + efi_64 = false; + } else { + debug_putstr("Wrong EFI loader signature.\n"); + return 0; + } + + /* Get systab from boot params. */ +#ifdef CONFIG_X86_64 + systab = (efi_system_table_t *)(ei->efi_systab | ((__u64)ei->efi_systab_hi<<32)); +#else + if (ei->efi_systab_hi || ei->efi_memmap_hi) { + debug_putstr("Error getting RSDP address: EFI system table located above 4GB.\n"); + return 0; + } + systab = (efi_system_table_t *)ei->efi_systab; +#endif + if (!systab) + error("EFI system table not found."); + + /* + * Get EFI tables from systab. + */ + size = efi_64 ? sizeof(efi_config_table_64_t) : + sizeof(efi_config_table_32_t); + + for (i = 0; i < systab->nr_tables; i++) { + acpi_physical_address table; + void *config_tables; + efi_guid_t guid; + + config_tables = (void *)(systab->tables + size * i); + if (efi_64) { + efi_config_table_64_t *tmp_table; + + tmp_table = config_tables; + guid = tmp_table->guid; + table = tmp_table->table; + + if (!IS_ENABLED(CONFIG_X86_64) && table >> 32) { + debug_putstr("Error getting RSDP address: EFI config table located above 4GB.\n"); + return 0; + } + } else { + efi_config_table_32_t *tmp_table; + + tmp_table = config_tables; + guid = tmp_table->guid; + table = tmp_table->table; + } + + if (!(efi_guidcmp(guid, ACPI_TABLE_GUID))) + rsdp_addr = table; + else if (!(efi_guidcmp(guid, ACPI_20_TABLE_GUID))) + return table; + } +#endif + return rsdp_addr; +} From 93a209aaaad495d7d0bc9b6186a4495934f70402 Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:47 +0800 Subject: [PATCH 05/13] x86/boot: Search for RSDP in memory Scan memory (EBDA) for the RSDP and verify RSDP by signature and checksum. [ bp: - Trim commit message. - Simplify bios_get_rsdp_addr() and cleanup mad casting. ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: bhe@redhat.com Cc: caoj.fnst@cn.fujitsu.com Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: kasong@redhat.com Cc: Kees Cook Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-5-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/compressed/acpi.c | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index 5559fde1c0fe..66bda1b5cf94 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -107,3 +107,80 @@ static acpi_physical_address efi_get_rsdp_addr(void) #endif return rsdp_addr; } + +static u8 compute_checksum(u8 *buffer, u32 length) +{ + u8 *end = buffer + length; + u8 sum = 0; + + while (buffer < end) + sum += *(buffer++); + + return sum; +} + +/* Search a block of memory for the RSDP signature. */ +static u8 *scan_mem_for_rsdp(u8 *start, u32 length) +{ + struct acpi_table_rsdp *rsdp; + u8 *address, *end; + + end = start + length; + + /* Search from given start address for the requested length */ + for (address = start; address < end; address += ACPI_RSDP_SCAN_STEP) { + /* + * Both RSDP signature and checksum must be correct. + * Note: Sometimes there exists more than one RSDP in memory; + * the valid RSDP has a valid checksum, all others have an + * invalid checksum. + */ + rsdp = (struct acpi_table_rsdp *)address; + + /* BAD Signature */ + if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) + continue; + + /* Check the standard checksum */ + if (compute_checksum((u8 *)rsdp, ACPI_RSDP_CHECKSUM_LENGTH)) + continue; + + /* Check extended checksum if table version >= 2 */ + if ((rsdp->revision >= 2) && + (compute_checksum((u8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH))) + continue; + + /* Signature and checksum valid, we have found a real RSDP */ + return address; + } + return NULL; +} + +/* Search RSDP address in EBDA. */ +static acpi_physical_address bios_get_rsdp_addr(void) +{ + unsigned long address; + u8 *rsdp; + + /* Get the location of the Extended BIOS Data Area (EBDA) */ + address = *(u16 *)ACPI_EBDA_PTR_LOCATION; + address <<= 4; + + /* + * Search EBDA paragraphs (EBDA is required to be a minimum of + * 1K length) + */ + if (address > 0x400) { + rsdp = scan_mem_for_rsdp((u8 *)address, ACPI_EBDA_WINDOW_SIZE); + if (rsdp) + return (acpi_physical_address)(unsigned long)rsdp; + } + + /* Search upper memory: 16-byte boundaries in E0000h-FFFFFh */ + rsdp = scan_mem_for_rsdp((u8 *) ACPI_HI_RSDP_WINDOW_BASE, + ACPI_HI_RSDP_WINDOW_SIZE); + if (rsdp) + return (acpi_physical_address)(unsigned long)rsdp; + + return 0; +} From 3a63f70bf4c3a17f5d9c9bf3bc3288a23bdfefce Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:48 +0800 Subject: [PATCH 06/13] x86/boot: Early parse RSDP and save it in boot_params The RSDP is needed by KASLR so parse it early and save it in boot_params.acpi_rsdp_addr, before KASLR setup runs. RSDP is needed by other kernel facilities so have the parsing code built-in instead of a long "depends on" line in Kconfig. [ bp: - Trim commit message and comments - Add CONFIG_ACPI dependency in the Makefile - Move ->acpi_rsdp_addr assignment with the rest of boot_params massaging in extract_kernel(). ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: bhe@redhat.com Cc: Cao jin Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: Juergen Gross Cc: kasong@redhat.com Cc: Kees Cook Cc: "Kirill A. Shutemov" Cc: Masahiro Yamada Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: Tom Lendacky Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-6-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/compressed/Makefile | 2 ++ arch/x86/boot/compressed/acpi.c | 19 +++++++++++++++++++ arch/x86/boot/compressed/misc.c | 3 +++ arch/x86/boot/compressed/misc.h | 7 +++++++ 4 files changed, 31 insertions(+) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index f0515ac895a4..6b84afdd7538 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -84,6 +84,8 @@ ifdef CONFIG_X86_64 vmlinux-objs-y += $(obj)/pgtable_64.o endif +vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o + $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \ diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index 66bda1b5cf94..b2b50ad5299a 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -184,3 +184,22 @@ static acpi_physical_address bios_get_rsdp_addr(void) return 0; } + +/* Return RSDP address on success, otherwise 0. */ +acpi_physical_address get_rsdp_addr(void) +{ + acpi_physical_address pa; + + pa = get_acpi_rsdp(); + + if (!pa) + pa = boot_params->acpi_rsdp_addr; + + if (!pa) + pa = efi_get_rsdp_addr(); + + if (!pa) + pa = bios_get_rsdp_addr(); + + return pa; +} diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 8dd1d5ccae58..c0d6c560df69 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -351,6 +351,9 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap, /* Clear flags intended for solely in-kernel use. */ boot_params->hdr.loadflags &= ~KASLR_FLAG; + /* Save RSDP address for later use. */ + boot_params->acpi_rsdp_addr = get_rsdp_addr(); + sanitize_boot_params(boot_params); if (boot_params->screen_info.orig_video_mode == 7) { diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 764ad50c0119..e1dd15769cf3 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -119,3 +119,10 @@ static inline void console_init(void) void set_sev_encryption_mask(void); #endif + +/* acpi.c */ +#ifdef CONFIG_ACPI +acpi_physical_address get_rsdp_addr(void); +#else +static inline acpi_physical_address get_rsdp_addr(void) { return 0; } +#endif From 02a3e3cdb7f12fb8589e4a2e85d23c9e6544b54a Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:49 +0800 Subject: [PATCH 07/13] x86/boot: Parse SRAT table and count immovable memory regions Parse SRAT for the immovable memory regions and use that information to control which offset KASLR selects so that it doesn't overlap with any movable region. [ bp: - Move struct mem_vector where it is visible so that it builds. - Correct comments. - Rewrite commit message. ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: Ard Biesheuvel Cc: Baoquan He Cc: Cc: Dave Hansen Cc: "H. Peter Anvin" Cc: Cc: Ingo Molnar Cc: Juergen Gross Cc: Cc: Cc: "Kirill A. Shutemov" Cc: Cc: Thomas Gleixner Cc: Tom Lendacky Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-7-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/compressed/acpi.c | 121 +++++++++++++++++++++++++++++++ arch/x86/boot/compressed/kaslr.c | 4 - arch/x86/boot/compressed/misc.h | 10 +++ 3 files changed, 131 insertions(+), 4 deletions(-) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index b2b50ad5299a..c5a949335d8b 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -4,9 +4,22 @@ #include "error.h" #include "../string.h" +#include #include #include +/* + * Longest parameter of 'acpi=' is 'copy_dsdt', plus an extra '\0' + * for termination. + */ +#define MAX_ACPI_ARG_LENGTH 10 + +/* + * Immovable memory regions representation. Max amount of memory regions is + * MAX_NUMNODES*2. + */ +struct mem_vector immovable_mem[MAX_NUMNODES*2]; + /* * Max length of 64-bit hex address string is 19, prefix "0x" + 16 hex * digits, and '\0' for termination. @@ -203,3 +216,111 @@ acpi_physical_address get_rsdp_addr(void) return pa; } + +#if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE) +/* Compute SRAT address from RSDP. */ +static unsigned long get_acpi_srat_table(void) +{ + unsigned long root_table, acpi_table; + struct acpi_table_header *header; + struct acpi_table_rsdp *rsdp; + u32 num_entries, size, len; + char arg[10]; + u8 *entry; + + rsdp = (struct acpi_table_rsdp *)(long)boot_params->acpi_rsdp_addr; + if (!rsdp) + return 0; + + /* Get ACPI root table from RSDP.*/ + if (!(cmdline_find_option("acpi", arg, sizeof(arg)) == 4 && + !strncmp(arg, "rsdt", 4)) && + rsdp->xsdt_physical_address && + rsdp->revision > 1) { + root_table = rsdp->xsdt_physical_address; + size = ACPI_XSDT_ENTRY_SIZE; + } else { + root_table = rsdp->rsdt_physical_address; + size = ACPI_RSDT_ENTRY_SIZE; + } + + if (!root_table) + return 0; + + header = (struct acpi_table_header *)root_table; + len = header->length; + if (len < sizeof(struct acpi_table_header) + size) + return 0; + + num_entries = (len - sizeof(struct acpi_table_header)) / size; + entry = (u8 *)(root_table + sizeof(struct acpi_table_header)); + + while (num_entries--) { + if (size == ACPI_RSDT_ENTRY_SIZE) + acpi_table = *(u32 *)entry; + else + acpi_table = *(u64 *)entry; + + if (acpi_table) { + header = (struct acpi_table_header *)acpi_table; + + if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_SRAT)) + return acpi_table; + } + entry += size; + } + return 0; +} + +/** + * count_immovable_mem_regions - Parse SRAT and cache the immovable + * memory regions into the immovable_mem array. + * + * Return the number of immovable memory regions on success, 0 on failure: + * + * - Too many immovable memory regions + * - ACPI off or no SRAT found + * - No immovable memory region found. + */ +int count_immovable_mem_regions(void) +{ + unsigned long table_addr, table_end, table; + struct acpi_subtable_header *sub_table; + struct acpi_table_header *table_header; + char arg[MAX_ACPI_ARG_LENGTH]; + int num = 0; + + if (cmdline_find_option("acpi", arg, sizeof(arg)) == 3 && + !strncmp(arg, "off", 3)) + return 0; + + table_addr = get_acpi_srat_table(); + if (!table_addr) + return 0; + + table_header = (struct acpi_table_header *)table_addr; + table_end = table_addr + table_header->length; + table = table_addr + sizeof(struct acpi_table_srat); + + while (table + sizeof(struct acpi_subtable_header) < table_end) { + sub_table = (struct acpi_subtable_header *)table; + if (sub_table->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) { + struct acpi_srat_mem_affinity *ma; + + ma = (struct acpi_srat_mem_affinity *)sub_table; + if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && ma->length) { + immovable_mem[num].start = ma->base_address; + immovable_mem[num].size = ma->length; + num++; + } + + if (num >= MAX_NUMNODES*2) { + debug_putstr("Too many immovable memory regions, aborting.\n"); + return 0; + } + } + table += sub_table->length; + } + return num; +} +#endif /* CONFIG_RANDOMIZE_BASE && CONFIG_MEMORY_HOTREMOVE */ diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c index 9ed9709d9947..b251572e77af 100644 --- a/arch/x86/boot/compressed/kaslr.c +++ b/arch/x86/boot/compressed/kaslr.c @@ -87,10 +87,6 @@ static unsigned long get_boot_seed(void) #define KASLR_COMPRESSED_BOOT #include "../../lib/kaslr.c" -struct mem_vector { - unsigned long long start; - unsigned long long size; -}; /* Only supporting at most 4 unusable memmap regions with kaslr */ #define MAX_MEMMAP_REGIONS 4 diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index e1dd15769cf3..9911d11c5d09 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -72,6 +72,10 @@ int cmdline_find_option(const char *option, char *buffer, int bufsize); int cmdline_find_option_bool(const char *option); #endif +struct mem_vector { + unsigned long long start; + unsigned long long size; +}; #if CONFIG_RANDOMIZE_BASE /* kaslr.c */ @@ -126,3 +130,9 @@ acpi_physical_address get_rsdp_addr(void); #else static inline acpi_physical_address get_rsdp_addr(void) { return 0; } #endif + +#if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE) +int count_immovable_mem_regions(void); +#else +static inline int count_immovable_mem_regions(void) { return 0; } +#endif From 690eaa53205769fd5c0c36e3aa737a615135f969 Mon Sep 17 00:00:00 2001 From: Chao Fan Date: Wed, 23 Jan 2019 19:08:50 +0800 Subject: [PATCH 08/13] x86/boot/KASLR: Limit KASLR to extract the kernel in immovable memory only KASLR may randomly choose a range which is located in movable memory regions. As a result, this will break memory hotplug and make the movable memory chosen by KASLR immovable. Therefore, limit KASLR to choose memory regions in the immovable range after consulting the SRAT table. [ bp: - Rewrite commit message. - Trim comments. ] Signed-off-by: Chao Fan Signed-off-by: Borislav Petkov Cc: Ard Biesheuvel Cc: Baoquan He Cc: caoj.fnst@cn.fujitsu.com Cc: Dave Hansen Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: Juergen Gross Cc: kasong@redhat.com Cc: Kees Cook Cc: "Kirill A. Shutemov" Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: Tom Lendacky Cc: x86-ml Link: https://lkml.kernel.org/r/20190123110850.12433-8-fanc.fnst@cn.fujitsu.com --- arch/x86/boot/compressed/kaslr.c | 71 +++++++++++++++++++++++++++----- arch/x86/boot/compressed/misc.h | 1 + 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c index b251572e77af..a2039afe14a7 100644 --- a/arch/x86/boot/compressed/kaslr.c +++ b/arch/x86/boot/compressed/kaslr.c @@ -97,6 +97,8 @@ static bool memmap_too_large; /* Store memory limit specified by "mem=nn[KMG]" or "memmap=nn[KMG]" */ static unsigned long long mem_limit = ULLONG_MAX; +/* Number of immovable memory regions */ +static int num_immovable_mem; enum mem_avoid_index { MEM_AVOID_ZO_RANGE = 0, @@ -413,6 +415,9 @@ static void mem_avoid_init(unsigned long input, unsigned long input_size, /* Mark the memmap regions we need to avoid */ handle_mem_options(); + /* Enumerate the immovable memory regions */ + num_immovable_mem = count_immovable_mem_regions(); + #ifdef CONFIG_X86_VERBOSE_BOOTUP /* Make sure video RAM can be used. */ add_identity_map(0, PMD_SIZE); @@ -568,9 +573,9 @@ static unsigned long slots_fetch_random(void) return 0; } -static void process_mem_region(struct mem_vector *entry, - unsigned long minimum, - unsigned long image_size) +static void __process_mem_region(struct mem_vector *entry, + unsigned long minimum, + unsigned long image_size) { struct mem_vector region, overlap; unsigned long start_orig, end; @@ -646,6 +651,56 @@ static void process_mem_region(struct mem_vector *entry, } } +static bool process_mem_region(struct mem_vector *region, + unsigned long long minimum, + unsigned long long image_size) +{ + int i; + /* + * If no immovable memory found, or MEMORY_HOTREMOVE disabled, + * use @region directly. + */ + if (!num_immovable_mem) { + __process_mem_region(region, minimum, image_size); + + if (slot_area_index == MAX_SLOT_AREA) { + debug_putstr("Aborted e820/efi memmap scan (slot_areas full)!\n"); + return 1; + } + return 0; + } + +#ifdef CONFIG_MEMORY_HOTREMOVE + /* + * If immovable memory found, filter the intersection between + * immovable memory and @region. + */ + for (i = 0; i < num_immovable_mem; i++) { + unsigned long long start, end, entry_end, region_end; + struct mem_vector entry; + + if (!mem_overlaps(region, &immovable_mem[i])) + continue; + + start = immovable_mem[i].start; + end = start + immovable_mem[i].size; + region_end = region->start + region->size; + + entry.start = clamp(region->start, start, end); + entry_end = clamp(region_end, start, end); + entry.size = entry_end - entry.start; + + __process_mem_region(&entry, minimum, image_size); + + if (slot_area_index == MAX_SLOT_AREA) { + debug_putstr("Aborted e820/efi memmap scan when walking immovable regions(slot_areas full)!\n"); + return 1; + } + } + return 0; +#endif +} + #ifdef CONFIG_EFI /* * Returns true if mirror region found (and must have been processed @@ -711,11 +766,8 @@ process_efi_entries(unsigned long minimum, unsigned long image_size) region.start = md->phys_addr; region.size = md->num_pages << EFI_PAGE_SHIFT; - process_mem_region(®ion, minimum, image_size); - if (slot_area_index == MAX_SLOT_AREA) { - debug_putstr("Aborted EFI scan (slot_areas full)!\n"); + if (process_mem_region(®ion, minimum, image_size)) break; - } } return true; } @@ -742,11 +794,8 @@ static void process_e820_entries(unsigned long minimum, continue; region.start = entry->addr; region.size = entry->size; - process_mem_region(®ion, minimum, image_size); - if (slot_area_index == MAX_SLOT_AREA) { - debug_putstr("Aborted e820 scan (slot_areas full)!\n"); + if (process_mem_region(®ion, minimum, image_size)) break; - } } } diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 9911d11c5d09..19e8231a6fd6 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -132,6 +132,7 @@ static inline acpi_physical_address get_rsdp_addr(void) { return 0; } #endif #if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE) +extern struct mem_vector immovable_mem[MAX_NUMNODES*2]; int count_immovable_mem_regions(void); #else static inline int count_immovable_mem_regions(void) { return 0; } From 82f9ed3a93307089242ff8a5c694e82c8c93f522 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 5 Feb 2019 12:05:45 +0100 Subject: [PATCH 09/13] x86/boot: Fix cmdline_find_option() prototype visibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ac09c5f43cf6 ("x86/boot: Build the command line parsing code unconditionally") enabled building the command line parsing code unconditionally but it forgot to remove the respective ifdeffery around the prototypes in the misc.h header, leading to arch/x86/boot/compressed/acpi.c: In function ‘get_acpi_rsdp’: arch/x86/boot/compressed/acpi.c:37:8: warning: implicit declaration of function \ ‘cmdline_find_option’ [-Wimplicit-function-declaration] ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN); ^~~~~~~~~~~~~~~~~~~ for configs where neither CONFIG_EARLY_PRINTK nor CONFIG_RANDOMIZE_BASE was defined. Drop the ifdeffery in the header too. Fixes: ac09c5f43cf6 ("x86/boot: Build the command line parsing code unconditionally") Reported-by: kbuild test robot Signed-off-by: Borislav Petkov Cc: Chao Fan Cc: x86@kernel.org Link: https://lkml.kernel.org/r/5c51daf0.83pQEkvDZILqoSYW%lkp@intel.com Link: https://lkml.kernel.org/r/20190205131352.GA27396@zn.tnic --- arch/x86/boot/compressed/misc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 19e8231a6fd6..5b8d51996cdd 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -66,11 +66,9 @@ static inline void debug_puthex(const char *s) #endif -#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE /* cmdline.c */ int cmdline_find_option(const char *option, char *buffer, int bufsize); int cmdline_find_option_bool(const char *option); -#endif struct mem_vector { unsigned long long start; From 82df8261c6a9523511d83ac367c7d64375ebabf4 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 5 Feb 2019 14:04:01 +0100 Subject: [PATCH 10/13] x86/boot: Fix randconfig build error due to MEMORY_HOTREMOVE When building randconfigs, one of the failures is: ld: arch/x86/boot/compressed/kaslr.o: in function `choose_random_location': kaslr.c:(.text+0xbf7): undefined reference to `count_immovable_mem_regions' ld: kaslr.c:(.text+0xcbe): undefined reference to `immovable_mem' make[2]: *** [arch/x86/boot/compressed/vmlinux] Error 1 because CONFIG_ACPI is not enabled in this particular .config but CONFIG_MEMORY_HOTREMOVE is and count_immovable_mem_regions() is unresolvable because it is defined in compressed/acpi.c which is the compilation unit that depends on CONFIG_ACPI. Add CONFIG_ACPI to the explicit dependencies for MEMORY_HOTREMOVE. Signed-off-by: Borislav Petkov Cc: Chao Fan Cc: x86@kernel.org Link: https://lkml.kernel.org/r/20190205131033.9564-1-bp@alien8.de --- arch/x86/boot/compressed/kaslr.c | 2 +- arch/x86/boot/compressed/misc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c index a2039afe14a7..fa0332dda9f2 100644 --- a/arch/x86/boot/compressed/kaslr.c +++ b/arch/x86/boot/compressed/kaslr.c @@ -670,7 +670,7 @@ static bool process_mem_region(struct mem_vector *region, return 0; } -#ifdef CONFIG_MEMORY_HOTREMOVE +#if defined(CONFIG_MEMORY_HOTREMOVE) && defined(CONFIG_ACPI) /* * If immovable memory found, filter the intersection between * immovable memory and @region. diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 5b8d51996cdd..fd13655e0f9b 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -129,7 +129,7 @@ acpi_physical_address get_rsdp_addr(void); static inline acpi_physical_address get_rsdp_addr(void) { return 0; } #endif -#if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE) +#if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE) && defined(CONFIG_ACPI) extern struct mem_vector immovable_mem[MAX_NUMNODES*2]; int count_immovable_mem_regions(void); #else From ccec81e4251f5a5421e02874e394338a897056ca Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 5 Feb 2019 01:38:52 +0800 Subject: [PATCH 11/13] x86/kexec: Fill in acpi_rsdp_addr from the first kernel When efi=noruntime or efi=oldmap is used on the kernel command line, EFI services won't be available in the second kernel, therefore the second kernel will not be able to get the ACPI RSDP address from firmware by calling EFI services and so it won't boot. Commit e6e094e053af ("x86/acpi, x86/boot: Take RSDP address from boot params if available") added an acpi_rsdp_addr field to boot_params which stores the RSDP address for other kernel users. Recently, after 3a63f70bf4c3 ("x86/boot: Early parse RSDP and save it in boot_params") the acpi_rsdp_addr will always be filled with a valid RSDP address. So fill in that value into the second kernel's boot_params thus ensuring that the second kernel receives the RSDP value from the first kernel. [ bp: massage commit message. ] Signed-off-by: Kairui Song Signed-off-by: Borislav Petkov Cc: AKASHI Takahiro Cc: Andrew Morton Cc: Baoquan He Cc: Chao Fan Cc: Dave Young Cc: David Howells Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: kexec@lists.infradead.org Cc: Philipp Rudo Cc: Thomas Gleixner Cc: x86-ml Cc: Yannik Sembritzki Link: https://lkml.kernel.org/r/20190204173852.4863-1-kasong@redhat.com --- arch/x86/kernel/kexec-bzimage64.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 0d5efa34f359..2a0ff871025a 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -215,6 +215,9 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params, params->screen_info.ext_mem_k = 0; params->alt_mem_k = 0; + /* Always fill in RSDP: it is either 0 or a valid value */ + params->acpi_rsdp_addr = boot_params.acpi_rsdp_addr; + /* Default APM info */ memset(¶ms->apm_bios_info, 0, sizeof(params->apm_bios_info)); @@ -253,7 +256,6 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params, setup_efi_state(params, params_load_addr, efi_map_offset, efi_map_sz, efi_setup_data_offset); #endif - /* Setup EDD info */ memcpy(params->eddbuf, boot_params.eddbuf, EDDMAXNR * sizeof(struct edd_info)); From f9d230e893e864f13ce5ded9a49990fd024bfed5 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 11 Feb 2019 12:19:45 +0100 Subject: [PATCH 12/13] x86/boot: Correct RSDP parsing with 32-bit EFI Guenter Roeck reported triple faults of a 64-bit VM using a 32-bit OVMF EFI image. After some singlestepping of the image in gdb, it turned out that some of the EFI config tables were at bogus addresses. Which, as Ard pointed out, results from using the wrong efi_config_table typedef. So switch all EFI table pointers to unsigned longs and convert them to the proper typedef only when accessing them. This way, the proper table type is being used. Shorten variable names, while at it. Fixes: 33f0df8d843d ("x86/boot: Search for RSDP in the EFI tables") Reported-by: Guenter Roeck Signed-off-by: Borislav Petkov Tested-by: Chao Fan Cc: Ard Biesheuvel Cc: bhe@redhat.com Cc: caoj.fnst@cn.fujitsu.com Cc: "H. Peter Anvin" Cc: indou.takao@jp.fujitsu.com Cc: Ingo Molnar Cc: kasong@redhat.com Cc: Kees Cook Cc: msys.mizuma@gmail.com Cc: Thomas Gleixner Cc: x86-ml Link: https://lkml.kernel.org/r/20190208190248.GA10854@roeck-us.net --- arch/x86/boot/compressed/acpi.c | 52 ++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index c5a949335d8b..0ef4ad55b29b 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -50,7 +50,8 @@ static acpi_physical_address efi_get_rsdp_addr(void) acpi_physical_address rsdp_addr = 0; #ifdef CONFIG_EFI - efi_system_table_t *systab; + unsigned long systab, systab_tables, config_tables; + unsigned int nr_tables; struct efi_info *ei; bool efi_64; int size, i; @@ -70,46 +71,57 @@ static acpi_physical_address efi_get_rsdp_addr(void) /* Get systab from boot params. */ #ifdef CONFIG_X86_64 - systab = (efi_system_table_t *)(ei->efi_systab | ((__u64)ei->efi_systab_hi<<32)); + systab = ei->efi_systab | ((__u64)ei->efi_systab_hi << 32); #else if (ei->efi_systab_hi || ei->efi_memmap_hi) { debug_putstr("Error getting RSDP address: EFI system table located above 4GB.\n"); return 0; } - systab = (efi_system_table_t *)ei->efi_systab; + systab = ei->efi_systab; #endif if (!systab) error("EFI system table not found."); - /* - * Get EFI tables from systab. - */ - size = efi_64 ? sizeof(efi_config_table_64_t) : - sizeof(efi_config_table_32_t); + /* Handle EFI bitness properly */ + if (efi_64) { + efi_system_table_64_t *stbl = (efi_system_table_64_t *)systab; - for (i = 0; i < systab->nr_tables; i++) { + config_tables = stbl->tables; + nr_tables = stbl->nr_tables; + size = sizeof(efi_config_table_64_t); + } else { + efi_system_table_32_t *stbl = (efi_system_table_32_t *)systab; + + config_tables = stbl->tables; + nr_tables = stbl->nr_tables; + size = sizeof(efi_config_table_32_t); + } + + if (!config_tables) + error("EFI config tables not found."); + + /* Get EFI tables from systab. */ + for (i = 0; i < nr_tables; i++) { acpi_physical_address table; - void *config_tables; efi_guid_t guid; - config_tables = (void *)(systab->tables + size * i); - if (efi_64) { - efi_config_table_64_t *tmp_table; + config_tables += size; - tmp_table = config_tables; - guid = tmp_table->guid; - table = tmp_table->table; + if (efi_64) { + efi_config_table_64_t *tbl = (efi_config_table_64_t *)config_tables; + + guid = tbl->guid; + table = tbl->table; if (!IS_ENABLED(CONFIG_X86_64) && table >> 32) { debug_putstr("Error getting RSDP address: EFI config table located above 4GB.\n"); return 0; } } else { - efi_config_table_32_t *tmp_table; + efi_config_table_32_t *tbl = (efi_config_table_32_t *)config_tables; - tmp_table = config_tables; - guid = tmp_table->guid; - table = tmp_table->table; + guid = tbl->guid; + table = tbl->table; } if (!(efi_guidcmp(guid, ACPI_TABLE_GUID))) From 6f913de3231e1d70a871135b38219da7810df218 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Tue, 19 Feb 2019 10:52:24 +0300 Subject: [PATCH 13/13] x86/boot/compressed/64: Do not read legacy ROM on EFI system EFI systems do not necessarily provide a legacy ROM. If the ROM is missing the memory is not mapped at all. Trying to dereference values in the legacy ROM area leads to a crash on Macbook Pro. Only look for values in the legacy ROM area for non-EFI system. Fixes: 3548e131ec6a ("x86/boot/compressed/64: Find a place for 32-bit trampoline") Reported-by: Pitam Mitra Signed-off-by: Kirill A. Shutemov Signed-off-by: Thomas Gleixner Tested-by: Bockjoo Kim Cc: bp@alien8.de Cc: hpa@zytor.com Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/20190219075224.35058-1-kirill.shutemov@linux.intel.com Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=202351 --- arch/x86/boot/compressed/pgtable_64.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/x86/boot/compressed/pgtable_64.c b/arch/x86/boot/compressed/pgtable_64.c index 9e2157371491..f8debf7aeb4c 100644 --- a/arch/x86/boot/compressed/pgtable_64.c +++ b/arch/x86/boot/compressed/pgtable_64.c @@ -1,5 +1,7 @@ +#include #include #include +#include #include "pgtable.h" #include "../string.h" @@ -37,9 +39,10 @@ int cmdline_find_option_bool(const char *option); static unsigned long find_trampoline_placement(void) { - unsigned long bios_start, ebda_start; + unsigned long bios_start = 0, ebda_start = 0; unsigned long trampoline_start; struct boot_e820_entry *entry; + char *signature; int i; /* @@ -47,8 +50,18 @@ static unsigned long find_trampoline_placement(void) * This code is based on reserve_bios_regions(). */ - ebda_start = *(unsigned short *)0x40e << 4; - bios_start = *(unsigned short *)0x413 << 10; + /* + * EFI systems may not provide legacy ROM. The memory may not be mapped + * at all. + * + * Only look for values in the legacy ROM for non-EFI system. + */ + signature = (char *)&boot_params->efi_info.efi_loader_signature; + if (strncmp(signature, EFI32_LOADER_SIGNATURE, 4) && + strncmp(signature, EFI64_LOADER_SIGNATURE, 4)) { + ebda_start = *(unsigned short *)0x40e << 4; + bios_start = *(unsigned short *)0x413 << 10; + } if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX) bios_start = BIOS_START_MAX;