Commit 32e2d7c8 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull EFI updates from Ingo Molnar:
 "The main changes in this cycle were:

   - Changes to the EFI init code to establish whether secure boot
     authentication was performed at boot time. (Josh Boyer, David
     Howells)

   - Wire up the UEFI memory attributes table for x86. This eliminates
     any runtime memory regions that are both writable and executable,
     on recent firmware versions. (Sai Praneeth)

   - Move the BGRT init code to an earlier stage so that we can still
     use efi_mem_reserve(). (Dave Young)

   - Preserve debug symbols in the ARM/arm64 UEFI stub (Ard Biesheuvel)

   - Code deduplication work and various other cleanups (Lukas Wunner)

   - ... plus various other fixes and cleanups"

* 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  efi/libstub: Make file I/O chunking x86-specific
  efi: Print the secure boot status in x86 setup_arch()
  efi: Disable secure boot if shim is in insecure mode
  efi: Get and store the secure boot status
  efi: Add SHIM and image security database GUID definitions
  arm/efi: Allow invocation of arbitrary runtime services
  x86/efi: Allow invocation of arbitrary runtime services
  efi/libstub: Preserve .debug sections after absolute relocation check
  efi/x86: Add debug code to print cooked memmap
  efi/x86: Move the EFI BGRT init code to early init code
  efi: Use typed function pointers for the runtime services table
  efi/esrt: Fix typo in pr_err() message
  x86/efi: Add support for EFI_MEMORY_ATTRIBUTES_TABLE
  efi: Introduce the EFI_MEM_ATTR bit and set it from the memory attributes table
  efi: Make EFI_MEMORY_ATTRIBUTES_TABLE initialization common across all architectures
  x86/efi: Deduplicate efi_char16_printk()
  efi: Deduplicate efi_file_size() / _read() / _close()
parents f7458a5d b3879a4d
......@@ -31,6 +31,8 @@ Offset Proto Name Meaning
1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below)
1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer
(below)
1EB/001 ALL kbd_status Numlock is enabled
1EC/001 ALL secure_boot Secure boot is enabled in the firmware
1EF/001 ALL sentinel Used to detect broken bootloaders
290/040 ALL edd_mbr_sig_buffer EDD MBR signatures
2D0/A00 ALL e820_map E820 memory map table
......
......@@ -55,6 +55,7 @@ void efi_virtmap_unload(void);
#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
#define __efi_call_early(f, ...) f(__VA_ARGS__)
#define efi_call_runtime(f, ...) sys_table_arg->runtime->f(__VA_ARGS__)
#define efi_is_64bit() (false)
#define efi_call_proto(protocol, f, instance, ...) \
......
......@@ -50,6 +50,7 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
#define __efi_call_early(f, ...) f(__VA_ARGS__)
#define efi_call_runtime(f, ...) sys_table_arg->runtime->f(__VA_ARGS__)
#define efi_is_64bit() (true)
#define efi_call_proto(protocol, f, instance, ...) \
......
......@@ -32,160 +32,13 @@ static void setup_boot_services##bits(struct efi_config *c) \
\
table = (typeof(table))sys_table; \
\
c->runtime_services = table->runtime; \
c->boot_services = table->boottime; \
c->text_output = table->con_out; \
}
BOOT_SERVICES(32);
BOOT_SERVICES(64);
void efi_char16_printk(efi_system_table_t *, efi_char16_t *);
static efi_status_t
__file_size32(void *__fh, efi_char16_t *filename_16,
void **handle, u64 *file_sz)
{
efi_file_handle_32_t *h, *fh = __fh;
efi_file_info_t *info;
efi_status_t status;
efi_guid_t info_guid = EFI_FILE_INFO_ID;
u32 info_sz;
status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16,
EFI_FILE_MODE_READ, (u64)0);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to open file: ");
efi_char16_printk(sys_table, filename_16);
efi_printk(sys_table, "\n");
return status;
}
*handle = h;
info_sz = 0;
status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
&info_sz, NULL);
if (status != EFI_BUFFER_TOO_SMALL) {
efi_printk(sys_table, "Failed to get file info size\n");
return status;
}
grow:
status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
info_sz, (void **)&info);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to alloc mem for file info\n");
return status;
}
status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
&info_sz, info);
if (status == EFI_BUFFER_TOO_SMALL) {
efi_call_early(free_pool, info);
goto grow;
}
*file_sz = info->file_size;
efi_call_early(free_pool, info);
if (status != EFI_SUCCESS)
efi_printk(sys_table, "Failed to get initrd info\n");
return status;
}
static efi_status_t
__file_size64(void *__fh, efi_char16_t *filename_16,
void **handle, u64 *file_sz)
{
efi_file_handle_64_t *h, *fh = __fh;
efi_file_info_t *info;
efi_status_t status;
efi_guid_t info_guid = EFI_FILE_INFO_ID;
u64 info_sz;
status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16,
EFI_FILE_MODE_READ, (u64)0);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to open file: ");
efi_char16_printk(sys_table, filename_16);
efi_printk(sys_table, "\n");
return status;
}
*handle = h;
info_sz = 0;
status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
&info_sz, NULL);
if (status != EFI_BUFFER_TOO_SMALL) {
efi_printk(sys_table, "Failed to get file info size\n");
return status;
}
grow:
status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
info_sz, (void **)&info);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to alloc mem for file info\n");
return status;
}
status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
&info_sz, info);
if (status == EFI_BUFFER_TOO_SMALL) {
efi_call_early(free_pool, info);
goto grow;
}
*file_sz = info->file_size;
efi_call_early(free_pool, info);
if (status != EFI_SUCCESS)
efi_printk(sys_table, "Failed to get initrd info\n");
return status;
}
efi_status_t
efi_file_size(efi_system_table_t *sys_table, void *__fh,
efi_char16_t *filename_16, void **handle, u64 *file_sz)
{
if (efi_early->is64)
return __file_size64(__fh, filename_16, handle, file_sz);
return __file_size32(__fh, filename_16, handle, file_sz);
}
efi_status_t
efi_file_read(void *handle, unsigned long *size, void *addr)
{
unsigned long func;
if (efi_early->is64) {
efi_file_handle_64_t *fh = handle;
func = (unsigned long)fh->read;
return efi_early->call(func, handle, size, addr);
} else {
efi_file_handle_32_t *fh = handle;
func = (unsigned long)fh->read;
return efi_early->call(func, handle, size, addr);
}
}
efi_status_t efi_file_close(void *handle)
{
if (efi_early->is64) {
efi_file_handle_64_t *fh = handle;
return efi_early->call((unsigned long)fh->close, handle);
} else {
efi_file_handle_32_t *fh = handle;
return efi_early->call((unsigned long)fh->close, handle);
}
}
static inline efi_status_t __open_volume32(void *__image, void **__fh)
{
efi_file_io_interface_t *io;
......@@ -249,30 +102,8 @@ efi_open_volume(efi_system_table_t *sys_table, void *__image, void **__fh)
void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
{
unsigned long output_string;
size_t offset;
if (efi_early->is64) {
struct efi_simple_text_output_protocol_64 *out;
u64 *func;
offset = offsetof(typeof(*out), output_string);
output_string = efi_early->text_output + offset;
out = (typeof(out))(unsigned long)efi_early->text_output;
func = (u64 *)output_string;
efi_early->call(*func, out, str);
} else {
struct efi_simple_text_output_protocol_32 *out;
u32 *func;
offset = offsetof(typeof(*out), output_string);
output_string = efi_early->text_output + offset;
out = (typeof(out))(unsigned long)efi_early->text_output;
func = (u32 *)output_string;
efi_early->call(*func, out, str);
}
efi_call_proto(efi_simple_text_output_protocol, output_string,
efi_early->text_output, str);
}
static efi_status_t
......@@ -1157,6 +988,13 @@ struct boot_params *efi_main(struct efi_config *c,
else
setup_boot_services32(efi_early);
/*
* If the boot loader gave us a value for secure_boot then we use that,
* otherwise we ask the BIOS.
*/
if (boot_params->secure_boot == efi_secureboot_mode_unset)
boot_params->secure_boot = efi_get_secureboot(sys_table);
setup_graphics(boot_params);
setup_efi_pci(boot_params);
......
......@@ -82,7 +82,7 @@ ENTRY(efi_pe_entry)
/* Relocate efi_config->call() */
leal efi32_config(%esi), %eax
add %esi, 32(%eax)
add %esi, 40(%eax)
pushl %eax
call make_boot_params
......@@ -108,7 +108,7 @@ ENTRY(efi32_stub_entry)
/* Relocate efi_config->call() */
leal efi32_config(%esi), %eax
add %esi, 32(%eax)
add %esi, 40(%eax)
pushl %eax
2:
call efi_main
......@@ -264,7 +264,7 @@ relocated:
#ifdef CONFIG_EFI_STUB
.data
efi32_config:
.fill 4,8,0
.fill 5,8,0
.long efi_call_phys
.long 0
.byte 0
......
......@@ -264,7 +264,7 @@ ENTRY(efi_pe_entry)
/*
* Relocate efi_config->call().
*/
addq %rbp, efi64_config+32(%rip)
addq %rbp, efi64_config+40(%rip)
movq %rax, %rdi
call make_boot_params
......@@ -284,7 +284,7 @@ handover_entry:
* Relocate efi_config->call().
*/
movq efi_config(%rip), %rax
addq %rbp, 32(%rax)
addq %rbp, 40(%rax)
2:
movq efi_config(%rip), %rdi
call efi_main
......@@ -456,14 +456,14 @@ efi_config:
#ifdef CONFIG_EFI_MIXED
.global efi32_config
efi32_config:
.fill 4,8,0
.fill 5,8,0
.quad efi64_thunk
.byte 0
#endif
.global efi64_config
efi64_config:
.fill 4,8,0
.fill 5,8,0
.quad efi_call
.byte 1
#endif /* CONFIG_EFI_STUB */
......
......@@ -191,6 +191,7 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
struct efi_config {
u64 image_handle;
u64 table;
u64 runtime_services;
u64 boot_services;
u64 text_output;
efi_status_t (*call)(unsigned long, ...);
......@@ -226,6 +227,10 @@ static inline bool efi_is_64bit(void)
#define __efi_call_early(f, ...) \
__efi_early()->call((unsigned long)f, __VA_ARGS__);
#define efi_call_runtime(f, ...) \
__efi_early()->call(efi_table_attr(efi_runtime_services, f, \
__efi_early()->runtime_services), __VA_ARGS__)
extern bool efi_reboot_required(void);
#else
......
......@@ -135,7 +135,8 @@ struct boot_params {
__u8 eddbuf_entries; /* 0x1e9 */
__u8 edd_mbr_sig_buf_entries; /* 0x1ea */
__u8 kbd_status; /* 0x1eb */
__u8 _pad5[3]; /* 0x1ec */
__u8 secure_boot; /* 0x1ec */
__u8 _pad5[2]; /* 0x1ed */
/*
* The sentinel is set to a nonzero value (0xff) in header.S.
*
......
......@@ -35,6 +35,7 @@
#include <linux/bootmem.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/efi-bgrt.h>
#include <asm/irqdomain.h>
#include <asm/pci_x86.h>
......@@ -1557,6 +1558,12 @@ int __init early_acpi_boot_init(void)
return 0;
}
static int __init acpi_parse_bgrt(struct acpi_table_header *table)
{
efi_bgrt_init(table);
return 0;
}
int __init acpi_boot_init(void)
{
/* those are executed after early-quirks are executed */
......@@ -1581,6 +1588,8 @@ int __init acpi_boot_init(void)
acpi_process_madt();
acpi_table_parse(ACPI_SIG_HPET, acpi_parse_hpet);
if (IS_ENABLED(CONFIG_ACPI_BGRT))
acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
if (!acpi_noirq)
x86_init.pci.init = pci_acpi_init;
......
......@@ -81,6 +81,7 @@ void common(void) {
BLANK();
OFFSET(BP_scratch, boot_params, scratch);
OFFSET(BP_secure_boot, boot_params, secure_boot);
OFFSET(BP_loadflags, boot_params, hdr.loadflags);
OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch);
OFFSET(BP_version, boot_params, hdr.version);
......
......@@ -1176,6 +1176,20 @@ void __init setup_arch(char **cmdline_p)
/* Allocate bigger log buffer */
setup_log_buf(1);
if (efi_enabled(EFI_BOOT)) {
switch (boot_params.secure_boot) {
case efi_secureboot_mode_disabled:
pr_info("Secure boot disabled\n");
break;
case efi_secureboot_mode_enabled:
pr_info("Secure boot enabled\n");
break;
default:
pr_info("Secure boot could not be determined\n");
break;
}
}
reserve_initrd();
acpi_table_upgrade();
......
......@@ -19,8 +19,7 @@
#include <linux/efi.h>
#include <linux/efi-bgrt.h>
struct acpi_table_bgrt *bgrt_tab;
void *__initdata bgrt_image;
struct acpi_table_bgrt bgrt_tab;
size_t __initdata bgrt_image_size;
struct bmp_header {
......@@ -28,66 +27,58 @@ struct bmp_header {
u32 size;
} __packed;
void __init efi_bgrt_init(void)
void __init efi_bgrt_init(struct acpi_table_header *table)
{
acpi_status status;
void *image;
struct bmp_header bmp_header;
struct acpi_table_bgrt *bgrt = &bgrt_tab;
if (acpi_disabled)
return;
status = acpi_get_table("BGRT", 0,
(struct acpi_table_header **)&bgrt_tab);
if (ACPI_FAILURE(status))
return;
if (bgrt_tab->header.length < sizeof(*bgrt_tab)) {
if (table->length < sizeof(bgrt_tab)) {
pr_notice("Ignoring BGRT: invalid length %u (expected %zu)\n",
bgrt_tab->header.length, sizeof(*bgrt_tab));
table->length, sizeof(bgrt_tab));
return;
}
if (bgrt_tab->version != 1) {
*bgrt = *(struct acpi_table_bgrt *)table;
if (bgrt->version != 1) {
pr_notice("Ignoring BGRT: invalid version %u (expected 1)\n",
bgrt_tab->version);
return;
bgrt->version);
goto out;
}
if (bgrt_tab->status & 0xfe) {
if (bgrt->status & 0xfe) {
pr_notice("Ignoring BGRT: reserved status bits are non-zero %u\n",
bgrt_tab->status);
return;
bgrt->status);
goto out;
}
if (bgrt_tab->image_type != 0) {
if (bgrt->image_type != 0) {
pr_notice("Ignoring BGRT: invalid image type %u (expected 0)\n",
bgrt_tab->image_type);
return;
bgrt->image_type);
goto out;
}
if (!bgrt_tab->image_address) {
if (!bgrt->image_address) {
pr_notice("Ignoring BGRT: null image address\n");
return;
goto out;
}
image = memremap(bgrt_tab->image_address, sizeof(bmp_header), MEMREMAP_WB);
image = early_memremap(bgrt->image_address, sizeof(bmp_header));
if (!image) {
pr_notice("Ignoring BGRT: failed to map image header memory\n");
return;
goto out;
}
memcpy(&bmp_header, image, sizeof(bmp_header));
memunmap(image);
early_memunmap(image, sizeof(bmp_header));
if (bmp_header.id != 0x4d42) {
pr_notice("Ignoring BGRT: Incorrect BMP magic number 0x%x (expected 0x4d42)\n",
bmp_header.id);
return;
goto out;
}
bgrt_image_size = bmp_header.size;
efi_mem_reserve(bgrt->image_address, bgrt_image_size);
bgrt_image = memremap(bgrt_tab->image_address, bmp_header.size, MEMREMAP_WB);
if (!bgrt_image) {
pr_notice("Ignoring BGRT: failed to map image memory\n");
bgrt_image = NULL;
return;
}
efi_mem_reserve(bgrt_tab->image_address, bgrt_image_size);
return;
out:
memset(bgrt, 0, sizeof(bgrt_tab));
}
......@@ -542,11 +542,6 @@ void __init efi_init(void)
efi_print_memmap();
}
void __init efi_late_init(void)
{
efi_bgrt_init();
}
void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
{
u64 addr, npages;
......@@ -960,6 +955,11 @@ static void __init __efi_enter_virtual_mode(void)
return;
}
if (efi_enabled(EFI_DBG)) {
pr_info("EFI runtime memory map:\n");
efi_print_memmap();
}
BUG_ON(!efi.systab);
if (efi_setup_page_tables(pa, 1 << pg_shift)) {
......
......@@ -414,10 +414,44 @@ void __init parse_efi_setup(u64 phys_addr, u32 data_len)
efi_setup = phys_addr + sizeof(struct setup_data);
}
void __init efi_runtime_update_mappings(void)
static int __init efi_update_mappings(efi_memory_desc_t *md, unsigned long pf)
{
unsigned long pfn;
pgd_t *pgd = efi_pgd;
int err1, err2;
/* Update the 1:1 mapping */
pfn = md->phys_addr >> PAGE_SHIFT;
err1 = kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf);
if (err1) {
pr_err("Error while updating 1:1 mapping PA 0x%llx -> VA 0x%llx!\n",
md->phys_addr, md->virt_addr);
}
err2 = kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf);
if (err2) {
pr_err("Error while updating VA mapping PA 0x%llx -> VA 0x%llx!\n",
md->phys_addr, md->virt_addr);
}
return err1 || err2;
}
static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *md)
{
unsigned long pf = 0;
if (md->attribute & EFI_MEMORY_XP)
pf |= _PAGE_NX;
if (!(md->attribute & EFI_MEMORY_RO))
pf |= _PAGE_RW;
return efi_update_mappings(md, pf);
}
void __init efi_runtime_update_mappings(void)
{
efi_memory_desc_t *md;
if (efi_enabled(EFI_OLD_MEMMAP)) {
......@@ -426,6 +460,24 @@ void __init efi_runtime_update_mappings(void)
return;
}
/*
* Use the EFI Memory Attribute Table for mapping permissions if it
* exists, since it is intended to supersede EFI_PROPERTIES_TABLE.
*/
if (efi_enabled(EFI_MEM_ATTR)) {
efi_memattr_apply_permissions(NULL, efi_update_mem_attr);
return;
}
/*
* EFI_MEMORY_ATTRIBUTES_TABLE is intended to replace
* EFI_PROPERTIES_TABLE. So, use EFI_PROPERTIES_TABLE to update
* permissions only if EFI_MEMORY_ATTRIBUTES_TABLE is not
* published by the firmware. Even if we find a buggy implementation of
* EFI_MEMORY_ATTRIBUTES_TABLE, don't fall back to
* EFI_PROPERTIES_TABLE, because of the same reason.
*/
if (!efi_enabled(EFI_NX_PE_DATA))
return;
......@@ -446,15 +498,7 @@ void __init efi_runtime_update_mappings(void)
(md->type != EFI_RUNTIME_SERVICES_CODE))
pf |= _PAGE_RW;
/* Update the 1:1 mapping */
pfn = md->phys_addr >> PAGE_SHIFT;
if (kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf))
pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
md->phys_addr, md->virt_addr);
if (kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf))
pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
md->phys_addr, md->virt_addr);
efi_update_mappings(md, pf);
}
}
......
......@@ -15,40 +15,41 @@
#include <linux/sysfs.h>
#include <linux/efi-bgrt.h>
static void *bgrt_image;
static struct kobject *bgrt_kobj;
static ssize_t show_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->version);
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.version);
}
static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
static ssize_t show_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->status);
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.status);
}
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
static ssize_t show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_type);
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.image_type);
}
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
static ssize_t show_xoffset(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_x);
return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.image_offset_x);
}
static DEVICE_ATTR(xoffset, S_IRUGO, show_xoffset, NULL);