diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index 6e9f14c0a71b..cb4126051f62 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -120,11 +120,6 @@ int apei_exec_collect_resources(struct apei_exec_context *ctx, struct dentry; struct dentry *apei_get_debugfs_dir(void); -#define apei_estatus_for_each_section(estatus, section) \ - for (section = (struct acpi_hest_generic_data *)(estatus + 1); \ - (void *)section - (void *)estatus < estatus->data_length; \ - section = (void *)(section+1) + section->error_data_length) - static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus) { if (estatus->raw_data_length) diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index ec50c32ea3da..b38737c83a24 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -281,7 +281,7 @@ static struct acpi_generic_address *einj_get_trigger_parameter_region( ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); for (i = 0; i < trigger_tab->entry_count; i++) { if (entry->action == ACPI_EINJ_TRIGGER_ERROR && - entry->instruction == ACPI_EINJ_WRITE_REGISTER_VALUE && + entry->instruction <= ACPI_EINJ_WRITE_REGISTER_VALUE && entry->register_region.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && (entry->register_region.address & param2) == (param1 & param2)) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index d661d452b238..077f9bad6f44 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1157,7 +1157,8 @@ static int ghes_probe(struct platform_device *ghes_dev) generic->header.source_id); goto err_edac_unreg; } - rc = request_irq(ghes->irq, ghes_irq_func, 0, "GHES IRQ", ghes); + rc = request_irq(ghes->irq, ghes_irq_func, IRQF_SHARED, + "GHES IRQ", ghes); if (rc) { pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", generic->header.source_id); @@ -1265,9 +1266,14 @@ static int __init ghes_init(void) if (acpi_disabled) return -ENODEV; - if (hest_disable) { + switch (hest_disable) { + case HEST_NOT_FOUND: + return -ENODEV; + case HEST_DISABLED: pr_info(GHES_PFX "HEST is not enabled!\n"); return -EINVAL; + default: + break; } if (ghes_disable) { diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index 456b488eb1df..9cb74115a43d 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -37,7 +37,7 @@ #define HEST_PFX "HEST: " -bool hest_disable; +int hest_disable; EXPORT_SYMBOL_GPL(hest_disable); /* HEST table parsing */ @@ -213,7 +213,7 @@ err: static int __init setup_hest_disable(char *str) { - hest_disable = 1; + hest_disable = HEST_DISABLED; return 0; } @@ -232,9 +232,10 @@ void __init acpi_hest_init(void) status = acpi_get_table(ACPI_SIG_HEST, 0, (struct acpi_table_header **)&hest_tab); - if (status == AE_NOT_FOUND) - goto err; - else if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + hest_disable = HEST_NOT_FOUND; + return; + } else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(HEST_PFX "Failed to get table, %s\n", msg); rc = -EINVAL; @@ -257,5 +258,5 @@ void __init acpi_hest_init(void) pr_info(HEST_PFX "Table parsing has been initialized.\n"); return; err: - hest_disable = 1; + hest_disable = HEST_DISABLED; } diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index bb542acc0574..037fd537bbf6 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -30,30 +30,13 @@ #include "internal.h" -enum acpi_blacklist_predicates { - all_versions, - less_than_or_equal, - equal, - greater_than_or_equal, -}; - -struct acpi_blacklist_item { - char oem_id[7]; - char oem_table_id[9]; - u32 oem_revision; - char *table; - enum acpi_blacklist_predicates oem_revision_predicate; - char *reason; - u32 is_critical_error; -}; - static struct dmi_system_id acpi_rev_dmi_table[] __initdata; /* * POLICY: If *anything* doesn't work, put it on the blacklist. * If they are critical errors, mark it critical, and abort driver load. */ -static struct acpi_blacklist_item acpi_blacklist[] __initdata = { +static struct acpi_platform_list acpi_blacklist[] __initdata = { /* Compaq Presario 1700 */ {"PTLTD ", " DSDT ", 0x06040000, ACPI_SIG_DSDT, less_than_or_equal, "Multiple problems", 1}, @@ -67,65 +50,27 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = { {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal, "Incorrect _ADR", 1}, - {""} + { } }; int __init acpi_blacklisted(void) { - int i = 0; + int i; int blacklisted = 0; - struct acpi_table_header table_header; - while (acpi_blacklist[i].oem_id[0] != '\0') { - if (acpi_get_table_header(acpi_blacklist[i].table, 0, &table_header)) { - i++; - continue; - } + i = acpi_match_platform_list(acpi_blacklist); + if (i >= 0) { + pr_err(PREFIX "Vendor \"%6.6s\" System \"%8.8s\" Revision 0x%x has a known ACPI BIOS problem.\n", + acpi_blacklist[i].oem_id, + acpi_blacklist[i].oem_table_id, + acpi_blacklist[i].oem_revision); - if (strncmp(acpi_blacklist[i].oem_id, table_header.oem_id, 6)) { - i++; - continue; - } + pr_err(PREFIX "Reason: %s. This is a %s error\n", + acpi_blacklist[i].reason, + (acpi_blacklist[i].data ? + "non-recoverable" : "recoverable")); - if (strncmp - (acpi_blacklist[i].oem_table_id, table_header.oem_table_id, - 8)) { - i++; - continue; - } - - if ((acpi_blacklist[i].oem_revision_predicate == all_versions) - || (acpi_blacklist[i].oem_revision_predicate == - less_than_or_equal - && table_header.oem_revision <= - acpi_blacklist[i].oem_revision) - || (acpi_blacklist[i].oem_revision_predicate == - greater_than_or_equal - && table_header.oem_revision >= - acpi_blacklist[i].oem_revision) - || (acpi_blacklist[i].oem_revision_predicate == equal - && table_header.oem_revision == - acpi_blacklist[i].oem_revision)) { - - printk(KERN_ERR PREFIX - "Vendor \"%6.6s\" System \"%8.8s\" " - "Revision 0x%x has a known ACPI BIOS problem.\n", - acpi_blacklist[i].oem_id, - acpi_blacklist[i].oem_table_id, - acpi_blacklist[i].oem_revision); - - printk(KERN_ERR PREFIX - "Reason: %s. This is a %s error\n", - acpi_blacklist[i].reason, - (acpi_blacklist[i]. - is_critical_error ? "non-recoverable" : - "recoverable")); - - blacklisted = acpi_blacklist[i].is_critical_error; - break; - } else { - i++; - } + blacklisted = acpi_blacklist[i].data; } (void)early_acpi_osi_init(); diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 32d7e43b64c4..78a5a23010ab 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -308,11 +308,13 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); /* * ACPI table sysfs I/F: * /sys/firmware/acpi/tables/ + * /sys/firmware/acpi/tables/data/ * /sys/firmware/acpi/tables/dynamic/ */ static LIST_HEAD(acpi_table_attr_list); static struct kobject *tables_kobj; +static struct kobject *tables_data_kobj; static struct kobject *dynamic_tables_kobj; static struct kobject *hotplug_kobj; @@ -327,6 +329,11 @@ struct acpi_table_attr { struct list_head node; }; +struct acpi_data_attr { + struct bin_attribute attr; + u64 addr; +}; + static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t offset, size_t count) @@ -422,6 +429,70 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) return AE_OK; } +static ssize_t acpi_data_show(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + struct acpi_data_attr *data_attr; + void __iomem *base; + ssize_t rc; + + data_attr = container_of(bin_attr, struct acpi_data_attr, attr); + + base = acpi_os_map_memory(data_attr->addr, data_attr->attr.size); + if (!base) + return -ENOMEM; + rc = memory_read_from_buffer(buf, count, &offset, base, + data_attr->attr.size); + acpi_os_unmap_memory(base, data_attr->attr.size); + + return rc; +} + +static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr) +{ + struct acpi_table_bert *bert = th; + + if (bert->header.length < sizeof(struct acpi_table_bert) || + bert->region_length < sizeof(struct acpi_hest_generic_status)) { + kfree(data_attr); + return -EINVAL; + } + data_attr->addr = bert->address; + data_attr->attr.size = bert->region_length; + data_attr->attr.attr.name = "BERT"; + + return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr); +} + +static struct acpi_data_obj { + char *name; + int (*fn)(void *, struct acpi_data_attr *); +} acpi_data_objs[] = { + { ACPI_SIG_BERT, acpi_bert_data_init }, +}; + +#define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs) + +static int acpi_table_data_init(struct acpi_table_header *th) +{ + struct acpi_data_attr *data_attr; + int i; + + for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) { + if (ACPI_COMPARE_NAME(th->signature, acpi_data_objs[i].name)) { + data_attr = kzalloc(sizeof(*data_attr), GFP_KERNEL); + if (!data_attr) + return -ENOMEM; + sysfs_attr_init(&data_attr->attr.attr); + data_attr->attr.read = acpi_data_show; + data_attr->attr.attr.mode = 0400; + return acpi_data_objs[i].fn(th, data_attr); + } + } + return 0; +} + static int acpi_tables_sysfs_init(void) { struct acpi_table_attr *table_attr; @@ -434,6 +505,10 @@ static int acpi_tables_sysfs_init(void) if (!tables_kobj) goto err; + tables_data_kobj = kobject_create_and_add("data", tables_kobj); + if (!tables_data_kobj) + goto err_tables_data; + dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj); if (!dynamic_tables_kobj) goto err_dynamic_tables; @@ -458,13 +533,17 @@ static int acpi_tables_sysfs_init(void) return ret; } list_add_tail(&table_attr->node, &acpi_table_attr_list); + acpi_table_data_init(table_header); } kobject_uevent(tables_kobj, KOBJ_ADD); + kobject_uevent(tables_data_kobj, KOBJ_ADD); kobject_uevent(dynamic_tables_kobj, KOBJ_ADD); return 0; err_dynamic_tables: + kobject_put(tables_data_kobj); +err_tables_data: kobject_put(tables_kobj); err: return -ENOMEM; diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index b9d956c916f5..0a9e5979aaa9 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -816,3 +816,39 @@ static int __init acpi_backlight(char *str) return 1; } __setup("acpi_backlight=", acpi_backlight); + +/** + * acpi_match_platform_list - Check if the system matches with a given list + * @plat: pointer to acpi_platform_list table terminated by a NULL entry + * + * Return the matched index if the system is found in the platform list. + * Otherwise, return a negative error code. + */ +int acpi_match_platform_list(const struct acpi_platform_list *plat) +{ + struct acpi_table_header hdr; + int idx = 0; + + if (acpi_disabled) + return -ENODEV; + + for (; plat->oem_id[0]; plat++, idx++) { + if (ACPI_FAILURE(acpi_get_table_header(plat->table, 0, &hdr))) + continue; + + if (strncmp(plat->oem_id, hdr.oem_id, ACPI_OEM_ID_SIZE)) + continue; + + if (strncmp(plat->oem_table_id, hdr.oem_table_id, ACPI_OEM_TABLE_ID_SIZE)) + continue; + + if ((plat->pred == all_versions) || + (plat->pred == less_than_or_equal && hdr.oem_revision <= plat->oem_revision) || + (plat->pred == greater_than_or_equal && hdr.oem_revision >= plat->oem_revision) || + (plat->pred == equal && hdr.oem_revision == plat->oem_revision)) + return idx; + } + + return -ENODEV; +} +EXPORT_SYMBOL(acpi_match_platform_list); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 65ee4fcace1f..ad713cd6b364 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2466,39 +2466,31 @@ enum { PPC, }; -struct hw_vendor_info { - u16 valid; - char oem_id[ACPI_OEM_ID_SIZE]; - char oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; - int oem_pwr_table; -}; - /* Hardware vendor-specific info that has its own power management modes */ -static struct hw_vendor_info vendor_info[] __initdata = { - {1, "HP ", "ProLiant", PSS}, - {1, "ORACLE", "X4-2 ", PPC}, - {1, "ORACLE", "X4-2L ", PPC}, - {1, "ORACLE", "X4-2B ", PPC}, - {1, "ORACLE", "X3-2 ", PPC}, - {1, "ORACLE", "X3-2L ", PPC}, - {1, "ORACLE", "X3-2B ", PPC}, - {1, "ORACLE", "X4470M2 ", PPC}, - {1, "ORACLE", "X4270M3 ", PPC}, - {1, "ORACLE", "X4270M2 ", PPC}, - {1, "ORACLE", "X4170M2 ", PPC}, - {1, "ORACLE", "X4170 M3", PPC}, - {1, "ORACLE", "X4275 M3", PPC}, - {1, "ORACLE", "X6-2 ", PPC}, - {1, "ORACLE", "Sudbury ", PPC}, - {0, "", ""}, +static struct acpi_platform_list plat_info[] __initdata = { + {"HP ", "ProLiant", 0, ACPI_SIG_FADT, all_versions, 0, PSS}, + {"ORACLE", "X4-2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4-2L ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4-2B ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X3-2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X3-2L ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X3-2B ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4470M2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4270M3 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4270M2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4170M2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4170 M3", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X4275 M3", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "X6-2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + {"ORACLE", "Sudbury ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, + { } /* End */ }; static bool __init intel_pstate_platform_pwr_mgmt_exists(void) { - struct acpi_table_header hdr; - struct hw_vendor_info *v_info; const struct x86_cpu_id *id; u64 misc_pwr; + int idx; id = x86_match_cpu(intel_pstate_cpu_oob_ids); if (id) { @@ -2507,21 +2499,15 @@ static bool __init intel_pstate_platform_pwr_mgmt_exists(void) return true; } - if (acpi_disabled || - ACPI_FAILURE(acpi_get_table_header(ACPI_SIG_FADT, 0, &hdr))) + idx = acpi_match_platform_list(plat_info); + if (idx < 0) return false; - for (v_info = vendor_info; v_info->valid; v_info++) { - if (!strncmp(hdr.oem_id, v_info->oem_id, ACPI_OEM_ID_SIZE) && - !strncmp(hdr.oem_table_id, v_info->oem_table_id, - ACPI_OEM_TABLE_ID_SIZE)) - switch (v_info->oem_pwr_table) { - case PSS: - return intel_pstate_no_acpi_pss(); - case PPC: - return intel_pstate_has_acpi_ppc() && - (!force_load); - } + switch (plat_info[idx].data) { + case PSS: + return intel_pstate_no_acpi_pss(); + case PPC: + return intel_pstate_has_acpi_ppc() && !force_load; } return false; diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 48a8f69da42a..bf3672a81e49 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -606,7 +606,6 @@ void cper_estatus_print(const char *pfx, const struct acpi_hest_generic_status *estatus) { struct acpi_hest_generic_data *gdata; - unsigned int data_len; int sec_no = 0; char newpfx[64]; __u16 severity; @@ -617,14 +616,10 @@ 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)); - data_len = estatus->data_length; - gdata = (struct acpi_hest_generic_data *)(estatus + 1); snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); - while (data_len >= acpi_hest_get_size(gdata)) { + apei_estatus_for_each_section(estatus, gdata) { cper_estatus_print_section(newpfx, gdata, sec_no); - data_len -= acpi_hest_get_record_size(gdata); - gdata = acpi_hest_get_next(gdata); sec_no++; } } @@ -653,15 +648,12 @@ int cper_estatus_check(const struct acpi_hest_generic_status *estatus) if (rc) return rc; data_len = estatus->data_length; - gdata = (struct acpi_hest_generic_data *)(estatus + 1); - while (data_len >= acpi_hest_get_size(gdata)) { + apei_estatus_for_each_section(estatus, gdata) { gedata_len = acpi_hest_get_error_length(gdata); if (gedata_len > data_len - acpi_hest_get_size(gdata)) return -EINVAL; - data_len -= acpi_hest_get_record_size(gdata); - gdata = acpi_hest_get_next(gdata); } if (data_len) return -EINVAL; diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 76284bb560a6..c46694abea28 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -16,7 +16,13 @@ #ifdef __KERNEL__ -extern bool hest_disable; +enum hest_status { + HEST_ENABLED, + HEST_DISABLED, + HEST_NOT_FOUND, +}; + +extern int hest_disable; extern int erst_disable; #ifdef CONFIG_ACPI_APEI_GHES extern bool ghes_disable; diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index 9f26e01186ae..9061c5c743b3 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -113,6 +113,11 @@ static inline void *acpi_hest_get_next(struct acpi_hest_generic_data *gdata) return (void *)(gdata) + acpi_hest_get_record_size(gdata); } +#define apei_estatus_for_each_section(estatus, section) \ + for (section = (struct acpi_hest_generic_data *)(estatus + 1); \ + (void *)section - (void *)(estatus + 1) < estatus->data_length; \ + section = acpi_hest_get_next(section)) + int ghes_notify_sea(void); #endif /* GHES_H */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 56d6231bedfb..35652fa87272 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -558,6 +558,25 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, #define ACPI_OST_SC_DRIVER_LOAD_FAILURE 0x81 #define ACPI_OST_SC_INSERT_NOT_SUPPORTED 0x82 +enum acpi_predicate { + all_versions, + less_than_or_equal, + equal, + greater_than_or_equal, +}; + +/* Table must be terminted by a NULL entry */ +struct acpi_platform_list { + char oem_id[ACPI_OEM_ID_SIZE+1]; + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE+1]; + u32 oem_revision; + char *table; + enum acpi_predicate pred; + char *reason; + u32 data; +}; +int acpi_match_platform_list(const struct acpi_platform_list *plat); + extern void acpi_early_init(void); extern void acpi_subsystem_init(void);