diff options
Diffstat (limited to 'drivers/firmware')
21 files changed, 724 insertions, 128 deletions
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index e996395af5f2..8f952f2f1a29 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -776,6 +776,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) { dev_err(&sdev->dev, "failed to setup transport\n"); scmi_device_destroy(sdev); + return; } /* setup handle now as the transport is ready */ diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 3098410abad8..781a4a337557 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -174,6 +174,11 @@ config UEFI_CPER_ARM depends on UEFI_CPER && ( ARM || ARM64 ) default y +config UEFI_CPER_X86 + bool + depends on UEFI_CPER && X86 + default y + config EFI_DEV_PATH_PARSER bool depends on ACPI diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index cb805374f4bc..5f9f5039de50 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y) obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o +obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index adaa9a3714b9..60a95719ecb8 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -13,6 +13,9 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Note, all properties are considered as u8 arrays. + * To get a value of any of them the caller must use device_property_read_u8_array(). */ #define pr_fmt(fmt) "apple-properties: " fmt @@ -96,12 +99,13 @@ static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, entry[i].name = key; entry[i].length = val_len - sizeof(val_len); entry[i].is_array = !!entry[i].length; - entry[i].pointer.raw_data = ptr + key_len + sizeof(val_len); + entry[i].type = DEV_PROP_U8; + entry[i].pointer.u8_data = ptr + key_len + sizeof(val_len); if (dump_properties) { dev_info(dev, "property: %s\n", entry[i].name); print_hex_dump(KERN_INFO, pr_fmt(), DUMP_PREFIX_OFFSET, - 16, 1, entry[i].pointer.raw_data, + 16, 1, entry[i].pointer.u8_data, entry[i].length, true); } diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index e456f4602df1..96688986da56 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -134,10 +134,16 @@ static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) /* Indicate capsule binary uploading is done */ cap_info->index = NO_FURTHER_WRITE_ACTION; - 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"); + + if (cap_info->header.flags & EFI_CAPSULE_PERSIST_ACROSS_RESET) { + pr_info("Successfully uploaded capsule file with reboot type '%s'\n", + !cap_info->reset_type ? "RESET_COLD" : + cap_info->reset_type == 1 ? "RESET_WARM" : + "RESET_SHUTDOWN"); + } else { + pr_info("Successfully processed capsule file\n"); + } + return 0; } diff --git a/drivers/firmware/efi/cper-arm.c b/drivers/firmware/efi/cper-arm.c index 698e5c8e0c8d..502811344e81 100644 --- a/drivers/firmware/efi/cper-arm.c +++ b/drivers/firmware/efi/cper-arm.c @@ -30,8 +30,6 @@ #include <acpi/ghes.h> #include <ras/ras_event.h> -#define INDENT_SP " " - static const char * const arm_reg_ctx_strs[] = { "AArch32 general purpose registers", "AArch32 EL1 context registers", @@ -283,7 +281,7 @@ void cper_print_proc_arm(const char *pfx, pfx, proc->psci_state); } - snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); + snprintf(newpfx, sizeof(newpfx), "%s ", pfx); err_info = (struct cper_arm_err_info *)(proc + 1); for (i = 0; i < proc->err_info_num; i++) { @@ -310,7 +308,7 @@ void cper_print_proc_arm(const char *pfx, if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO) { printk("%serror_info: 0x%016llx\n", newpfx, err_info->error_info); - snprintf(infopfx, sizeof(infopfx), "%s%s", newpfx, INDENT_SP); + snprintf(infopfx, sizeof(infopfx), "%s ", newpfx); cper_print_arm_err_info(infopfx, err_info->type, err_info->error_info); } diff --git a/drivers/firmware/efi/cper-x86.c b/drivers/firmware/efi/cper-x86.c new file mode 100644 index 000000000000..2531de49f56c --- /dev/null +++ b/drivers/firmware/efi/cper-x86.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018, Advanced Micro Devices, Inc. + +#include <linux/cper.h> + +/* + * We don't need a "CPER_IA" prefix since these are all locally defined. + * This will save us a lot of line space. + */ +#define VALID_LAPIC_ID BIT_ULL(0) +#define VALID_CPUID_INFO BIT_ULL(1) +#define VALID_PROC_ERR_INFO_NUM(bits) (((bits) & GENMASK_ULL(7, 2)) >> 2) +#define VALID_PROC_CXT_INFO_NUM(bits) (((bits) & GENMASK_ULL(13, 8)) >> 8) + +#define INFO_ERR_STRUCT_TYPE_CACHE \ + GUID_INIT(0xA55701F5, 0xE3EF, 0x43DE, 0xAC, 0x72, 0x24, 0x9B, \ + 0x57, 0x3F, 0xAD, 0x2C) +#define INFO_ERR_STRUCT_TYPE_TLB \ + GUID_INIT(0xFC06B535, 0x5E1F, 0x4562, 0x9F, 0x25, 0x0A, 0x3B, \ + 0x9A, 0xDB, 0x63, 0xC3) +#define INFO_ERR_STRUCT_TYPE_BUS \ + GUID_INIT(0x1CF3F8B3, 0xC5B1, 0x49a2, 0xAA, 0x59, 0x5E, 0xEF, \ + 0x92, 0xFF, 0xA6, 0x3C) +#define INFO_ERR_STRUCT_TYPE_MS \ + GUID_INIT(0x48AB7F57, 0xDC34, 0x4f6c, 0xA7, 0xD3, 0xB0, 0xB5, \ + 0xB0, 0xA7, 0x43, 0x14) + +#define INFO_VALID_CHECK_INFO BIT_ULL(0) +#define INFO_VALID_TARGET_ID BIT_ULL(1) +#define INFO_VALID_REQUESTOR_ID BIT_ULL(2) +#define INFO_VALID_RESPONDER_ID BIT_ULL(3) +#define INFO_VALID_IP BIT_ULL(4) + +#define CHECK_VALID_TRANS_TYPE BIT_ULL(0) +#define CHECK_VALID_OPERATION BIT_ULL(1) +#define CHECK_VALID_LEVEL BIT_ULL(2) +#define CHECK_VALID_PCC BIT_ULL(3) +#define CHECK_VALID_UNCORRECTED BIT_ULL(4) +#define CHECK_VALID_PRECISE_IP BIT_ULL(5) +#define CHECK_VALID_RESTARTABLE_IP BIT_ULL(6) +#define CHECK_VALID_OVERFLOW BIT_ULL(7) + +#define CHECK_VALID_BUS_PART_TYPE BIT_ULL(8) +#define CHECK_VALID_BUS_TIME_OUT BIT_ULL(9) +#define CHECK_VALID_BUS_ADDR_SPACE BIT_ULL(10) + +#define CHECK_VALID_BITS(check) (((check) & GENMASK_ULL(15, 0))) +#define CHECK_TRANS_TYPE(check) (((check) & GENMASK_ULL(17, 16)) >> 16) +#define CHECK_OPERATION(check) (((check) & GENMASK_ULL(21, 18)) >> 18) +#define CHECK_LEVEL(check) (((check) & GENMASK_ULL(24, 22)) >> 22) +#define CHECK_PCC BIT_ULL(25) +#define CHECK_UNCORRECTED BIT_ULL(26) +#define CHECK_PRECISE_IP BIT_ULL(27) +#define CHECK_RESTARTABLE_IP BIT_ULL(28) +#define CHECK_OVERFLOW BIT_ULL(29) + +#define CHECK_BUS_PART_TYPE(check) (((check) & GENMASK_ULL(31, 30)) >> 30) +#define CHECK_BUS_TIME_OUT BIT_ULL(32) +#define CHECK_BUS_ADDR_SPACE(check) (((check) & GENMASK_ULL(34, 33)) >> 33) + +#define CHECK_VALID_MS_ERR_TYPE BIT_ULL(0) +#define CHECK_VALID_MS_PCC BIT_ULL(1) +#define CHECK_VALID_MS_UNCORRECTED BIT_ULL(2) +#define CHECK_VALID_MS_PRECISE_IP BIT_ULL(3) +#define CHECK_VALID_MS_RESTARTABLE_IP BIT_ULL(4) +#define CHECK_VALID_MS_OVERFLOW BIT_ULL(5) + +#define CHECK_MS_ERR_TYPE(check) (((check) & GENMASK_ULL(18, 16)) >> 16) +#define CHECK_MS_PCC BIT_ULL(19) +#define CHECK_MS_UNCORRECTED BIT_ULL(20) +#define CHECK_MS_PRECISE_IP BIT_ULL(21) +#define CHECK_MS_RESTARTABLE_IP BIT_ULL(22) +#define CHECK_MS_OVERFLOW BIT_ULL(23) + +#define CTX_TYPE_MSR 1 +#define CTX_TYPE_MMREG 7 + +enum err_types { + ERR_TYPE_CACHE = 0, + ERR_TYPE_TLB, + ERR_TYPE_BUS, + ERR_TYPE_MS, + N_ERR_TYPES +}; + +static enum err_types cper_get_err_type(const guid_t *err_type) +{ + if (guid_equal(err_type, &INFO_ERR_STRUCT_TYPE_CACHE)) + return ERR_TYPE_CACHE; + else if (guid_equal(err_type, &INFO_ERR_STRUCT_TYPE_TLB)) + return ERR_TYPE_TLB; + else if (guid_equal(err_type, &INFO_ERR_STRUCT_TYPE_BUS)) + return ERR_TYPE_BUS; + else if (guid_equal(err_type, &INFO_ERR_STRUCT_TYPE_MS)) + return ERR_TYPE_MS; + else + return N_ERR_TYPES; +} + +static const char * const ia_check_trans_type_strs[] = { + "Instruction", + "Data Access", + "Generic", +}; + +static const char * const ia_check_op_strs[] = { + "generic error", + "generic read", + "generic write", + "data read", + "data write", + "instruction fetch", + "prefetch", + "eviction", + "snoop", +}; + +static const char * const ia_check_bus_part_type_strs[] = { + "Local Processor originated request", + "Local Processor responded to request", + "Local Processor observed", + "Generic", +}; + +static const char * const ia_check_bus_addr_space_strs[] = { + "Memory Access", + "Reserved", + "I/O", + "Other Transaction", +}; + +static const char * const ia_check_ms_error_type_strs[] = { + "No Error", + "Unclassified", + "Microcode ROM Parity Error", + "External Error", + "FRC Error", + "Internal Unclassified", +}; + +static const char * const ia_reg_ctx_strs[] = { + "Unclassified Data", + "MSR Registers (Machine Check and other MSRs)", + "32-bit Mode Execution Context", + "64-bit Mode Execution Context", + "FXSAVE Context", + "32-bit Mode Debug Registers (DR0-DR7)", + "64-bit Mode Debug Registers (DR0-DR7)", + "Memory Mapped Registers", +}; + +static inline void print_bool(char *str, const char *pfx, u64 check, u64 bit) +{ + printk("%s%s: %s\n", pfx, str, (check & bit) ? "true" : "false"); +} + +static void print_err_info_ms(const char *pfx, u16 validation_bits, u64 check) +{ + if (validation_bits & CHECK_VALID_MS_ERR_TYPE) { + u8 err_type = CHECK_MS_ERR_TYPE(check); + + printk("%sError Type: %u, %s\n", pfx, err_type, + err_type < ARRAY_SIZE(ia_check_ms_error_type_strs) ? + ia_check_ms_error_type_strs[err_type] : "unknown"); + } + + if (validation_bits & CHECK_VALID_MS_PCC) + print_bool("Processor Context Corrupt", pfx, check, CHECK_MS_PCC); + + if (validation_bits & CHECK_VALID_MS_UNCORRECTED) + print_bool("Uncorrected", pfx, check, CHECK_MS_UNCORRECTED); + + if (validation_bits & CHECK_VALID_MS_PRECISE_IP) + print_bool("Precise IP", pfx, check, CHECK_MS_PRECISE_IP); + + if (validation_bits & CHECK_VALID_MS_RESTARTABLE_IP) + print_bool("Restartable IP", pfx, check, CHECK_MS_RESTARTABLE_IP); + + if (validation_bits & CHECK_VALID_MS_OVERFLOW) + print_bool("Overflow", pfx, check, CHECK_MS_OVERFLOW); +} + +static void print_err_info(const char *pfx, u8 err_type, u64 check) +{ + u16 validation_bits = CHECK_VALID_BITS(check); + + /* + * The MS Check structure varies a lot from the others, so use a + * separate function for decoding. + */ + if (err_type == ERR_TYPE_MS) + return print_err_info_ms(pfx, validation_bits, check); + + if (validation_bits & CHECK_VALID_TRANS_TYPE) { + u8 trans_type = CHECK_TRANS_TYPE(check); + + printk("%sTransaction Type: %u, %s\n", pfx, trans_type, + trans_type < ARRAY_SIZE(ia_check_trans_type_strs) ? + ia_check_trans_type_strs[trans_type] : "unknown"); + } + + if (validation_bits & CHECK_VALID_OPERATION) { + u8 op = CHECK_OPERATION(check); + + /* + * CACHE has more operation types than TLB or BUS, though the + * name and the order are the same. + */ + u8 max_ops = (err_type == ERR_TYPE_CACHE) ? 9 : 7; + + printk("%sOperation: %u, %s\n", pfx, op, + op < max_ops ? ia_check_op_strs[op] : "unknown"); + } + + if (validation_bits & CHECK_VALID_LEVEL) + printk("%sLevel: %llu\n", pfx, CHECK_LEVEL(check)); + + if (validation_bits & CHECK_VALID_PCC) + print_bool("Processor Context Corrupt", pfx, check, CHECK_PCC); + + if (validation_bits & CHECK_VALID_UNCORRECTED) + print_bool("Uncorrected", pfx, check, CHECK_UNCORRECTED); + + if (validation_bits & CHECK_VALID_PRECISE_IP) + print_bool("Precise IP", pfx, check, CHECK_PRECISE_IP); + + if (validation_bits & CHECK_VALID_RESTARTABLE_IP) + print_bool("Restartable IP", pfx, check, CHECK_RESTARTABLE_IP); + + if (validation_bits & CHECK_VALID_OVERFLOW) + print_bool("Overflow", pfx, check, CHECK_OVERFLOW); + + if (err_type != ERR_TYPE_BUS) + return; + + if (validation_bits & CHECK_VALID_BUS_PART_TYPE) { + u8 part_type = CHECK_BUS_PART_TYPE(check); + + printk("%sParticipation Type: %u, %s\n", pfx, part_type, + part_type < ARRAY_SIZE(ia_check_bus_part_type_strs) ? + ia_check_bus_part_type_strs[part_type] : "unknown"); + } + + if (validation_bits & CHECK_VALID_BUS_TIME_OUT) + print_bool("Time Out", pfx, check, CHECK_BUS_TIME_OUT); + + if (validation_bits & CHECK_VALID_BUS_ADDR_SPACE) { + u8 addr_space = CHECK_BUS_ADDR_SPACE(check); + + printk("%sAddress Space: %u, %s\n", pfx, addr_space, + addr_space < ARRAY_SIZE(ia_check_bus_addr_space_strs) ? + ia_check_bus_addr_space_strs[addr_space] : "unknown"); + } +} + +void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc) +{ + int i; + struct cper_ia_err_info *err_info; + struct cper_ia_proc_ctx *ctx_info; + char newpfx[64], infopfx[64]; + u8 err_type; + + if (proc->validation_bits & VALID_LAPIC_ID) + printk("%sLocal APIC_ID: 0x%llx\n", pfx, proc->lapic_id); + + if (proc->validation_bits & VALID_CPUID_INFO) { + printk("%sCPUID Info:\n", pfx); + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, proc->cpuid, + sizeof(proc->cpuid), 0); + } + + snprintf(newpfx, sizeof(newpfx), "%s ", pfx); + + err_info = (struct cper_ia_err_info *)(proc + 1); + for (i = 0; i < VALID_PROC_ERR_INFO_NUM(proc->validation_bits); i++) { + printk("%sError Information Structure %d:\n", pfx, i); + + err_type = cper_get_err_type(&err_info->err_type); + printk("%sError Structure Type: %s\n", newpfx, + err_type < ARRAY_SIZE(cper_proc_error_type_strs) ? + cper_proc_error_type_strs[err_type] : "unknown"); + + if (err_type >= N_ERR_TYPES) { + printk("%sError Structure Type: %pUl\n", newpfx, + &err_info->err_type); + } + + if (err_info->validation_bits & INFO_VALID_CHECK_INFO) { + printk("%sCheck Information: 0x%016llx\n", newpfx, + err_info->check_info); + + if (err_type < N_ERR_TYPES) { + snprintf(infopfx, sizeof(infopfx), "%s ", + newpfx); + + print_err_info(infopfx, err_type, + err_info->check_info); + } + } + + if (err_info->validation_bits & INFO_VALID_TARGET_ID) { + printk("%sTarget Identifier: 0x%016llx\n", + newpfx, err_info->target_id); + } + + if (err_info->validation_bits & INFO_VALID_REQUESTOR_ID) { + printk("%sRequestor Identifier: 0x%016llx\n", + newpfx, err_info->requestor_id); + } + + if (err_info->validation_bits & INFO_VALID_RESPONDER_ID) { + printk("%sResponder Identifier: 0x%016llx\n", + newpfx, err_info->responder_id); + } + + if (err_info->validation_bits & INFO_VALID_IP) { + printk("%sInstruction Pointer: 0x%016llx\n", + newpfx, err_info->ip); + } + + err_info++; + } + + ctx_info = (struct cper_ia_proc_ctx *)err_info; + for (i = 0; i < VALID_PROC_CXT_INFO_NUM(proc->validation_bits); i++) { + int size = sizeof(*ctx_info) + ctx_info->reg_arr_size; + int groupsize = 4; + + printk("%sContext Information Structure %d:\n", pfx, i); + + printk("%sRegister Context Type: %s\n", newpfx, + ctx_info->reg_ctx_type < ARRAY_SIZE(ia_reg_ctx_strs) ? + ia_reg_ctx_strs[ctx_info->reg_ctx_type] : "unknown"); + + printk("%sRegister Array Size: 0x%04x\n", newpfx, + ctx_info->reg_arr_size); + + if (ctx_info->reg_ctx_type == CTX_TYPE_MSR) { + groupsize = 8; /* MSRs are 8 bytes wide. */ + printk("%sMSR Address: 0x%08x\n", newpfx, + ctx_info->msr_addr); + } + + if (ctx_info->reg_ctx_type == CTX_TYPE_MMREG) { + printk("%sMM Register Address: 0x%016llx\n", newpfx, + ctx_info->mm_reg_addr); + } + + printk("%sRegister Array:\n", newpfx); + print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, groupsize, + (ctx_info + 1), ctx_info->reg_arr_size, 0); + + ctx_info = (struct cper_ia_proc_ctx *)((long)ctx_info + size); + } +} diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index c165933ebf38..3bf0dca378a6 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -37,8 +37,6 @@ #include <acpi/ghes.h> #include <ras/ras_event.h> -#define INDENT_SP " " - static char rcd_decode_str[CPER_REC_LEN]; /* @@ -433,7 +431,7 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); - snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); + snprintf(newpfx, sizeof(newpfx), "%s ", pfx); if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); @@ -470,6 +468,16 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata else goto err_section_too_small; #endif +#if defined(CONFIG_UEFI_CPER_X86) + } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { + struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); + + printk("%ssection_type: IA32/X64 processor error\n", newpfx); + if (gdata->error_data_length >= sizeof(*ia_err)) + cper_print_proc_ia(newpfx, ia_err); + else + goto err_section_too_small; +#endif } else { const void *err = acpi_hest_get_payload(gdata); @@ -500,7 +508,7 @@ void cper_estatus_print(const char *pfx, "It has been corrected by h/w " "and requires no further action"); printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); - snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); + snprintf(newpfx, sizeof(newpfx), "%s ", pfx); apei_estatus_for_each_section(estatus, gdata) { cper_estatus_print_section(newpfx, gdata, sec_no); diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index b9bd827caa22..1b4d465cc5d9 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -98,6 +98,16 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg, (phys_seed >> 32) & mask : TEXT_OFFSET; /* + * With CONFIG_RANDOMIZE_TEXT_OFFSET=y, TEXT_OFFSET may not + * be a multiple of EFI_KIMG_ALIGN, and we must ensure that + * we preserve the misalignment of 'offset' relative to + * EFI_KIMG_ALIGN so that statically allocated objects whose + * alignment exceeds PAGE_SIZE appear correctly aligned in + * memory. + */ + offset |= TEXT_OFFSET % EFI_KIMG_ALIGN; + + /* * If KASLR is enabled, and we have some randomness available, * locate the kernel at a randomized offset in physical memory. */ diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c index 8f07eb414c00..72d9dfbebf08 100644 --- a/drivers/firmware/efi/libstub/secureboot.c +++ b/drivers/firmware/efi/libstub/secureboot.c @@ -30,6 +30,9 @@ static const efi_char16_t shim_MokSBState_name[] = L"MokSBState"; /* * Determine whether we're in secure boot mode. + * + * Please keep the logic in sync with + * arch/x86/xen/efi.c:xen_efi_get_secureboot(). */ enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table_arg) { diff --git a/drivers/firmware/efi/libstub/tpm.c b/drivers/firmware/efi/libstub/tpm.c index 9d08cea3f1b0..caa37a6dd9d4 100644 --- a/drivers/firmware/efi/libstub/tpm.c +++ b/drivers/firmware/efi/libstub/tpm.c @@ -59,7 +59,7 @@ void efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg) #endif -void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) +static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) { efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID; diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index f16b381a569c..a456a000048b 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -55,6 +55,14 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY the EBDA on Google servers. If found, this log is exported to userland in the file /sys/firmware/log. +config GOOGLE_FRAMEBUFFER_COREBOOT + tristate "Coreboot Framebuffer" + depends on FB_SIMPLE + depends on GOOGLE_COREBOOT_TABLE + help + This option enables the kernel to search for a framebuffer in + the coreboot table. If found, it is registered with simplefb. + config GOOGLE_MEMCONSOLE_COREBOOT tristate "Firmware Memory Console" depends on GOOGLE_COREBOOT_TABLE diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile index dcd3675efcfc..d0b3fba96194 100644 --- a/drivers/firmware/google/Makefile +++ b/drivers/firmware/google/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_GOOGLE_SMI) += gsmi.o obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o +obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o diff --git a/drivers/firmware/google/coreboot_table-acpi.c b/drivers/firmware/google/coreboot_table-acpi.c index fb98db2d20e2..77197fe3d42f 100644 --- a/drivers/firmware/google/coreboot_table-acpi.c +++ b/drivers/firmware/google/coreboot_table-acpi.c @@ -53,7 +53,7 @@ static int coreboot_table_acpi_probe(struct platform_device *pdev) if (!ptr) return -ENOMEM; - return coreboot_table_init(ptr); + return coreboot_table_init(&pdev->dev, ptr); } static int coreboot_table_acpi_remove(struct platform_device *pdev) diff --git a/drivers/firmware/google/coreboot_table-of.c b/drivers/firmware/google/coreboot_table-of.c index 727acdc83e83..f15bf404c579 100644 --- a/drivers/firmware/google/coreboot_table-of.c +++ b/drivers/firmware/google/coreboot_table-of.c @@ -34,7 +34,7 @@ static int coreboot_table_of_probe(struct platform_device *pdev) if (!ptr) return -ENOMEM; - return coreboot_table_init(ptr); + return coreboot_table_init(&pdev->dev, ptr); } static int coreboot_table_of_remove(struct platform_device *pdev) diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 0019d3ec18dd..19db5709ae28 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -4,6 +4,7 @@ * Module providing coreboot table access. * * Copyright 2017 Google Inc. + * Copyright 2017 Samuel Holland <samuel@sholland.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by @@ -15,37 +16,96 @@ * GNU General Public License for more details. */ +#include <linux/device.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include "coreboot_table.h" -struct coreboot_table_entry { - u32 tag; - u32 size; -}; +#define CB_DEV(d) container_of(d, struct coreboot_device, dev) +#define CB_DRV(d) container_of(d, struct coreboot_driver, drv) static struct coreboot_table_header __iomem *ptr_header; -/* - * This function parses the coreboot table for an entry that contains the base - * address of the given entry tag. The coreboot table consists of a header - * directly followed by a number of small, variable-sized entries, which each - * contain an identifying tag and their length as the first two fields. - */ -int coreboot_table_find(int tag, void *data, size_t data_size) +static int coreboot_bus_match(struct device *dev, struct device_driver *drv) { - struct coreboot_table_header header; - struct coreboot_table_entry entry; - void *ptr_entry; - int i; + struct coreboot_device *device = CB_DEV(dev); + struct coreboot_driver *driver = CB_DRV(drv); - if (!ptr_header) - return -EPROBE_DEFER; + return device->entry.tag == driver->tag; +} +static int coreboot_bus_probe(struct device *dev) +{ + int ret = -ENODEV; + struct coreboot_device *device = CB_DEV(dev); + struct coreboot_driver *driver = CB_DRV(dev->driver); + + if (driver->probe) + ret = driver->probe(device); + + return ret; +} + +static int coreboot_bus_remove(struct device *dev) +{ + int ret = 0; + struct coreboot_device *device = CB_DEV(dev); + struct coreboot_driver *driver = CB_DRV(dev->driver); + + if (driver->remove) + ret = driver->remove(device); + + return ret; +} + +static struct bus_type coreboot_bus_type = { + .name = "coreboot", + .match = coreboot_bus_match, + .probe = coreboot_bus_probe, + .remove = coreboot_bus_remove, +}; + +static int __init coreboot_bus_init(void) +{ + return bus_register(&coreboot_bus_type); +} +module_init(coreboot_bus_init); + +static void coreboot_device_release(struct device *dev) +{ + struct coreboot_device *device = CB_DEV(dev); + + kfree(device); +} + +int coreboot_driver_register(struct coreboot_driver *driver) +{ + driver->drv.bus = &coreboot_bus_type; + + return driver_register(&driver->drv); +} +EXPORT_SYMBOL(coreboot_driver_register); + +void coreboot_driver_unregister(struct coreboot_driver *driver) +{ + driver_unregister(&driver->drv); +} +EXPORT_SYMBOL(coreboot_driver_unregister); + +int coreboot_table_init(struct device *dev, void __iomem *ptr) +{ + int i, ret; + void *ptr_entry; + struct coreboot_device *device; + struct coreboot_table_entry entry; + struct coreboot_table_header header; + + ptr_header = ptr; memcpy_fromio(&header, ptr_header, sizeof(header)); if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { @@ -54,37 +114,41 @@ int coreboot_table_find(int tag, void *data, size_t data_size) } ptr_entry = (void *)ptr_header + header.header_bytes; - for (i = 0; i < header.table_entries; i++) { memcpy_fromio(&entry, ptr_entry, sizeof(entry)); - if (entry.tag == tag) { - if (data_size < entry.size) - return -EINVAL; - memcpy_fromio(data, ptr_entry, entry.size); + device = kzalloc(sizeof(struct device) + entry.size, GFP_KERNEL); + if (!device) { + ret = -ENOMEM; + break; + } + + dev_set_name(&device->dev, "coreboot%d", i); + device->dev.parent = dev; + device->dev.bus = &coreboot_bus_type; + device->dev.release = coreboot_device_release; + memcpy_fromio(&device->entry, ptr_entry, entry.size); - return 0; + ret = device_register(&device->dev); + if (ret) { + put_device(&device->dev); + break; } ptr_entry += entry.size; } - return -ENOENT; -} -EXPORT_SYMBOL(coreboot_table_find); - -int coreboot_table_init(void __iomem *ptr) -{ - ptr_header = ptr; - - return 0; + return ret; } EXPORT_SYMBOL(coreboot_table_init); int coreboot_table_exit(void) { - if (ptr_header) + if (ptr_header) { + bus_unregister(&coreboot_bus_type); iounmap(ptr_header); + ptr_header = NULL; + } return 0; } diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h index 6eff1ae0c5d3..8ad95a94481b 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/google/coreboot_table.h @@ -3,7 +3,9 @@ * * Internal header for coreboot table access. * + * Copyright 2014 Gerd Hoffmann <kraxel@redhat.com> * Copyright 2017 Google Inc. + * Copyright 2017 Samuel Holland <samuel@sholland.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by @@ -20,14 +22,6 @@ #include <linux/io.h> -/* List of coreboot entry structures that is used */ -struct lb_cbmem_ref { - uint32_t tag; - uint32_t size; - - uint64_t cbmem_addr; -}; - /* Coreboot table header structure */ struct coreboot_table_header { char signature[4]; @@ -38,11 +32,67 @@ struct coreboot_table_header { u32 table_entries; }; -/* Retrieve coreboot table entry with tag *tag* and copy it to data */ -int coreboot_table_find(int tag, void *data, size_t data_size); +/* List of coreboot entry structures that is used */ +/* Generic */ +struct coreboot_table_entry { + u32 tag; + u32 size; +}; + +/* Points to a CBMEM entry */ +struct lb_cbmem_ref { + u32 tag; + u32 size; + + u64 cbmem_addr; +}; + +/* Describes framebuffer setup by coreboot */ +struct lb_framebuffer { + u32 tag; + u32 size; + + u64 physical_address; + u32 x_resolution; + u32 y_resolution; + u32 bytes_per_line; + u8 bits_per_pixel; + u8 red_mask_pos; + u8 red_mask_size; + u8 green_mask_pos; + u8 green_mask_size; + u8 blue_mask_pos; + u8 blue_mask_size; + u8 reserved_mask_pos; + u8 reserved_mask_size; +}; + +/* A device, additionally with information from coreboot. */ +struct coreboot_device { + struct device dev; + union { + struct coreboot_table_entry entry; + struct lb_cbmem_ref cbmem_ref; + struct lb_framebuffer framebuffer; + }; +}; + +/* A driver for handling devices described in coreboot tables. */ +struct coreboot_driver { + int (*probe)(struct coreboot_device *); + int (*remove)(struct coreboot_device *); + struct device_driver drv; + u32 tag; +}; + +/* Register a driver that uses the data from a coreboot table. */ +int coreboot_driver_register(struct coreboot_driver *driver); + +/* Unregister a driver that uses the data from a coreboot table. */ +void coreboot_driver_unregister(struct coreboot_driver *driver); /* Initialize coreboot table module given a pointer to iomem */ -int coreboot_table_init(void __iomem *ptr); +int coreboot_table_init(struct device *dev, void __iomem *ptr); /* Cleanup coreboot table module */ int coreboot_table_exit(void); diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c new file mode 100644 index 000000000000..b8b49c067157 --- /dev/null +++ b/drivers/firmware/google/framebuffer-coreboot.c @@ -0,0 +1,115 @@ +/* + * framebuffer-coreboot.c + * + * Memory based framebuffer accessed through coreboot table. + * + * Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com> + * Copyright 2017 Google Inc. + * Copyright 2017 Samuel Holland <samuel@sholland.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_data/simplefb.h> +#include <linux/platform_device.h> + +#include "coreboot_table.h" + +#define CB_TAG_FRAMEBUFFER 0x12 + +static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; + +static int framebuffer_probe(struct coreboot_device *dev) +{ + int i; + u32 length; + struct lb_framebuffer *fb = &dev->framebuffer; + struct platform_device *pdev; + struct resource res; + struct simplefb_platform_data pdata = { + .width = fb->x_resolution, + .height = fb->y_resolution, + .stride = fb->bytes_per_line, + .format = NULL, + }; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (fb->bits_per_pixel == formats[i].bits_per_pixel && + fb->red_mask_pos == formats[i].red.offset && + fb->red_mask_size == formats[i].red.length && + fb->green_mask_pos == formats[i].green.offset && + fb->green_mask_size == formats[i].green.length && + fb->blue_mask_pos == formats[i].blue.offset && + fb->blue_mask_size == formats[i].blue.length && + fb->reserved_mask_pos == formats[i].transp.offset && + fb->reserved_mask_size == formats[i].transp.length) + pdata.format = formats[i].name; + } + if (!pdata.format) + return -ENODEV; + + memset(&res, 0, sizeof(res)); + res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + res.name = "Coreboot Framebuffer"; + res.start = fb->physical_address; + length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line); + res.end = res.start + length - 1; + if (res.end <= res.start) + return -EINVAL; + + pdev = platform_device_register_resndata(&dev->dev, + "simple-framebuffer", 0, + &res, 1, &pdata, + sizeof(pdata)); + if (IS_ERR(pdev)) + pr_warn("coreboot: could not register framebuffer\n"); + else + dev_set_drvdata(&dev->dev, pdev); + + return PTR_ERR_OR_ZERO(pdev); +} + +static int framebuffer_remove(struct coreboot_device *dev) +{ + struct platform_device *pdev = dev_get_drvdata(&dev->dev); + + platform_device_unregister(pdev); + + return 0; +} + +static struct coreboot_driver framebuffer_driver = { + .probe = framebuffer_probe, + .remove = framebuffer_remove, + .drv = { + .name = "framebuffer", + }, + .tag = CB_TAG_FRAMEBUFFER, +}; + +static int __init coreboot_framebuffer_init(void) +{ + return coreboot_driver_register(&framebuffer_driver); +} + +static void coreboot_framebuffer_exit(void) +{ + coreboot_driver_unregister(&framebuffer_driver); +} + +module_init(coreboot_framebuffer_init); +module_exit(coreboot_framebuffer_exit); + +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/google/memconsole-coreboot.c index 52738887735c..b29e10757bfb 100644 --- a/drivers/firmware/google/memconsole-coreboot.c +++ b/drivers/firmware/google/memconsole-coreboot.c @@ -15,9 +15,9 @@ * GNU General Public License for more details. */ +#include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/platform_device.h> #include "memconsole.h" #include "coreboot_table.h" @@ -73,18 +73,19 @@ static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count) return done; } -static int memconsole_coreboot_init(phys_addr_t physaddr) +static int memconsole_probe(struct coreboot_device *dev) { struct cbmem_cons __iomem *tmp_cbmc; - tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB); + tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr, + sizeof(*tmp_cbmc), MEMREMAP_WB); if (!tmp_cbmc) return -ENOMEM; /* Read size only once to prevent overrun attack through /dev/mem. */ cbmem_console_size = tmp_cbmc->size_dont_access_after_boot; - cbmem_console = memremap(physaddr, + cbmem_console = memremap(dev->cbmem_ref.cbmem_addr, cbmem_console_size + sizeof(*cbmem_console), MEMREMAP_WB); memunmap(tmp_cbmc); @@ -93,26 +94,11 @@ static int memconsole_coreboot_init(phys_addr_t physaddr) return -ENOMEM; memconsole_setup(memconsole_coreboot_read); - return 0; -} - -static int memconsole_probe(struct platform_device *pdev) -{ - int ret; - struct lb_cbmem_ref entry; - - ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry)); - if (ret) - return ret; - - ret = memconsole_coreboot_init(entry.cbmem_addr); - if (ret) - return ret; return memconsole_sysfs_init(); } -static int memconsole_remove(struct platform_device *pdev) +static int memconsole_remove(struct coreboot_device *dev) { memconsole_exit(); @@ -122,28 +108,27 @@ static int memconsole_remove(struct platform_device *pdev) return 0; } -static struct platform_driver memconsole_driver = { +static struct coreboot_driver memconsole_driver = { .probe = memconsole_probe, .remove = memconsole_remove, - .driver = { + .drv = { .name = "memconsole", }, + .tag = CB_TAG_CBMEM_CONSOLE, }; -static int __init platform_memconsole_init(void) +static void coreboot_memconsole_exit(void) { - struct platform_device *pdev; - - pdev = platform_device_register_simple("memconsole", -1, NULL, 0); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - platform_driver_register(&memconsole_driver); + coreboot_driver_unregister(&memconsole_driver); +} - return 0; +static int __init coreboot_memconsole_init(void) +{ + return coreboot_driver_register(&memconsole_driver); } -module_init(platform_memconsole_init); +module_exit(coreboot_memconsole_exit); +module_init(coreboot_memconsole_init); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index e4b40f2b4627..e9db895916c3 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -286,20 +286,15 @@ static int vpd_sections_init(phys_addr_t physaddr) return 0; } -static int vpd_probe(struct platform_device *pdev) +static int vpd_probe(struct coreboot_device *dev) { int ret; - struct lb_cbmem_ref entry; - - ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry)); - if (ret) - return ret; vpd_kobj = kobject_create_and_add("vpd", firmware_kobj); if (!vpd_kobj) return -ENOMEM; - ret = vpd_sections_init(entry.cbmem_addr); + ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr); if (ret) { kobject_put(vpd_kobj); return ret; @@ -308,7 +303,7 @@ static int vpd_probe(struct platform_device *pdev) return 0; } -static int vpd_remove(struct platform_device *pdev) +static int vpd_remove(struct coreboot_device *dev) { vpd_section_destroy(&ro_vpd); vpd_section_destroy(&rw_vpd); @@ -318,41 +313,27 @@ static int vpd_remove(struct platform_device *pdev) return 0; } -static struct platform_driver vpd_driver = { +static struct coreboot_driver vpd_driver = { .probe = vpd_probe, .remove = vpd_remove, - .driver = { + .drv = { .name = "vpd", }, + .tag = CB_TAG_VPD, }; -static struct platform_device *vpd_pdev; - -static int __init vpd_platform_init(void) +static int __init coreboot_vpd_init(void) { - int ret; - - ret = platform_driver_register(&vpd_driver); - if (ret) - return ret; - - vpd_pdev = platform_device_register_simple("vpd", -1, NULL, 0); - if (IS_ERR(vpd_pdev)) { - platform_driver_unregister(&vpd_driver); - return PTR_ERR(vpd_pdev); - } - - return 0; + return coreboot_driver_register(&vpd_driver); } -static void __exit vpd_platform_exit(void) +static void __exit coreboot_vpd_exit(void) { - platform_device_unregister(vpd_pdev); - platform_driver_unregister(&vpd_driver); + coreboot_driver_unregister(&vpd_driver); } -module_init(vpd_platform_init); -module_exit(vpd_platform_exit); +module_init(coreboot_vpd_init); +module_exit(coreboot_vpd_exit); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index dfbd894d5bb7..4e24e591ae74 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -147,7 +147,7 @@ static u32 smc(u32 cmd_addr) "smc #0 @ switch to secure world\n" : "=r" (r0) : "r" (r0), "r" (r1), "r" (r2) - : "r3"); + : "r3", "r12"); } while (r0 == QCOM_SCM_INTERRUPTED); return r0; @@ -263,7 +263,7 @@ static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) "smc #0 @ switch to secure world\n" : "=r" (r0) : "r" (r0), "r" (r1), "r" (r2) - : "r3"); + : "r3", "r12"); return r0; } @@ -298,7 +298,7 @@ static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) "smc #0 @ switch to secure world\n" : "=r" (r0) : "r" (r0), "r" (r1), "r" (r2), "r" (r3) - ); + : "r12"); return r0; } @@ -328,7 +328,7 @@ u32 qcom_scm_get_version(void) "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1) : "r" (r0), "r" (r1) - : "r2", "r3"); + : "r2", "r3", "r12"); } while (r0 == QCOM_SCM_INTERRUPTED); version = r1; |