Commit 3b29b03a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86/EFI changes from Ingo Molnar:
 "EFI loader robustness enhancements plus smaller fixes"

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  efi: Fix the ACPI BGRT driver for images located in EFI boot services memory
  efi: Add a function to look up existing IO memory mappings
  efi: Defer freeing boot services memory until after ACPI init
  x86, EFI: Calculate the EFI framebuffer size instead of trusting the firmware
  efifb: Skip DMI checks if the bootloader knows what it's doing
  efi: initialize efi.runtime_version to make query_variable_info/update_capsule workable
  efi: Build EFI stub with EFI-appropriate options
  X86: Improve GOP detection in the EFI boot stub
parents 58ae9c0d 2223af38
...@@ -28,6 +28,9 @@ VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \ ...@@ -28,6 +28,9 @@ VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
$(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \ $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
$(obj)/piggy.o $(obj)/piggy.o
$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
$(obj)/efi_stub_$(BITS).o: KBUILD_CLFAGS += -fshort-wchar -mno-red-zone
ifeq ($(CONFIG_EFI_STUB), y) ifeq ($(CONFIG_EFI_STUB), y)
VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o
endif endif
......
...@@ -276,8 +276,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -276,8 +276,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
nr_gops = size / sizeof(void *); nr_gops = size / sizeof(void *);
for (i = 0; i < nr_gops; i++) { for (i = 0; i < nr_gops; i++) {
struct efi_graphics_output_mode_info *info; struct efi_graphics_output_mode_info *info;
efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID; efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
void *pciio; bool conout_found = false;
void *dummy;
void *h = gop_handle[i]; void *h = gop_handle[i];
status = efi_call_phys3(sys_table->boottime->handle_protocol, status = efi_call_phys3(sys_table->boottime->handle_protocol,
...@@ -285,19 +286,21 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -285,19 +286,21 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
continue; continue;
efi_call_phys3(sys_table->boottime->handle_protocol, status = efi_call_phys3(sys_table->boottime->handle_protocol,
h, &pciio_proto, &pciio); h, &conout_proto, &dummy);
if (status == EFI_SUCCESS)
conout_found = true;
status = efi_call_phys4(gop->query_mode, gop, status = efi_call_phys4(gop->query_mode, gop,
gop->mode->mode, &size, &info); gop->mode->mode, &size, &info);
if (status == EFI_SUCCESS && (!first_gop || pciio)) { if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
/* /*
* Apple provide GOPs that are not backed by * Systems that use the UEFI Console Splitter may
* real hardware (they're used to handle * provide multiple GOP devices, not all of which are
* multiple displays). The workaround is to * backed by real hardware. The workaround is to search
* search for a GOP implementing the PCIIO * for a GOP implementing the ConOut protocol, and if
* protocol, and if one isn't found, to just * one isn't found, to just fall back to the first GOP.
* fallback to the first GOP.
*/ */
width = info->horizontal_resolution; width = info->horizontal_resolution;
height = info->vertical_resolution; height = info->vertical_resolution;
...@@ -308,10 +311,10 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -308,10 +311,10 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
pixels_per_scan_line = info->pixels_per_scan_line; pixels_per_scan_line = info->pixels_per_scan_line;
/* /*
* Once we've found a GOP supporting PCIIO, * Once we've found a GOP supporting ConOut,
* don't bother looking any further. * don't bother looking any further.
*/ */
if (pciio) if (conout_found)
break; break;
first_gop = gop; first_gop = gop;
...@@ -328,7 +331,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -328,7 +331,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
si->lfb_width = width; si->lfb_width = width;
si->lfb_height = height; si->lfb_height = height;
si->lfb_base = fb_base; si->lfb_base = fb_base;
si->lfb_size = fb_size;
si->pages = 1; si->pages = 1;
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
...@@ -376,6 +378,10 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -376,6 +378,10 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
si->rsvd_pos = 0; si->rsvd_pos = 0;
} }
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
free_handle: free_handle:
efi_call_phys1(sys_table->boottime->free_pool, gop_handle); efi_call_phys1(sys_table->boottime->free_pool, gop_handle);
return status; return status;
......
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) #define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT)
#define EFI_READ_CHUNK_SIZE (1024 * 1024) #define EFI_READ_CHUNK_SIZE (1024 * 1024)
#define EFI_CONSOLE_OUT_DEVICE_GUID \
EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
0x3f, 0xc1, 0x4d)
#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0
#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 #define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1
#define PIXEL_BIT_MASK 2 #define PIXEL_BIT_MASK 2
......
obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o
obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
/*
* Copyright 2012 Intel Corporation
* Author: Josh Triplett <josh@joshtriplett.org>
*
* Based on the bgrt driver:
* Copyright 2012 Red Hat, Inc <mjg@redhat.com>
* Author: Matthew Garrett
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/efi.h>
#include <linux/efi-bgrt.h>
struct acpi_table_bgrt *bgrt_tab;
void *bgrt_image;
size_t bgrt_image_size;
struct bmp_header {
u16 id;
u32 size;
} __packed;
void efi_bgrt_init(void)
{
acpi_status status;
void __iomem *image;
bool ioremapped = false;
struct bmp_header bmp_header;
if (acpi_disabled)
return;
status = acpi_get_table("BGRT", 0,
(struct acpi_table_header **)&bgrt_tab);
if (ACPI_FAILURE(status))
return;
if (bgrt_tab->version != 1)
return;
if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address)
return;
image = efi_lookup_mapped_addr(bgrt_tab->image_address);
if (!image) {
image = ioremap(bgrt_tab->image_address, sizeof(bmp_header));
ioremapped = true;
if (!image)
return;
}
memcpy_fromio(&bmp_header, image, sizeof(bmp_header));
if (ioremapped)
iounmap(image);
bgrt_image_size = bmp_header.size;
bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL);
if (!bgrt_image)
return;
if (ioremapped) {
image = ioremap(bgrt_tab->image_address, bmp_header.size);
if (!image) {
kfree(bgrt_image);
bgrt_image = NULL;
return;
}
}
memcpy_fromio(bgrt_image, image, bgrt_image_size);
if (ioremapped)
iounmap(image);
}
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/efi-bgrt.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/memblock.h> #include <linux/memblock.h>
...@@ -419,10 +420,21 @@ void __init efi_reserve_boot_services(void) ...@@ -419,10 +420,21 @@ void __init efi_reserve_boot_services(void)
} }
} }
static void __init efi_free_boot_services(void) static void __init efi_unmap_memmap(void)
{
if (memmap.map) {
early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
memmap.map = NULL;
}
}
void __init efi_free_boot_services(void)
{ {
void *p; void *p;
if (!efi_native)
return;
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
efi_memory_desc_t *md = p; efi_memory_desc_t *md = p;
unsigned long long start = md->phys_addr; unsigned long long start = md->phys_addr;
...@@ -438,6 +450,8 @@ static void __init efi_free_boot_services(void) ...@@ -438,6 +450,8 @@ static void __init efi_free_boot_services(void)
free_bootmem_late(start, size); free_bootmem_late(start, size);
} }
efi_unmap_memmap();
} }
static int __init efi_systab_init(void *phys) static int __init efi_systab_init(void *phys)
...@@ -732,6 +746,11 @@ void __init efi_init(void) ...@@ -732,6 +746,11 @@ void __init efi_init(void)
#endif #endif
} }
void __init efi_late_init(void)
{
efi_bgrt_init();
}
void __init efi_set_executable(efi_memory_desc_t *md, bool executable) void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
{ {
u64 addr, npages; u64 addr, npages;
...@@ -763,6 +782,34 @@ static void __init runtime_code_page_mkexec(void) ...@@ -763,6 +782,34 @@ static void __init runtime_code_page_mkexec(void)
} }
} }
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
* it as RAM. So, look it up in the existing EFI memory map instead. Only
* callable after efi_enter_virtual_mode and before efi_free_boot_services.
*/
void __iomem *efi_lookup_mapped_addr(u64 phys_addr)
{
void *p;
if (WARN_ON(!memmap.map))
return NULL;
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
efi_memory_desc_t *md = p;
u64 size = md->num_pages << EFI_PAGE_SHIFT;
u64 end = md->phys_addr + size;
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
md->type != EFI_BOOT_SERVICES_CODE &&
md->type != EFI_BOOT_SERVICES_DATA)
continue;
if (!md->virt_addr)
continue;
if (phys_addr >= md->phys_addr && phys_addr < end) {
phys_addr += md->virt_addr - md->phys_addr;
return (__force void __iomem *)(unsigned long)phys_addr;
}
}
return NULL;
}
/* /*
* This function will switch the EFI runtime services to virtual mode. * This function will switch the EFI runtime services to virtual mode.
* Essentially, look through the EFI memmap and map every region that * Essentially, look through the EFI memmap and map every region that
...@@ -787,8 +834,10 @@ void __init efi_enter_virtual_mode(void) ...@@ -787,8 +834,10 @@ void __init efi_enter_virtual_mode(void)
* non-native EFI * non-native EFI
*/ */
if (!efi_native) if (!efi_native) {
goto out; efi_unmap_memmap();
return;
}
/* Merge contiguous regions of the same type and attribute */ /* Merge contiguous regions of the same type and attribute */
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
...@@ -877,19 +926,13 @@ void __init efi_enter_virtual_mode(void) ...@@ -877,19 +926,13 @@ void __init efi_enter_virtual_mode(void)
panic("EFI call to SetVirtualAddressMap() failed!"); panic("EFI call to SetVirtualAddressMap() failed!");
} }
/*
* Thankfully, it does seem that no runtime services other than
* SetVirtualAddressMap() will touch boot services code, so we can
* get rid of it all at this point
*/
efi_free_boot_services();
/* /*
* Now that EFI is in virtual mode, update the function * Now that EFI is in virtual mode, update the function
* pointers in the runtime service table to the new virtual addresses. * pointers in the runtime service table to the new virtual addresses.
* *
* Call EFI services through wrapper functions. * Call EFI services through wrapper functions.
*/ */
efi.runtime_version = efi_systab.fw_revision;
efi.get_time = virt_efi_get_time; efi.get_time = virt_efi_get_time;
efi.set_time = virt_efi_set_time; efi.set_time = virt_efi_set_time;
efi.get_wakeup_time = virt_efi_get_wakeup_time; efi.get_wakeup_time = virt_efi_get_wakeup_time;
...@@ -906,9 +949,6 @@ void __init efi_enter_virtual_mode(void) ...@@ -906,9 +949,6 @@ void __init efi_enter_virtual_mode(void)
if (__supported_pte_mask & _PAGE_NX) if (__supported_pte_mask & _PAGE_NX)
runtime_code_page_mkexec(); runtime_code_page_mkexec();
out:
early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
memmap.map = NULL;
kfree(new_memmap); kfree(new_memmap);
} }
......
...@@ -385,8 +385,8 @@ config ACPI_CUSTOM_METHOD ...@@ -385,8 +385,8 @@ config ACPI_CUSTOM_METHOD
to override that restriction). to override that restriction).
config ACPI_BGRT config ACPI_BGRT
tristate "Boottime Graphics Resource Table support" bool "Boottime Graphics Resource Table support"
default n depends on EFI
help help
This driver adds support for exposing the ACPI Boottime Graphics This driver adds support for exposing the ACPI Boottime Graphics
Resource Table, which allows the operating system to obtain Resource Table, which allows the operating system to obtain
......
/* /*
* Copyright 2012 Red Hat, Inc <mjg@redhat.com> * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
* Copyright 2012 Intel Corporation
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -11,20 +12,10 @@ ...@@ -11,20 +12,10 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/io.h> #include <linux/efi-bgrt.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
static struct acpi_table_bgrt *bgrt_tab;
static struct kobject *bgrt_kobj; static struct kobject *bgrt_kobj;
struct bmp_header {
u16 id;
u32 size;
} __attribute ((packed));
static struct bmp_header bmp_header;
static ssize_t show_version(struct device *dev, static ssize_t show_version(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
...@@ -63,18 +54,7 @@ static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL); ...@@ -63,18 +54,7 @@ static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
static ssize_t show_image(struct file *file, struct kobject *kobj, static ssize_t show_image(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, size_t count) struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{ {
int size = attr->size; memcpy(buf, attr->private + off, count);
void __iomem *image = attr->private;
if (off >= size) {
count = 0;
} else {
if (off + count > size)
count = size - off;
memcpy_fromio(buf, image+off, count);
}
return count; return count;
} }
...@@ -101,45 +81,18 @@ static struct attribute_group bgrt_attribute_group = { ...@@ -101,45 +81,18 @@ static struct attribute_group bgrt_attribute_group = {
static int __init bgrt_init(void) static int __init bgrt_init(void)
{ {
acpi_status status;
int ret; int ret;
void __iomem *bgrt;
if (acpi_disabled) if (!bgrt_image)
return -ENODEV;
status = acpi_get_table("BGRT", 0,
(struct acpi_table_header **)&bgrt_tab);
if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
sysfs_bin_attr_init(&image_attr); sysfs_bin_attr_init(&image_attr);
image_attr.private = bgrt_image;
bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header)); image_attr.size = bgrt_image_size;
if (!bgrt) {
ret = -EINVAL;
goto out_err;
}
memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header));
image_attr.size = bmp_header.size;
iounmap(bgrt);
image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size);
if (!image_attr.private) {
ret = -EINVAL;
goto out_err;
}
bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj); bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
if (!bgrt_kobj) { if (!bgrt_kobj)
ret = -EINVAL; return -EINVAL;
goto out_iounmap;
}
ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group); ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
if (ret) if (ret)
...@@ -155,22 +108,11 @@ static int __init bgrt_init(void) ...@@ -155,22 +108,11 @@ static int __init bgrt_init(void)
sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group); sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
out_kobject: out_kobject:
kobject_put(bgrt_kobj); kobject_put(bgrt_kobj);
out_iounmap:
iounmap(image_attr.private);
out_err:
return ret; return ret;
} }
static void __exit bgrt_exit(void)
{
iounmap(image_attr.private);
sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
sysfs_remove_bin_file(bgrt_kobj, &image_attr);
}
module_init(bgrt_init); module_init(bgrt_init);
module_exit(bgrt_exit);
MODULE_AUTHOR("Matthew Garrett"); MODULE_AUTHOR("Matthew Garrett, Josh Triplett <josh@joshtriplett.org>");
MODULE_DESCRIPTION("BGRT boot graphic support"); MODULE_DESCRIPTION("BGRT boot graphic support");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -553,7 +553,9 @@ static int __init efifb_init(void) ...@@ -553,7 +553,9 @@ static int __init efifb_init(void)
int ret; int ret;
char *option = NULL; char *option = NULL;
dmi_check_system(dmi_system_table); if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
!(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
dmi_check_system(dmi_system_table);
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return -ENODEV; return -ENODEV;
......
#ifndef _LINUX_EFI_BGRT_H
#define _LINUX_EFI_BGRT_H
#ifdef CONFIG_ACPI_BGRT
#include <linux/acpi.h>
void efi_bgrt_init(void);
/* The BGRT data itself; only valid if bgrt_image != NULL. */
extern void *bgrt_image;
extern size_t bgrt_image_size;
extern struct acpi_table_bgrt *bgrt_tab;
#else /* !CONFIG_ACPI_BGRT */
static inline void efi_bgrt_init(void) {}
#endif /* !CONFIG_ACPI_BGRT */
#endif /* _LINUX_EFI_BGRT_H */
...@@ -496,6 +496,14 @@ extern void efi_map_pal_code (void); ...@@ -496,6 +496,14 @@ extern void efi_map_pal_code (void);
extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg); extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg);
extern void efi_gettimeofday (struct timespec *ts); extern void efi_gettimeofday (struct timespec *ts);
extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */
#ifdef CONFIG_X86
extern void efi_late_init(void);
extern void efi_free_boot_services(void);
#else
static inline void efi_late_init(void) {}
static inline void efi_free_boot_services(void) {}
#endif
extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
extern u64 efi_get_iobase (void); extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr); extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr);
......
...@@ -68,6 +68,8 @@ struct screen_info { ...@@ -68,6 +68,8 @@ struct screen_info {
#define VIDEO_FLAGS_NOCURSOR (1 << 0) /* The video mode has no cursor set */ #define VIDEO_FLAGS_NOCURSOR (1 << 0) /* The video mode has no cursor set */
#define VIDEO_CAPABILITY_SKIP_QUIRKS (1 << 0)
#ifdef __KERNEL__ #ifdef __KERNEL__
extern struct screen_info screen_info; extern struct screen_info screen_info;
......
...@@ -631,6 +631,11 @@ asmlinkage void __init start_kernel(void) ...@@ -631,6 +631,11 @@ asmlinkage void __init start_kernel(void)
acpi_early_init(); /* before LAPIC and SMP init */ acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late(); sfi_init_late();
if (efi_enabled) {
efi_late_init();
efi_free_boot_services();
}
ftrace_init(); ftrace_init();
/* Do the rest non-__init'ed, we're now alive */ /* Do the rest non-__init'ed, we're now alive */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment