diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/dmi-id.c | 4 | ||||
-rw-r--r-- | drivers/firmware/dmi_scan.c | 49 | ||||
-rw-r--r-- | drivers/firmware/efi/Kconfig | 9 | ||||
-rw-r--r-- | drivers/firmware/efi/arm-runtime.c | 16 | ||||
-rw-r--r-- | drivers/firmware/efi/capsule-loader.c | 117 | ||||
-rw-r--r-- | drivers/firmware/efi/capsule.c | 11 | ||||
-rw-r--r-- | drivers/firmware/efi/efi.c | 3 | ||||
-rw-r--r-- | drivers/firmware/efi/test/efi_test.c | 11 |
8 files changed, 122 insertions, 98 deletions
diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index dc269cb288c2..951b6c79f166 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -47,7 +47,7 @@ DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME); DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION); DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL); DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID); -DEFINE_DMI_ATTR_WITH_SHOW(product_family, 0400, DMI_PRODUCT_FAMILY); +DEFINE_DMI_ATTR_WITH_SHOW(product_family, 0444, DMI_PRODUCT_FAMILY); DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR); DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME); DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION); @@ -192,7 +192,7 @@ static void __init dmi_id_init_attr_table(void) ADD_DMI_ATTR(product_version, DMI_PRODUCT_VERSION); ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL); ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID); - ADD_DMI_ATTR(product_family, DMI_PRODUCT_FAMILY); + ADD_DMI_ATTR(product_family, DMI_PRODUCT_FAMILY); ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR); ADD_DMI_ATTR(board_name, DMI_BOARD_NAME); ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 93f7acdaac7a..783041964439 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -144,7 +144,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, buf = dmi_early_remap(dmi_base, orig_dmi_len); if (buf == NULL) - return -1; + return -ENOMEM; dmi_decode_table(buf, decode, NULL); @@ -178,7 +178,7 @@ static void __init dmi_save_ident(const struct dmi_header *dm, int slot, const char *d = (const char *) dm; const char *p; - if (dmi_ident[slot]) + if (dmi_ident[slot] || dm->length <= string) return; p = dmi_string(dm, d[string]); @@ -191,13 +191,14 @@ static void __init dmi_save_ident(const struct dmi_header *dm, int slot, static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, int index) { - const u8 *d = (u8 *) dm + index; + const u8 *d; char *s; int is_ff = 1, is_00 = 1, i; - if (dmi_ident[slot]) + if (dmi_ident[slot] || dm->length <= index + 16) return; + d = (u8 *) dm + index; for (i = 0; i < 16 && (is_ff || is_00); i++) { if (d[i] != 0x00) is_00 = 0; @@ -228,16 +229,17 @@ static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, static void __init dmi_save_type(const struct dmi_header *dm, int slot, int index) { - const u8 *d = (u8 *) dm + index; + const u8 *d; char *s; - if (dmi_ident[slot]) + if (dmi_ident[slot] || dm->length <= index) return; s = dmi_alloc(4); if (!s) return; + d = (u8 *) dm + index; sprintf(s, "%u", *d & 0x7F); dmi_ident[slot] = s; } @@ -278,9 +280,13 @@ static void __init dmi_save_devices(const struct dmi_header *dm) static void __init dmi_save_oem_strings_devices(const struct dmi_header *dm) { - int i, count = *(u8 *)(dm + 1); + int i, count; struct dmi_device *dev; + if (dm->length < 0x05) + return; + + count = *(u8 *)(dm + 1); for (i = 1; i <= count; i++) { const char *devname = dmi_string(dm, i); @@ -353,6 +359,9 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm) const char *name; const u8 *d = (u8 *)dm; + if (dm->length < 0x0B) + return; + /* Skip disabled device */ if ((d[0x5] & 0x80) == 0) return; @@ -387,7 +396,7 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) const char *d = (const char *)dm; static int nr; - if (dm->type != DMI_ENTRY_MEM_DEVICE) + if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12) return; if (nr >= dmi_memdev_nr) { pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n"); @@ -650,6 +659,21 @@ void __init dmi_scan_machine(void) goto error; /* + * Same logic as above, look for a 64-bit entry point + * first, and if not found, fall back to 32-bit entry point. + */ + memcpy_fromio(buf, p, 16); + for (q = p + 16; q < p + 0x10000; q += 16) { + memcpy_fromio(buf + 16, q, 16); + if (!dmi_smbios3_present(buf)) { + dmi_available = 1; + dmi_early_unmap(p, 0x10000); + goto out; + } + memcpy(buf, buf + 16, 16); + } + + /* * Iterate over all possible DMI header addresses q. * Maintain the 32 bytes around q in buf. On the * first iteration, substitute zero for the @@ -659,7 +683,7 @@ void __init dmi_scan_machine(void) memset(buf, 0, 16); for (q = p; q < p + 0x10000; q += 16) { memcpy_fromio(buf + 16, q, 16); - if (!dmi_smbios3_present(buf) || !dmi_present(buf)) { + if (!dmi_present(buf)) { dmi_available = 1; dmi_early_unmap(p, 0x10000); goto out; @@ -993,7 +1017,8 @@ EXPORT_SYMBOL(dmi_get_date); * @decode: Callback function * @private_data: Private data to be passed to the callback function * - * Returns -1 when the DMI table can't be reached, 0 on success. + * Returns 0 on success, -ENXIO if DMI is not selected or not present, + * or a different negative error code if DMI walking fails. */ int dmi_walk(void (*decode)(const struct dmi_header *, void *), void *private_data) @@ -1001,11 +1026,11 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *), u8 *buf; if (!dmi_available) - return -1; + return -ENXIO; buf = dmi_remap(dmi_base, dmi_len); if (buf == NULL) - return -1; + return -ENOMEM; dmi_decode_table(buf, decode, private_data); diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 2e78b0b96d74..394db40ed374 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -112,6 +112,15 @@ config EFI_CAPSULE_LOADER Most users should say N. +config EFI_CAPSULE_QUIRK_QUARK_CSH + boolean "Add support for Quark capsules with non-standard headers" + depends on X86 && !64BIT + select EFI_CAPSULE_LOADER + default y + help + Add support for processing Quark X1000 EFI capsules, whose header + layout deviates from the layout mandated by the UEFI specification. + config EFI_TEST tristate "EFI Runtime Service Tests Support" depends on EFI diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 974c5a31a005..1cc41c3d6315 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -11,6 +11,7 @@ * */ +#include <linux/dmi.h> #include <linux/efi.h> #include <linux/io.h> #include <linux/memblock.h> @@ -166,3 +167,18 @@ void efi_virtmap_unload(void) efi_set_pgd(current->active_mm); preempt_enable(); } + + +static int __init arm_dmi_init(void) +{ + /* + * On arm64/ARM, DMI depends on UEFI, and dmi_scan_machine() needs to + * be called early because dmi_id_init(), which is an arch_initcall + * itself, depends on dmi_scan_machine() having been called already. + */ + dmi_scan_machine(); + if (dmi_available) + dmi_set_dump_stack_arch_desc(); + return 0; +} +core_initcall(arm_dmi_init); diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index 9ae6c116c474..ec8ac5c4dd84 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -20,15 +20,9 @@ #define NO_FURTHER_WRITE_ACTION -1 -struct capsule_info { - bool header_obtained; - int reset_type; - long index; - size_t count; - size_t total_size; - struct page **pages; - size_t page_bytes_remain; -}; +#ifndef phys_to_page +#define phys_to_page(x) pfn_to_page((x) >> PAGE_SHIFT) +#endif /** * efi_free_all_buff_pages - free all previous allocated buffer pages @@ -41,65 +35,70 @@ struct capsule_info { static void efi_free_all_buff_pages(struct capsule_info *cap_info) { while (cap_info->index > 0) - __free_page(cap_info->pages[--cap_info->index]); + __free_page(phys_to_page(cap_info->pages[--cap_info->index])); cap_info->index = NO_FURTHER_WRITE_ACTION; } -/** - * efi_capsule_setup_info - obtain the efi capsule header in the binary and - * setup capsule_info structure - * @cap_info: pointer to current instance of capsule_info structure - * @kbuff: a mapped first page buffer pointer - * @hdr_bytes: the total received number of bytes for efi header - **/ -static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info, - void *kbuff, size_t hdr_bytes) +int __efi_capsule_setup_info(struct capsule_info *cap_info) { - efi_capsule_header_t *cap_hdr; size_t pages_needed; int ret; void *temp_page; - /* Only process data block that is larger than efi header size */ - if (hdr_bytes < sizeof(efi_capsule_header_t)) - return 0; - - /* Reset back to the correct offset of header */ - cap_hdr = kbuff - cap_info->count; - pages_needed = ALIGN(cap_hdr->imagesize, PAGE_SIZE) >> PAGE_SHIFT; + pages_needed = ALIGN(cap_info->total_size, PAGE_SIZE) / PAGE_SIZE; if (pages_needed == 0) { - pr_err("%s: pages count invalid\n", __func__); + pr_err("invalid capsule size"); return -EINVAL; } /* Check if the capsule binary supported */ - ret = efi_capsule_supported(cap_hdr->guid, cap_hdr->flags, - cap_hdr->imagesize, + ret = efi_capsule_supported(cap_info->header.guid, + cap_info->header.flags, + cap_info->header.imagesize, &cap_info->reset_type); if (ret) { - pr_err("%s: efi_capsule_supported() failed\n", - __func__); + pr_err("capsule not supported\n"); return ret; } - cap_info->total_size = cap_hdr->imagesize; temp_page = krealloc(cap_info->pages, pages_needed * sizeof(void *), GFP_KERNEL | __GFP_ZERO); - if (!temp_page) { - pr_debug("%s: krealloc() failed\n", __func__); + if (!temp_page) return -ENOMEM; - } cap_info->pages = temp_page; - cap_info->header_obtained = true; return 0; } /** + * efi_capsule_setup_info - obtain the efi capsule header in the binary and + * setup capsule_info structure + * @cap_info: pointer to current instance of capsule_info structure + * @kbuff: a mapped first page buffer pointer + * @hdr_bytes: the total received number of bytes for efi header + * + * Platforms with non-standard capsule update mechanisms can override + * this __weak function so they can perform any required capsule + * image munging. See quark_quirk_function() for an example. + **/ +int __weak efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff, + size_t hdr_bytes) +{ + /* Only process data block that is larger than efi header size */ + if (hdr_bytes < sizeof(efi_capsule_header_t)) + return 0; + + memcpy(&cap_info->header, kbuff, sizeof(cap_info->header)); + cap_info->total_size = cap_info->header.imagesize; + + return __efi_capsule_setup_info(cap_info); +} + +/** * efi_capsule_submit_update - invoke the efi_capsule_update API once binary * upload done * @cap_info: pointer to current instance of capsule_info structure @@ -107,26 +106,17 @@ static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info, static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) { int ret; - void *cap_hdr_temp; - - cap_hdr_temp = vmap(cap_info->pages, cap_info->index, - VM_MAP, PAGE_KERNEL); - if (!cap_hdr_temp) { - pr_debug("%s: vmap() failed\n", __func__); - return -EFAULT; - } - ret = efi_capsule_update(cap_hdr_temp, cap_info->pages); - vunmap(cap_hdr_temp); + ret = efi_capsule_update(&cap_info->header, cap_info->pages); if (ret) { - pr_err("%s: efi_capsule_update() failed\n", __func__); + pr_err("capsule update failed\n"); return ret; } /* Indicate capsule binary uploading is done */ cap_info->index = NO_FURTHER_WRITE_ACTION; - pr_info("%s: Successfully upload capsule file with reboot type '%s'\n", - __func__, !cap_info->reset_type ? "RESET_COLD" : + pr_info("Successfully upload capsule file with reboot type '%s'\n", + !cap_info->reset_type ? "RESET_COLD" : cap_info->reset_type == 1 ? "RESET_WARM" : "RESET_SHUTDOWN"); return 0; @@ -171,37 +161,30 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, if (!cap_info->page_bytes_remain) { page = alloc_page(GFP_KERNEL); if (!page) { - pr_debug("%s: alloc_page() failed\n", __func__); ret = -ENOMEM; goto failed; } - cap_info->pages[cap_info->index++] = page; + cap_info->pages[cap_info->index++] = page_to_phys(page); cap_info->page_bytes_remain = PAGE_SIZE; + } else { + page = phys_to_page(cap_info->pages[cap_info->index - 1]); } - page = cap_info->pages[cap_info->index - 1]; - kbuff = kmap(page); - if (!kbuff) { - pr_debug("%s: kmap() failed\n", __func__); - ret = -EFAULT; - goto failed; - } kbuff += PAGE_SIZE - cap_info->page_bytes_remain; /* Copy capsule binary data from user space to kernel space buffer */ write_byte = min_t(size_t, count, cap_info->page_bytes_remain); if (copy_from_user(kbuff, buff, write_byte)) { - pr_debug("%s: copy_from_user() failed\n", __func__); ret = -EFAULT; goto fail_unmap; } cap_info->page_bytes_remain -= write_byte; /* Setup capsule binary info structure */ - if (!cap_info->header_obtained) { - ret = efi_capsule_setup_info(cap_info, kbuff, + if (cap_info->header.headersize == 0) { + ret = efi_capsule_setup_info(cap_info, kbuff - cap_info->count, cap_info->count + write_byte); if (ret) goto fail_unmap; @@ -211,11 +194,10 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, kunmap(page); /* Submit the full binary to efi_capsule_update() API */ - if (cap_info->header_obtained && + if (cap_info->header.headersize > 0 && cap_info->count >= cap_info->total_size) { if (cap_info->count > cap_info->total_size) { - pr_err("%s: upload size exceeded header defined size\n", - __func__); + pr_err("capsule upload size exceeded header defined size\n"); ret = -EINVAL; goto failed; } @@ -249,7 +231,7 @@ static int efi_capsule_flush(struct file *file, fl_owner_t id) struct capsule_info *cap_info = file->private_data; if (cap_info->index > 0) { - pr_err("%s: capsule upload not complete\n", __func__); + pr_err("capsule upload not complete\n"); efi_free_all_buff_pages(cap_info); ret = -ECANCELED; } @@ -328,8 +310,7 @@ static int __init efi_capsule_loader_init(void) ret = misc_register(&efi_capsule_misc); if (ret) - pr_err("%s: Failed to register misc char file note\n", - __func__); + pr_err("Unable to register capsule loader device\n"); return ret; } diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c index 6eedff45e6d7..901b9306bf94 100644 --- a/drivers/firmware/efi/capsule.c +++ b/drivers/firmware/efi/capsule.c @@ -214,7 +214,7 @@ efi_capsule_update_locked(efi_capsule_header_t *capsule, * * Return 0 on success, a converted EFI status code on failure. */ -int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages) +int efi_capsule_update(efi_capsule_header_t *capsule, phys_addr_t *pages) { u32 imagesize = capsule->imagesize; efi_guid_t guid = capsule->guid; @@ -247,16 +247,13 @@ int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages) efi_capsule_block_desc_t *sglist; sglist = kmap(sg_pages[i]); - if (!sglist) { - rv = -ENOMEM; - goto out; - } for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) { - u64 sz = min_t(u64, imagesize, PAGE_SIZE); + u64 sz = min_t(u64, imagesize, + PAGE_SIZE - (u64)*pages % PAGE_SIZE); sglist[j].length = sz; - sglist[j].data = page_to_phys(*pages++); + sglist[j].data = *pages++; imagesize -= sz; count--; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index b372aad3b449..045d6d311bde 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -528,7 +528,8 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, } } - efi_memattr_init(); + if (efi_enabled(EFI_MEMMAP)) + efi_memattr_init(); /* Parse the EFI Properties table if it exists */ if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c index 8cd578f62059..08129b7b80ab 100644 --- a/drivers/firmware/efi/test/efi_test.c +++ b/drivers/firmware/efi/test/efi_test.c @@ -71,18 +71,13 @@ copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src, if (!access_ok(VERIFY_READ, src, 1)) return -EFAULT; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { + buf = memdup_user(src, len); + if (IS_ERR(buf)) { *dst = NULL; - return -ENOMEM; + return PTR_ERR(buf); } *dst = buf; - if (copy_from_user(*dst, src, len)) { - kfree(buf); - return -EFAULT; - } - return 0; } |