// SPDX-License-Identifier: GPL-2.0-only /* * apei-base.c - ACPI Platform Error Interface (APEI) supporting * infrastructure * * APEI allows to report errors (for example from the chipset) to * the operating system. This improves NMI handling especially. In * addition it supports error serialization and error injection. * * For more information about APEI, please refer to ACPI Specification * version 4.0, chapter 17. * * This file has Common functions used by more than one APEI table, * including framework of interpreter for ERST and EINJ; resource * management for APEI registers. * * Copyright (C) 2009, Intel Corp. * Author: Huang Ying <ying.huang@intel.com>
*/
/* * Interpret the specified action. Go through whole action table, * execute all instructions belong to the action.
*/ int __apei_exec_run(struct apei_exec_context *ctx, u8 action, bool optional)
{ int rc = -ENOENT;
u32 i, ip; struct acpi_whea_header *entry;
apei_exec_ins_func_t run;
ctx->ip = 0;
/* * "ip" is the instruction pointer of current instruction, * "ctx->ip" specifies the next instruction to executed, * instruction "run" function may change the "ctx->ip" to * implement "goto" semantics.
*/
rewind:
ip = 0; for (i = 0; i < ctx->entries; i++) {
entry = &ctx->action_table[i]; if (entry->action != action) continue; if (ip == ctx->ip) { if (entry->instruction >= ctx->instructions ||
!ctx->ins_table[entry->instruction].run) {
pr_warn(FW_WARN APEI_PFX "Invalid action table, unknown instruction type: %d\n",
entry->instruction); return -EINVAL;
}
run = ctx->ins_table[entry->instruction].run;
rc = run(ctx, entry); if (rc < 0) return rc; elseif (rc != APEI_EXEC_SET_IP)
ctx->ip++;
}
ip++; if (ctx->ip < ip) goto rewind;
}
if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) return apei_map_generic_address(&entry->register_region);
return 0;
}
/* * Pre-map all GARs in action table to make it possible to access them * in NMI handler.
*/ int apei_exec_pre_map_gars(struct apei_exec_context *ctx)
{ int rc, end;
if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
apei_unmap_generic_address(&entry->register_region);
return 0;
}
/* Post-unmap all GAR in action table. */ int apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
{ return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
NULL, NULL);
}
EXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars);
/* * Resource management for GARs in APEI
*/ struct apei_res { struct list_head list; unsignedlong start; unsignedlong end;
};
/* * EINJ has two groups of GARs (EINJ table entry and trigger table * entry), so common resources are subtracted from the trigger table * resources before the second requesting.
*/ int apei_resources_sub(struct apei_resources *resources1, struct apei_resources *resources2)
{ int rc;
/* * 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.
*/ int apei_resources_request(struct apei_resources *resources, constchar *desc)
{ struct apei_res *res, *res_bak = NULL; struct resource *r; struct apei_resources nvs_resources, arch_res; 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 nvs_res_fini;
rc = apei_resources_sub(resources, &nvs_resources); if (rc) goto nvs_res_fini;
if (arch_apei_filter_addr) {
apei_resources_init(&arch_res);
rc = apei_get_arch_resources(&arch_res); if (rc) goto arch_res_fini;
rc = apei_resources_sub(resources, &arch_res); if (rc) goto arch_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 [mem %#010llx-%#010llx] for %s registers\n",
(unsignedlonglong)res->start,
(unsignedlonglong)res->end - 1, desc);
res_bak = res; goto err_unmap_iomem;
}
}
list_for_each_entry(res, &resources->ioport, list) {
r = request_region(res->start, res->end - res->start, desc); if (!r) {
pr_err(APEI_PFX "Can not request [io %#06llx-%#06llx] for %s registers\n",
(unsignedlonglong)res->start,
(unsignedlonglong)res->end - 1, desc);
res_bak = res; goto err_unmap_ioport;
}
}
rc = apei_resources_merge(&apei_resources_all, resources); if (rc) {
pr_err(APEI_PFX "Fail to merge resources!\n"); goto err_unmap_ioport;
}
rc = apei_resources_sub(&apei_resources_all, resources); if (rc)
pr_err(APEI_PFX "Fail to sub resources!\n");
}
EXPORT_SYMBOL_GPL(apei_resources_release);
/* read GAR in interrupt (including NMI) or process context */ int apei_read(u64 *val, struct acpi_generic_address *reg)
{ int rc;
u32 access_bit_width;
u64 address;
acpi_status status;
rc = apei_check_gar(reg, &address, &access_bit_width); if (rc) return rc;
*val = 0; switch(reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY:
status = acpi_os_read_memory((acpi_physical_address) address,
val, access_bit_width); if (ACPI_FAILURE(status)) return -EIO; break; case ACPI_ADR_SPACE_SYSTEM_IO:
status = acpi_os_read_port(address, (u32 *)val,
access_bit_width); if (ACPI_FAILURE(status)) return -EIO; break; default: return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(apei_read);
/* write GAR in interrupt (including NMI) or process context */ int apei_write(u64 val, struct acpi_generic_address *reg)
{ int rc;
u32 access_bit_width;
u64 address;
acpi_status status;
rc = apei_check_gar(reg, &address, &access_bit_width); if (rc) return rc;
switch (reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY:
status = acpi_os_write_memory((acpi_physical_address) address,
val, access_bit_width); if (ACPI_FAILURE(status)) return -EIO; break; case ACPI_ADR_SPACE_SYSTEM_IO:
status = acpi_os_write_port(address, val, access_bit_width); if (ACPI_FAILURE(status)) return -EIO; break; default: return -EINVAL;
}
/* * Same register may be used by multiple instructions in GARs, so * resources are collected before requesting.
*/ int apei_exec_collect_resources(struct apei_exec_context *ctx, struct apei_resources *resources)
{ return apei_exec_for_each_entry(ctx, collect_res_callback,
resources, NULL);
}
EXPORT_SYMBOL_GPL(apei_exec_collect_resources);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.