Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2223af38 authored by Josh Triplett's avatar Josh Triplett Committed by H. Peter Anvin
Browse files

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: default avatarJosh Triplett <josh@joshtriplett.org>
Link: http://lkml.kernel.org/r/93ce9f823f1c1f3bb88bdd662cce08eee7a17f5d.1348876882.git.josh@joshtriplett.org


Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
parent 7bc90e01
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
obj-$(CONFIG_EFI) 		+= efi.o efi_$(BITS).o efi_stub_$(BITS).o
obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
+76 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+6 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/efi.h>
#include <linux/efi-bgrt.h>
#include <linux/export.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
@@ -745,6 +746,11 @@ void __init efi_init(void)
#endif
}

void __init efi_late_init(void)
{
	efi_bgrt_init();
}

void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
{
	u64 addr, npages;
+2 −2
Original line number Diff line number Diff line
@@ -385,8 +385,8 @@ config ACPI_CUSTOM_METHOD
	  to override that restriction).

config ACPI_BGRT
        tristate "Boottime Graphics Resource Table support"
        default n
	bool "Boottime Graphics Resource Table support"
	depends on EFI
        help
	  This driver adds support for exposing the ACPI Boottime Graphics
	  Resource Table, which allows the operating system to obtain
+9 −67
Original line number Diff line number Diff line
/*
 * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
 * Copyright 2012 Intel Corporation
 *
 * 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
@@ -11,20 +12,10 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/io.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <linux/efi-bgrt.h>

static struct acpi_table_bgrt *bgrt_tab;
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,
			    struct device_attribute *attr, char *buf)
{
@@ -63,18 +54,7 @@ static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
static ssize_t show_image(struct file *file, struct kobject *kobj,
	       struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
	int size = attr->size;
	void __iomem *image = attr->private;

	if (off >= size) {
		count = 0;
	} else {
		if (off + count > size)
			count = size - off;

		memcpy_fromio(buf, image+off, count);
	}

	memcpy(buf, attr->private + off, count);
	return count;
}

@@ -101,45 +81,18 @@ static struct attribute_group bgrt_attribute_group = {

static int __init bgrt_init(void)
{
	acpi_status status;
	int ret;
	void __iomem *bgrt;

	if (acpi_disabled)
		return -ENODEV;

	status = acpi_get_table("BGRT", 0,
				(struct acpi_table_header **)&bgrt_tab);

	if (ACPI_FAILURE(status))
	if (!bgrt_image)
		return -ENODEV;

	sysfs_bin_attr_init(&image_attr);

	bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header));

	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;
	}

	image_attr.private = bgrt_image;
	image_attr.size = bgrt_image_size;

	bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
	if (!bgrt_kobj) {
		ret = -EINVAL;
		goto out_iounmap;
	}
	if (!bgrt_kobj)
		return -EINVAL;

	ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
	if (ret)
@@ -155,22 +108,11 @@ out_group:
	sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
out_kobject:
	kobject_put(bgrt_kobj);
out_iounmap:
	iounmap(image_attr.private);
out_err:
	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_exit(bgrt_exit);

MODULE_AUTHOR("Matthew Garrett");
MODULE_AUTHOR("Matthew Garrett, Josh Triplett <josh@joshtriplett.org>");
MODULE_DESCRIPTION("BGRT boot graphic support");
MODULE_LICENSE("GPL");
Loading