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:
@@ -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
|
||||
|
191
arch/x86/platform/efi/early_printk.c
Normal file
191
arch/x86/platform/efi/early_printk.c
Normal 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,
|
||||
};
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user