Loading arch/x86/kernel/e820.c +2 −2 Original line number Diff line number Diff line Loading @@ -714,7 +714,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn) } #endif #ifdef CONFIG_HIBERNATION #ifdef CONFIG_ACPI /** * Mark ACPI NVS memory region, so that we can save/restore it during * hibernation and the subsequent resume. Loading @@ -727,7 +727,7 @@ static int __init e820_mark_nvs_memory(void) struct e820entry *ei = &e820.map[i]; if (ei->type == E820_NVS) suspend_nvs_register(ei->addr, ei->size); acpi_nvs_register(ei->addr, ei->size); } return 0; Loading drivers/acpi/Makefile +2 −1 Original line number Diff line number Diff line Loading @@ -20,11 +20,12 @@ obj-y += acpi.o \ # All the builtin files are in the "acpi." module_param namespace. acpi-y += osl.o utils.o reboot.o acpi-y += atomicio.o acpi-y += nvs.o # sleep related files acpi-y += wakeup.o acpi-y += sleep.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o nvs.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o # Loading drivers/acpi/apei/apei-base.c +43 −5 Original line number Diff line number Diff line Loading @@ -421,6 +421,17 @@ static int apei_resources_merge(struct apei_resources *resources1, return 0; } int apei_resources_add(struct apei_resources *resources, unsigned long start, unsigned long size, bool iomem) { if (iomem) return apei_res_add(&resources->iomem, start, size); else return apei_res_add(&resources->ioport, start, size); } EXPORT_SYMBOL_GPL(apei_resources_add); /* * EINJ has two groups of GARs (EINJ table entry and trigger table * entry), so common resources are subtracted from the trigger table Loading @@ -438,8 +449,19 @@ int apei_resources_sub(struct apei_resources *resources1, } EXPORT_SYMBOL_GPL(apei_resources_sub); static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) { struct apei_resources *resources = data; return apei_res_add(&resources->iomem, start, size); } static int apei_get_nvs_resources(struct apei_resources *resources) { return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); } /* * IO memory/port rersource management mechanism is used to check * IO memory/port resource management mechanism is used to check * whether memory/port area used by GARs conflicts with normal memory * or IO memory/port of devices. */ Loading @@ -448,21 +470,35 @@ int apei_resources_request(struct apei_resources *resources, { struct apei_res *res, *res_bak = NULL; struct resource *r; struct apei_resources nvs_resources; int rc; rc = apei_resources_sub(resources, &apei_resources_all); if (rc) return rc; /* * Some firmware uses ACPI NVS region, that has been marked as * busy, so exclude it from APEI resources to avoid false * conflict. */ apei_resources_init(&nvs_resources); rc = apei_get_nvs_resources(&nvs_resources); if (rc) goto res_fini; rc = apei_resources_sub(resources, &nvs_resources); if (rc) goto res_fini; rc = -EINVAL; list_for_each_entry(res, &resources->iomem, list) { r = request_mem_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX "Can not request iomem region <%016llx-%016llx> for GARs.\n", "Can not request [mem %#010llx-%#010llx] for %s registers\n", (unsigned long long)res->start, (unsigned long long)res->end); (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_iomem; } Loading @@ -472,9 +508,9 @@ int apei_resources_request(struct apei_resources *resources, r = request_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX "Can not request ioport region <%016llx-%016llx> for GARs.\n", "Can not request [io %#06llx-%#06llx] for %s registers\n", (unsigned long long)res->start, (unsigned long long)res->end); (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_ioport; } Loading @@ -500,6 +536,8 @@ err_unmap_iomem: break; release_mem_region(res->start, res->end - res->start); } res_fini: apei_resources_fini(&nvs_resources); return rc; } EXPORT_SYMBOL_GPL(apei_resources_request); Loading drivers/acpi/apei/apei-internal.h +3 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,9 @@ static inline void apei_resources_init(struct apei_resources *resources) } void apei_resources_fini(struct apei_resources *resources); int apei_resources_add(struct apei_resources *resources, unsigned long start, unsigned long size, bool iomem); int apei_resources_sub(struct apei_resources *resources1, struct apei_resources *resources2); int apei_resources_request(struct apei_resources *resources, Loading drivers/acpi/apei/einj.c +56 −10 Original line number Diff line number Diff line Loading @@ -194,8 +194,29 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) return 0; } static struct acpi_generic_address *einj_get_trigger_parameter_region( struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) { int i; struct acpi_whea_header *entry; entry = (struct acpi_whea_header *) ((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->register_region.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && (entry->register_region.address & param2) == (param1 & param2)) return &entry->register_region; entry++; } return NULL; } /* Execute instructions in trigger error action table */ static int __einj_error_trigger(u64 trigger_paddr) static int __einj_error_trigger(u64 trigger_paddr, u32 type, u64 param1, u64 param2) { struct acpi_einj_trigger *trigger_tab = NULL; struct apei_exec_context trigger_ctx; Loading @@ -204,14 +225,16 @@ static int __einj_error_trigger(u64 trigger_paddr) struct resource *r; u32 table_size; int rc = -EIO; struct acpi_generic_address *trigger_param_region = NULL; r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), "APEI EINJ Trigger Table"); if (!r) { pr_err(EINJ_PFX "Can not request iomem region <%016llx-%016llx> for Trigger table.\n", "Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr+sizeof(*trigger_tab)); (unsigned long long)trigger_paddr + sizeof(*trigger_tab) - 1); goto out; } trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); Loading @@ -232,9 +255,9 @@ static int __einj_error_trigger(u64 trigger_paddr) "APEI EINJ Trigger Table"); if (!r) { pr_err(EINJ_PFX "Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n", "Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", (unsigned long long)trigger_paddr + sizeof(*trigger_tab), (unsigned long long)trigger_paddr + table_size); (unsigned long long)trigger_paddr + table_size - 1); goto out_rel_header; } iounmap(trigger_tab); Loading @@ -255,6 +278,30 @@ static int __einj_error_trigger(u64 trigger_paddr) rc = apei_resources_sub(&trigger_resources, &einj_resources); if (rc) goto out_fini; /* * Some firmware will access target address specified in * param1 to trigger the error when injecting memory error. * This will cause resource conflict with regular memory. So * remove it from trigger table resources. */ if (param_extension && (type & 0x0038) && param2) { struct apei_resources addr_resources; apei_resources_init(&addr_resources); trigger_param_region = einj_get_trigger_parameter_region( trigger_tab, param1, param2); if (trigger_param_region) { rc = apei_resources_add(&addr_resources, trigger_param_region->address, trigger_param_region->bit_width/8, true); if (rc) goto out_fini; rc = apei_resources_sub(&trigger_resources, &addr_resources); } apei_resources_fini(&addr_resources); if (rc) goto out_fini; } rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); if (rc) goto out_fini; Loading Loading @@ -324,7 +371,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) if (rc) return rc; trigger_paddr = apei_exec_ctx_get_output(&ctx); rc = __einj_error_trigger(trigger_paddr); rc = __einj_error_trigger(trigger_paddr, type, param1, param2); if (rc) return rc; rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); Loading Loading @@ -465,10 +512,9 @@ static int __init einj_init(void) status = acpi_get_table(ACPI_SIG_EINJ, 0, (struct acpi_table_header **)&einj_tab); if (status == AE_NOT_FOUND) { pr_info(EINJ_PFX "Table is not found!\n"); if (status == AE_NOT_FOUND) return -ENODEV; } else if (ACPI_FAILURE(status)) { else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(EINJ_PFX "Failed to get table, %s\n", msg); return -EINVAL; Loading Loading
arch/x86/kernel/e820.c +2 −2 Original line number Diff line number Diff line Loading @@ -714,7 +714,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn) } #endif #ifdef CONFIG_HIBERNATION #ifdef CONFIG_ACPI /** * Mark ACPI NVS memory region, so that we can save/restore it during * hibernation and the subsequent resume. Loading @@ -727,7 +727,7 @@ static int __init e820_mark_nvs_memory(void) struct e820entry *ei = &e820.map[i]; if (ei->type == E820_NVS) suspend_nvs_register(ei->addr, ei->size); acpi_nvs_register(ei->addr, ei->size); } return 0; Loading
drivers/acpi/Makefile +2 −1 Original line number Diff line number Diff line Loading @@ -20,11 +20,12 @@ obj-y += acpi.o \ # All the builtin files are in the "acpi." module_param namespace. acpi-y += osl.o utils.o reboot.o acpi-y += atomicio.o acpi-y += nvs.o # sleep related files acpi-y += wakeup.o acpi-y += sleep.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o nvs.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o # Loading
drivers/acpi/apei/apei-base.c +43 −5 Original line number Diff line number Diff line Loading @@ -421,6 +421,17 @@ static int apei_resources_merge(struct apei_resources *resources1, return 0; } int apei_resources_add(struct apei_resources *resources, unsigned long start, unsigned long size, bool iomem) { if (iomem) return apei_res_add(&resources->iomem, start, size); else return apei_res_add(&resources->ioport, start, size); } EXPORT_SYMBOL_GPL(apei_resources_add); /* * EINJ has two groups of GARs (EINJ table entry and trigger table * entry), so common resources are subtracted from the trigger table Loading @@ -438,8 +449,19 @@ int apei_resources_sub(struct apei_resources *resources1, } EXPORT_SYMBOL_GPL(apei_resources_sub); static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) { struct apei_resources *resources = data; return apei_res_add(&resources->iomem, start, size); } static int apei_get_nvs_resources(struct apei_resources *resources) { return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); } /* * IO memory/port rersource management mechanism is used to check * IO memory/port resource management mechanism is used to check * whether memory/port area used by GARs conflicts with normal memory * or IO memory/port of devices. */ Loading @@ -448,21 +470,35 @@ int apei_resources_request(struct apei_resources *resources, { struct apei_res *res, *res_bak = NULL; struct resource *r; struct apei_resources nvs_resources; int rc; rc = apei_resources_sub(resources, &apei_resources_all); if (rc) return rc; /* * Some firmware uses ACPI NVS region, that has been marked as * busy, so exclude it from APEI resources to avoid false * conflict. */ apei_resources_init(&nvs_resources); rc = apei_get_nvs_resources(&nvs_resources); if (rc) goto res_fini; rc = apei_resources_sub(resources, &nvs_resources); if (rc) goto res_fini; rc = -EINVAL; list_for_each_entry(res, &resources->iomem, list) { r = request_mem_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX "Can not request iomem region <%016llx-%016llx> for GARs.\n", "Can not request [mem %#010llx-%#010llx] for %s registers\n", (unsigned long long)res->start, (unsigned long long)res->end); (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_iomem; } Loading @@ -472,9 +508,9 @@ int apei_resources_request(struct apei_resources *resources, r = request_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX "Can not request ioport region <%016llx-%016llx> for GARs.\n", "Can not request [io %#06llx-%#06llx] for %s registers\n", (unsigned long long)res->start, (unsigned long long)res->end); (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_ioport; } Loading @@ -500,6 +536,8 @@ err_unmap_iomem: break; release_mem_region(res->start, res->end - res->start); } res_fini: apei_resources_fini(&nvs_resources); return rc; } EXPORT_SYMBOL_GPL(apei_resources_request); Loading
drivers/acpi/apei/apei-internal.h +3 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,9 @@ static inline void apei_resources_init(struct apei_resources *resources) } void apei_resources_fini(struct apei_resources *resources); int apei_resources_add(struct apei_resources *resources, unsigned long start, unsigned long size, bool iomem); int apei_resources_sub(struct apei_resources *resources1, struct apei_resources *resources2); int apei_resources_request(struct apei_resources *resources, Loading
drivers/acpi/apei/einj.c +56 −10 Original line number Diff line number Diff line Loading @@ -194,8 +194,29 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) return 0; } static struct acpi_generic_address *einj_get_trigger_parameter_region( struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) { int i; struct acpi_whea_header *entry; entry = (struct acpi_whea_header *) ((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->register_region.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && (entry->register_region.address & param2) == (param1 & param2)) return &entry->register_region; entry++; } return NULL; } /* Execute instructions in trigger error action table */ static int __einj_error_trigger(u64 trigger_paddr) static int __einj_error_trigger(u64 trigger_paddr, u32 type, u64 param1, u64 param2) { struct acpi_einj_trigger *trigger_tab = NULL; struct apei_exec_context trigger_ctx; Loading @@ -204,14 +225,16 @@ static int __einj_error_trigger(u64 trigger_paddr) struct resource *r; u32 table_size; int rc = -EIO; struct acpi_generic_address *trigger_param_region = NULL; r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), "APEI EINJ Trigger Table"); if (!r) { pr_err(EINJ_PFX "Can not request iomem region <%016llx-%016llx> for Trigger table.\n", "Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr+sizeof(*trigger_tab)); (unsigned long long)trigger_paddr + sizeof(*trigger_tab) - 1); goto out; } trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); Loading @@ -232,9 +255,9 @@ static int __einj_error_trigger(u64 trigger_paddr) "APEI EINJ Trigger Table"); if (!r) { pr_err(EINJ_PFX "Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n", "Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", (unsigned long long)trigger_paddr + sizeof(*trigger_tab), (unsigned long long)trigger_paddr + table_size); (unsigned long long)trigger_paddr + table_size - 1); goto out_rel_header; } iounmap(trigger_tab); Loading @@ -255,6 +278,30 @@ static int __einj_error_trigger(u64 trigger_paddr) rc = apei_resources_sub(&trigger_resources, &einj_resources); if (rc) goto out_fini; /* * Some firmware will access target address specified in * param1 to trigger the error when injecting memory error. * This will cause resource conflict with regular memory. So * remove it from trigger table resources. */ if (param_extension && (type & 0x0038) && param2) { struct apei_resources addr_resources; apei_resources_init(&addr_resources); trigger_param_region = einj_get_trigger_parameter_region( trigger_tab, param1, param2); if (trigger_param_region) { rc = apei_resources_add(&addr_resources, trigger_param_region->address, trigger_param_region->bit_width/8, true); if (rc) goto out_fini; rc = apei_resources_sub(&trigger_resources, &addr_resources); } apei_resources_fini(&addr_resources); if (rc) goto out_fini; } rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); if (rc) goto out_fini; Loading Loading @@ -324,7 +371,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) if (rc) return rc; trigger_paddr = apei_exec_ctx_get_output(&ctx); rc = __einj_error_trigger(trigger_paddr); rc = __einj_error_trigger(trigger_paddr, type, param1, param2); if (rc) return rc; rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); Loading Loading @@ -465,10 +512,9 @@ static int __init einj_init(void) status = acpi_get_table(ACPI_SIG_EINJ, 0, (struct acpi_table_header **)&einj_tab); if (status == AE_NOT_FOUND) { pr_info(EINJ_PFX "Table is not found!\n"); if (status == AE_NOT_FOUND) return -ENODEV; } else if (ACPI_FAILURE(status)) { else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(EINJ_PFX "Failed to get table, %s\n", msg); return -EINVAL; Loading