]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/acpi/apei/einj.c
Merge branches 'einj', 'intel_idle', 'misc', 'srat' and 'turbostat-ivb' into release
[linux.git] / drivers / acpi / apei / einj.c
index 31546fd210295f5425e05cfadc313979fba4a8b4..5b898d4dda99930cc9e8282ab3abdc6c3f4d8b0e 100644 (file)
@@ -286,8 +286,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;
@@ -296,14 +317,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));
@@ -324,9 +347,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",
-                      (unsigned long long)trigger_paddr+sizeof(*trigger_tab),
-                      (unsigned long long)trigger_paddr + table_size);
+"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 - 1);
                goto out_rel_header;
        }
        iounmap(trigger_tab);
@@ -347,6 +370,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;
@@ -460,7 +507,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);
@@ -610,10 +657,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;