Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

efi: Fix the ACPI BGRT driver for images located in EFI boot services memory

The ACPI BGRT driver accesses the BIOS logo image when it initializes.
However, ACPI 5.0 (which introduces the BGRT) recommends putting the
logo image in EFI boot services memory, so that the OS can reclaim that
memory. Production systems follow this recommendation, breaking the
ACPI BGRT driver.

Move the bulk of the BGRT code to run during a new EFI late
initialization phase, which occurs after switching EFI to virtual mode,
and after initializing ACPI, but before freeing boot services memory.
Copy the BIOS logo image to kernel memory at that point, and make it
accessible to the BGRT driver. Rework the existing ACPI BGRT driver to
act as a simple wrapper exposing that image (and the properties from the
BGRT) via sysfs.

Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Link: http://lkml.kernel.org/r/93ce9f823f1c1f3bb88bdd662cce08eee7a17f5d.1348876882.git.josh@joshtriplett.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>

authored by

Josh Triplett and committed by
H. Peter Anvin
2223af38 7bc90e01

+120 -70
+1
arch/x86/platform/efi/Makefile
··· 1 1 obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o 2 + obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
+76
arch/x86/platform/efi/efi-bgrt.c
··· 1 + /* 2 + * Copyright 2012 Intel Corporation 3 + * Author: Josh Triplett <josh@joshtriplett.org> 4 + * 5 + * Based on the bgrt driver: 6 + * Copyright 2012 Red Hat, Inc <mjg@redhat.com> 7 + * Author: Matthew Garrett 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + */ 13 + #include <linux/kernel.h> 14 + #include <linux/acpi.h> 15 + #include <linux/efi.h> 16 + #include <linux/efi-bgrt.h> 17 + 18 + struct acpi_table_bgrt *bgrt_tab; 19 + void *bgrt_image; 20 + size_t bgrt_image_size; 21 + 22 + struct bmp_header { 23 + u16 id; 24 + u32 size; 25 + } __packed; 26 + 27 + void efi_bgrt_init(void) 28 + { 29 + acpi_status status; 30 + void __iomem *image; 31 + bool ioremapped = false; 32 + struct bmp_header bmp_header; 33 + 34 + if (acpi_disabled) 35 + return; 36 + 37 + status = acpi_get_table("BGRT", 0, 38 + (struct acpi_table_header **)&bgrt_tab); 39 + if (ACPI_FAILURE(status)) 40 + return; 41 + 42 + if (bgrt_tab->version != 1) 43 + return; 44 + if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address) 45 + return; 46 + 47 + image = efi_lookup_mapped_addr(bgrt_tab->image_address); 48 + if (!image) { 49 + image = ioremap(bgrt_tab->image_address, sizeof(bmp_header)); 50 + ioremapped = true; 51 + if (!image) 52 + return; 53 + } 54 + 55 + memcpy_fromio(&bmp_header, image, sizeof(bmp_header)); 56 + if (ioremapped) 57 + iounmap(image); 58 + bgrt_image_size = bmp_header.size; 59 + 60 + bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL); 61 + if (!bgrt_image) 62 + return; 63 + 64 + if (ioremapped) { 65 + image = ioremap(bgrt_tab->image_address, bmp_header.size); 66 + if (!image) { 67 + kfree(bgrt_image); 68 + bgrt_image = NULL; 69 + return; 70 + } 71 + } 72 + 73 + memcpy_fromio(bgrt_image, image, bgrt_image_size); 74 + if (ioremapped) 75 + iounmap(image); 76 + }
+6
arch/x86/platform/efi/efi.c
··· 31 31 #include <linux/kernel.h> 32 32 #include <linux/init.h> 33 33 #include <linux/efi.h> 34 + #include <linux/efi-bgrt.h> 34 35 #include <linux/export.h> 35 36 #include <linux/bootmem.h> 36 37 #include <linux/memblock.h> ··· 744 743 #if EFI_DEBUG 745 744 print_efi_memmap(); 746 745 #endif 746 + } 747 + 748 + void __init efi_late_init(void) 749 + { 750 + efi_bgrt_init(); 747 751 } 748 752 749 753 void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
+2 -2
drivers/acpi/Kconfig
··· 385 385 to override that restriction). 386 386 387 387 config ACPI_BGRT 388 - tristate "Boottime Graphics Resource Table support" 389 - default n 388 + bool "Boottime Graphics Resource Table support" 389 + depends on EFI 390 390 help 391 391 This driver adds support for exposing the ACPI Boottime Graphics 392 392 Resource Table, which allows the operating system to obtain
+9 -67
drivers/acpi/bgrt.c
··· 1 1 /* 2 2 * Copyright 2012 Red Hat, Inc <mjg@redhat.com> 3 + * Copyright 2012 Intel Corporation 3 4 * 4 5 * This program is free software; you can redistribute it and/or modify 5 6 * it under the terms of the GNU General Public License version 2 as ··· 12 11 #include <linux/init.h> 13 12 #include <linux/device.h> 14 13 #include <linux/sysfs.h> 15 - #include <linux/io.h> 16 - #include <acpi/acpi.h> 17 - #include <acpi/acpi_bus.h> 14 + #include <linux/efi-bgrt.h> 18 15 19 - static struct acpi_table_bgrt *bgrt_tab; 20 16 static struct kobject *bgrt_kobj; 21 - 22 - struct bmp_header { 23 - u16 id; 24 - u32 size; 25 - } __attribute ((packed)); 26 - 27 - static struct bmp_header bmp_header; 28 17 29 18 static ssize_t show_version(struct device *dev, 30 19 struct device_attribute *attr, char *buf) ··· 54 63 static ssize_t show_image(struct file *file, struct kobject *kobj, 55 64 struct bin_attribute *attr, char *buf, loff_t off, size_t count) 56 65 { 57 - int size = attr->size; 58 - void __iomem *image = attr->private; 59 - 60 - if (off >= size) { 61 - count = 0; 62 - } else { 63 - if (off + count > size) 64 - count = size - off; 65 - 66 - memcpy_fromio(buf, image+off, count); 67 - } 68 - 66 + memcpy(buf, attr->private + off, count); 69 67 return count; 70 68 } 71 69 ··· 81 101 82 102 static int __init bgrt_init(void) 83 103 { 84 - acpi_status status; 85 104 int ret; 86 - void __iomem *bgrt; 87 105 88 - if (acpi_disabled) 89 - return -ENODEV; 90 - 91 - status = acpi_get_table("BGRT", 0, 92 - (struct acpi_table_header **)&bgrt_tab); 93 - 94 - if (ACPI_FAILURE(status)) 106 + if (!bgrt_image) 95 107 return -ENODEV; 96 108 97 109 sysfs_bin_attr_init(&image_attr); 98 - 99 - bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header)); 100 - 101 - if (!bgrt) { 102 - ret = -EINVAL; 103 - goto out_err; 104 - } 105 - 106 - memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header)); 107 - image_attr.size = bmp_header.size; 108 - iounmap(bgrt); 109 - 110 - image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size); 111 - 112 - if (!image_attr.private) { 113 - ret = -EINVAL; 114 - goto out_err; 115 - } 116 - 110 + image_attr.private = bgrt_image; 111 + image_attr.size = bgrt_image_size; 117 112 118 113 bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj); 119 - if (!bgrt_kobj) { 120 - ret = -EINVAL; 121 - goto out_iounmap; 122 - } 114 + if (!bgrt_kobj) 115 + return -EINVAL; 123 116 124 117 ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group); 125 118 if (ret) ··· 108 155 sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group); 109 156 out_kobject: 110 157 kobject_put(bgrt_kobj); 111 - out_iounmap: 112 - iounmap(image_attr.private); 113 - out_err: 114 158 return ret; 115 159 } 116 160 117 - static void __exit bgrt_exit(void) 118 - { 119 - iounmap(image_attr.private); 120 - sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group); 121 - sysfs_remove_bin_file(bgrt_kobj, &image_attr); 122 - } 123 - 124 161 module_init(bgrt_init); 125 - module_exit(bgrt_exit); 126 162 127 - MODULE_AUTHOR("Matthew Garrett"); 163 + MODULE_AUTHOR("Matthew Garrett, Josh Triplett <josh@joshtriplett.org>"); 128 164 MODULE_DESCRIPTION("BGRT boot graphic support"); 129 165 MODULE_LICENSE("GPL");
+21
include/linux/efi-bgrt.h
··· 1 + #ifndef _LINUX_EFI_BGRT_H 2 + #define _LINUX_EFI_BGRT_H 3 + 4 + #ifdef CONFIG_ACPI_BGRT 5 + 6 + #include <linux/acpi.h> 7 + 8 + void efi_bgrt_init(void); 9 + 10 + /* The BGRT data itself; only valid if bgrt_image != NULL. */ 11 + extern void *bgrt_image; 12 + extern size_t bgrt_image_size; 13 + extern struct acpi_table_bgrt *bgrt_tab; 14 + 15 + #else /* !CONFIG_ACPI_BGRT */ 16 + 17 + static inline void efi_bgrt_init(void) {} 18 + 19 + #endif /* !CONFIG_ACPI_BGRT */ 20 + 21 + #endif /* _LINUX_EFI_BGRT_H */
+2
include/linux/efi.h
··· 497 497 extern void efi_gettimeofday (struct timespec *ts); 498 498 extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ 499 499 #ifdef CONFIG_X86 500 + extern void efi_late_init(void); 500 501 extern void efi_free_boot_services(void); 501 502 #else 503 + static inline void efi_late_init(void) {} 502 504 static inline void efi_free_boot_services(void) {} 503 505 #endif 504 506 extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
+3 -1
init/main.c
··· 631 631 acpi_early_init(); /* before LAPIC and SMP init */ 632 632 sfi_init_late(); 633 633 634 - if (efi_enabled) 634 + if (efi_enabled) { 635 + efi_late_init(); 635 636 efi_free_boot_services(); 637 + } 636 638 637 639 ftrace_init(); 638 640