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:
 "Main changes:

   - Add support for earlyprintk=efi which uses the EFI framebuffer.
     Very useful for debugging boot problems.

   - EFI stub support for large memory maps (more than 128 entries)

   - EFI ARM support - this was mostly done by generalizing x86 <-> ARM
     platform differences, such as by moving x86 EFI code into
     drivers/firmware/efi/ and sharing it with ARM.

   - Documentation updates

   - misc fixes"

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (26 commits)
  x86/efi: Add EFI framebuffer earlyprintk support
  boot, efi: Remove redundant memset()
  x86/efi: Fix config_table_type array termination
  x86 efi: bugfix interrupt disabling sequence
  x86: EFI stub support for large memory maps
  efi: resolve warnings found on ARM compile
  efi: Fix types in EFI calls to match EFI function definitions.
  efi: Renames in handle_cmdline_files() to complete generalization.
  efi: Generalize handle_ramdisks() and rename to handle_cmdline_files().
  efi: Allow efi_free() to be called with size of 0
  efi: use efi_get_memory_map() to get final map for x86
  efi: generalize efi_get_memory_map()
  efi: Rename __get_map() to efi_get_memory_map()
  efi: Move unicode to ASCII conversion to shared function.
  efi: Generalize relocate_kernel() for use by other architectures.
  efi: Move relocate_kernel() to shared file.
  efi: Enforce minimum alignment of 1 page on allocations.
  efi: Rename memory allocation/free functions
  efi: Add system table pointer argument to shared functions.
  efi: Move common EFI stub code from x86 arch code to common location
  ...
This commit is contained in:
Linus Torvalds
2013-11-12 10:48:30 +09:00
17 changed files with 1259 additions and 805 deletions

View File

@@ -1,2 +1,3 @@
obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o
obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o

View File

@@ -0,0 +1,191 @@
/*
* Copyright (C) 2013 Intel Corporation; author Matt Fleming
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*/
#include <linux/console.h>
#include <linux/efi.h>
#include <linux/font.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <asm/setup.h>
static const struct font_desc *font;
static u32 efi_x, efi_y;
static __init void early_efi_clear_scanline(unsigned int y)
{
unsigned long base, *dst;
u16 len;
base = boot_params.screen_info.lfb_base;
len = boot_params.screen_info.lfb_linelength;
dst = early_ioremap(base + y*len, len);
if (!dst)
return;
memset(dst, 0, len);
early_iounmap(dst, len);
}
static __init void early_efi_scroll_up(void)
{
unsigned long base, *dst, *src;
u16 len;
u32 i, height;
base = boot_params.screen_info.lfb_base;
len = boot_params.screen_info.lfb_linelength;
height = boot_params.screen_info.lfb_height;
for (i = 0; i < height - font->height; i++) {
dst = early_ioremap(base + i*len, len);
if (!dst)
return;
src = early_ioremap(base + (i + font->height) * len, len);
if (!src) {
early_iounmap(dst, len);
return;
}
memmove(dst, src, len);
early_iounmap(src, len);
early_iounmap(dst, len);
}
}
static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h)
{
const u32 color_black = 0x00000000;
const u32 color_white = 0x00ffffff;
const u8 *src;
u8 s8;
int m;
src = font->data + c * font->height;
s8 = *(src + h);
for (m = 0; m < 8; m++) {
if ((s8 >> (7 - m)) & 1)
*dst = color_white;
else
*dst = color_black;
dst++;
}
}
static __init void
early_efi_write(struct console *con, const char *str, unsigned int num)
{
struct screen_info *si;
unsigned long base;
unsigned int len;
const char *s;
void *dst;
base = boot_params.screen_info.lfb_base;
si = &boot_params.screen_info;
len = si->lfb_linelength;
while (num) {
unsigned int linemax;
unsigned int h, count = 0;
for (s = str; *s && *s != '\n'; s++) {
if (count == num)
break;
count++;
}
linemax = (si->lfb_width - efi_x) / font->width;
if (count > linemax)
count = linemax;
for (h = 0; h < font->height; h++) {
unsigned int n, x;
dst = early_ioremap(base + (efi_y + h) * len, len);
if (!dst)
return;
s = str;
n = count;
x = efi_x;
while (n-- > 0) {
early_efi_write_char(dst + x*4, *s, h);
x += font->width;
s++;
}
early_iounmap(dst, len);
}
num -= count;
efi_x += count * font->width;
str += count;
if (num > 0 && *s == '\n') {
efi_x = 0;
efi_y += font->height;
str++;
num--;
}
if (efi_x >= si->lfb_width) {
efi_x = 0;
efi_y += font->height;
}
if (efi_y + font->height >= si->lfb_height) {
u32 i;
efi_y -= font->height;
early_efi_scroll_up();
for (i = 0; i < font->height; i++)
early_efi_clear_scanline(efi_y + i);
}
}
}
static __init int early_efi_setup(struct console *con, char *options)
{
struct screen_info *si;
u16 xres, yres;
u32 i;
si = &boot_params.screen_info;
xres = si->lfb_width;
yres = si->lfb_height;
/*
* early_efi_write_char() implicitly assumes a framebuffer with
* 32-bits per pixel.
*/
if (si->lfb_depth != 32)
return -ENODEV;
font = get_default_font(xres, yres, -1, -1);
if (!font)
return -ENODEV;
efi_y = rounddown(yres, font->height) - font->height;
for (i = 0; i < (yres - efi_y) / font->height; i++)
early_efi_scroll_up();
return 0;
}
struct console early_efi_console = {
.name = "earlyefi",
.write = early_efi_write,
.setup = early_efi_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};

View File

@@ -60,19 +60,6 @@
static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 };
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,
.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,
};
EXPORT_SYMBOL(efi);
struct efi_memory_map memmap;
static struct efi efi_phys __initdata;
@@ -80,6 +67,13 @@ static efi_system_table_t efi_systab __initdata;
unsigned long x86_efi_facility;
static __initdata efi_config_table_type_t arch_tables[] = {
#ifdef CONFIG_X86_UV
{UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab},
#endif
{NULL_GUID, NULL, NULL},
};
/*
* Returns 1 if 'facility' is enabled, 0 otherwise.
*/
@@ -399,6 +393,8 @@ int __init efi_memblock_x86_reserve_range(void)
memblock_reserve(pmap, memmap.nr_map * memmap.desc_size);
efi.memmap = &memmap;
return 0;
}
@@ -578,80 +574,6 @@ static int __init efi_systab_init(void *phys)
return 0;
}
static int __init efi_config_init(u64 tables, int nr_tables)
{
void *config_tables, *tablep;
int i, sz;
if (efi_enabled(EFI_64BIT))
sz = sizeof(efi_config_table_64_t);
else
sz = sizeof(efi_config_table_32_t);
/*
* Let's see what config tables the firmware passed to us.
*/
config_tables = early_ioremap(tables, nr_tables * sz);
if (config_tables == NULL) {
pr_err("Could not map Configuration table!\n");
return -ENOMEM;
}
tablep = config_tables;
pr_info("");
for (i = 0; i < efi.systab->nr_tables; i++) {
efi_guid_t guid;
unsigned long table;
if (efi_enabled(EFI_64BIT)) {
u64 table64;
guid = ((efi_config_table_64_t *)tablep)->guid;
table64 = ((efi_config_table_64_t *)tablep)->table;
table = table64;
#ifdef CONFIG_X86_32
if (table64 >> 32) {
pr_cont("\n");
pr_err("Table located above 4GB, disabling EFI.\n");
early_iounmap(config_tables,
efi.systab->nr_tables * sz);
return -EINVAL;
}
#endif
} else {
guid = ((efi_config_table_32_t *)tablep)->guid;
table = ((efi_config_table_32_t *)tablep)->table;
}
if (!efi_guidcmp(guid, MPS_TABLE_GUID)) {
efi.mps = table;
pr_cont(" MPS=0x%lx ", table);
} else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) {
efi.acpi20 = table;
pr_cont(" ACPI 2.0=0x%lx ", table);
} else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) {
efi.acpi = table;
pr_cont(" ACPI=0x%lx ", table);
} else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) {
efi.smbios = table;
pr_cont(" SMBIOS=0x%lx ", table);
#ifdef CONFIG_X86_UV
} else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) {
efi.uv_systab = table;
pr_cont(" UVsystab=0x%lx ", table);
#endif
} else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) {
efi.hcdp = table;
pr_cont(" HCDP=0x%lx ", table);
} else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) {
efi.uga = table;
pr_cont(" UGA=0x%lx ", table);
}
tablep += sz;
}
pr_cont("\n");
early_iounmap(config_tables, efi.systab->nr_tables * sz);
return 0;
}
static int __init efi_runtime_init(void)
{
efi_runtime_services_t *runtime;
@@ -745,7 +667,7 @@ void __init efi_init(void)
efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff, vendor);
if (efi_config_init(efi.systab->tables, efi.systab->nr_tables))
if (efi_config_init(arch_tables))
return;
set_bit(EFI_CONFIG_TABLES, &x86_efi_facility);
@@ -816,34 +738,6 @@ 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;
}
void efi_memory_uc(u64 addr, unsigned long size)
{
unsigned long page_shift = 1UL << EFI_PAGE_SHIFT;