Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * apei-base.c - ACPI Platform Error Interface (APEI) supporting
0004  * infrastructure
0005  *
0006  * APEI allows to report errors (for example from the chipset) to
0007  * the operating system. This improves NMI handling especially. In
0008  * addition it supports error serialization and error injection.
0009  *
0010  * For more information about APEI, please refer to ACPI Specification
0011  * version 4.0, chapter 17.
0012  *
0013  * This file has Common functions used by more than one APEI table,
0014  * including framework of interpreter for ERST and EINJ; resource
0015  * management for APEI registers.
0016  *
0017  * Copyright (C) 2009, Intel Corp.
0018  *  Author: Huang Ying <ying.huang@intel.com>
0019  */
0020 
0021 #include <linux/kernel.h>
0022 #include <linux/module.h>
0023 #include <linux/init.h>
0024 #include <linux/acpi.h>
0025 #include <linux/slab.h>
0026 #include <linux/io.h>
0027 #include <linux/kref.h>
0028 #include <linux/rculist.h>
0029 #include <linux/interrupt.h>
0030 #include <linux/debugfs.h>
0031 #include <asm/unaligned.h>
0032 
0033 #include "apei-internal.h"
0034 
0035 #define APEI_PFX "APEI: "
0036 
0037 /*
0038  * APEI ERST (Error Record Serialization Table) and EINJ (Error
0039  * INJection) interpreter framework.
0040  */
0041 
0042 #define APEI_EXEC_PRESERVE_REGISTER 0x1
0043 
0044 void apei_exec_ctx_init(struct apei_exec_context *ctx,
0045             struct apei_exec_ins_type *ins_table,
0046             u32 instructions,
0047             struct acpi_whea_header *action_table,
0048             u32 entries)
0049 {
0050     ctx->ins_table = ins_table;
0051     ctx->instructions = instructions;
0052     ctx->action_table = action_table;
0053     ctx->entries = entries;
0054 }
0055 EXPORT_SYMBOL_GPL(apei_exec_ctx_init);
0056 
0057 int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
0058 {
0059     int rc;
0060 
0061     rc = apei_read(val, &entry->register_region);
0062     if (rc)
0063         return rc;
0064     *val >>= entry->register_region.bit_offset;
0065     *val &= entry->mask;
0066 
0067     return 0;
0068 }
0069 
0070 int apei_exec_read_register(struct apei_exec_context *ctx,
0071                 struct acpi_whea_header *entry)
0072 {
0073     int rc;
0074     u64 val = 0;
0075 
0076     rc = __apei_exec_read_register(entry, &val);
0077     if (rc)
0078         return rc;
0079     ctx->value = val;
0080 
0081     return 0;
0082 }
0083 EXPORT_SYMBOL_GPL(apei_exec_read_register);
0084 
0085 int apei_exec_read_register_value(struct apei_exec_context *ctx,
0086                   struct acpi_whea_header *entry)
0087 {
0088     int rc;
0089 
0090     rc = apei_exec_read_register(ctx, entry);
0091     if (rc)
0092         return rc;
0093     ctx->value = (ctx->value == entry->value);
0094 
0095     return 0;
0096 }
0097 EXPORT_SYMBOL_GPL(apei_exec_read_register_value);
0098 
0099 int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
0100 {
0101     int rc;
0102 
0103     val &= entry->mask;
0104     val <<= entry->register_region.bit_offset;
0105     if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
0106         u64 valr = 0;
0107         rc = apei_read(&valr, &entry->register_region);
0108         if (rc)
0109             return rc;
0110         valr &= ~(entry->mask << entry->register_region.bit_offset);
0111         val |= valr;
0112     }
0113     rc = apei_write(val, &entry->register_region);
0114 
0115     return rc;
0116 }
0117 
0118 int apei_exec_write_register(struct apei_exec_context *ctx,
0119                  struct acpi_whea_header *entry)
0120 {
0121     return __apei_exec_write_register(entry, ctx->value);
0122 }
0123 EXPORT_SYMBOL_GPL(apei_exec_write_register);
0124 
0125 int apei_exec_write_register_value(struct apei_exec_context *ctx,
0126                    struct acpi_whea_header *entry)
0127 {
0128     int rc;
0129 
0130     ctx->value = entry->value;
0131     rc = apei_exec_write_register(ctx, entry);
0132 
0133     return rc;
0134 }
0135 EXPORT_SYMBOL_GPL(apei_exec_write_register_value);
0136 
0137 int apei_exec_noop(struct apei_exec_context *ctx,
0138            struct acpi_whea_header *entry)
0139 {
0140     return 0;
0141 }
0142 EXPORT_SYMBOL_GPL(apei_exec_noop);
0143 
0144 /*
0145  * Interpret the specified action. Go through whole action table,
0146  * execute all instructions belong to the action.
0147  */
0148 int __apei_exec_run(struct apei_exec_context *ctx, u8 action,
0149             bool optional)
0150 {
0151     int rc = -ENOENT;
0152     u32 i, ip;
0153     struct acpi_whea_header *entry;
0154     apei_exec_ins_func_t run;
0155 
0156     ctx->ip = 0;
0157 
0158     /*
0159      * "ip" is the instruction pointer of current instruction,
0160      * "ctx->ip" specifies the next instruction to executed,
0161      * instruction "run" function may change the "ctx->ip" to
0162      * implement "goto" semantics.
0163      */
0164 rewind:
0165     ip = 0;
0166     for (i = 0; i < ctx->entries; i++) {
0167         entry = &ctx->action_table[i];
0168         if (entry->action != action)
0169             continue;
0170         if (ip == ctx->ip) {
0171             if (entry->instruction >= ctx->instructions ||
0172                 !ctx->ins_table[entry->instruction].run) {
0173                 pr_warn(FW_WARN APEI_PFX
0174                     "Invalid action table, unknown instruction type: %d\n",
0175                     entry->instruction);
0176                 return -EINVAL;
0177             }
0178             run = ctx->ins_table[entry->instruction].run;
0179             rc = run(ctx, entry);
0180             if (rc < 0)
0181                 return rc;
0182             else if (rc != APEI_EXEC_SET_IP)
0183                 ctx->ip++;
0184         }
0185         ip++;
0186         if (ctx->ip < ip)
0187             goto rewind;
0188     }
0189 
0190     return !optional && rc < 0 ? rc : 0;
0191 }
0192 EXPORT_SYMBOL_GPL(__apei_exec_run);
0193 
0194 typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
0195                       struct acpi_whea_header *entry,
0196                       void *data);
0197 
0198 static int apei_exec_for_each_entry(struct apei_exec_context *ctx,
0199                     apei_exec_entry_func_t func,
0200                     void *data,
0201                     int *end)
0202 {
0203     u8 ins;
0204     int i, rc;
0205     struct acpi_whea_header *entry;
0206     struct apei_exec_ins_type *ins_table = ctx->ins_table;
0207 
0208     for (i = 0; i < ctx->entries; i++) {
0209         entry = ctx->action_table + i;
0210         ins = entry->instruction;
0211         if (end)
0212             *end = i;
0213         if (ins >= ctx->instructions || !ins_table[ins].run) {
0214             pr_warn(FW_WARN APEI_PFX
0215                 "Invalid action table, unknown instruction type: %d\n",
0216                 ins);
0217             return -EINVAL;
0218         }
0219         rc = func(ctx, entry, data);
0220         if (rc)
0221             return rc;
0222     }
0223 
0224     return 0;
0225 }
0226 
0227 static int pre_map_gar_callback(struct apei_exec_context *ctx,
0228                 struct acpi_whea_header *entry,
0229                 void *data)
0230 {
0231     u8 ins = entry->instruction;
0232 
0233     if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
0234         return apei_map_generic_address(&entry->register_region);
0235 
0236     return 0;
0237 }
0238 
0239 /*
0240  * Pre-map all GARs in action table to make it possible to access them
0241  * in NMI handler.
0242  */
0243 int apei_exec_pre_map_gars(struct apei_exec_context *ctx)
0244 {
0245     int rc, end;
0246 
0247     rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback,
0248                       NULL, &end);
0249     if (rc) {
0250         struct apei_exec_context ctx_unmap;
0251         memcpy(&ctx_unmap, ctx, sizeof(*ctx));
0252         ctx_unmap.entries = end;
0253         apei_exec_post_unmap_gars(&ctx_unmap);
0254     }
0255 
0256     return rc;
0257 }
0258 EXPORT_SYMBOL_GPL(apei_exec_pre_map_gars);
0259 
0260 static int post_unmap_gar_callback(struct apei_exec_context *ctx,
0261                    struct acpi_whea_header *entry,
0262                    void *data)
0263 {
0264     u8 ins = entry->instruction;
0265 
0266     if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
0267         apei_unmap_generic_address(&entry->register_region);
0268 
0269     return 0;
0270 }
0271 
0272 /* Post-unmap all GAR in action table. */
0273 int apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
0274 {
0275     return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
0276                     NULL, NULL);
0277 }
0278 EXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars);
0279 
0280 /*
0281  * Resource management for GARs in APEI
0282  */
0283 struct apei_res {
0284     struct list_head list;
0285     unsigned long start;
0286     unsigned long end;
0287 };
0288 
0289 /* Collect all resources requested, to avoid conflict */
0290 static struct apei_resources apei_resources_all = {
0291     .iomem = LIST_HEAD_INIT(apei_resources_all.iomem),
0292     .ioport = LIST_HEAD_INIT(apei_resources_all.ioport),
0293 };
0294 
0295 static int apei_res_add(struct list_head *res_list,
0296             unsigned long start, unsigned long size)
0297 {
0298     struct apei_res *res, *resn, *res_ins = NULL;
0299     unsigned long end = start + size;
0300 
0301     if (end <= start)
0302         return 0;
0303 repeat:
0304     list_for_each_entry_safe(res, resn, res_list, list) {
0305         if (res->start > end || res->end < start)
0306             continue;
0307         else if (end <= res->end && start >= res->start) {
0308             kfree(res_ins);
0309             return 0;
0310         }
0311         list_del(&res->list);
0312         res->start = start = min(res->start, start);
0313         res->end = end = max(res->end, end);
0314         kfree(res_ins);
0315         res_ins = res;
0316         goto repeat;
0317     }
0318 
0319     if (res_ins)
0320         list_add(&res_ins->list, res_list);
0321     else {
0322         res_ins = kmalloc(sizeof(*res_ins), GFP_KERNEL);
0323         if (!res_ins)
0324             return -ENOMEM;
0325         res_ins->start = start;
0326         res_ins->end = end;
0327         list_add(&res_ins->list, res_list);
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 static int apei_res_sub(struct list_head *res_list1,
0334             struct list_head *res_list2)
0335 {
0336     struct apei_res *res1, *resn1, *res2, *res;
0337     res1 = list_entry(res_list1->next, struct apei_res, list);
0338     resn1 = list_entry(res1->list.next, struct apei_res, list);
0339     while (&res1->list != res_list1) {
0340         list_for_each_entry(res2, res_list2, list) {
0341             if (res1->start >= res2->end ||
0342                 res1->end <= res2->start)
0343                 continue;
0344             else if (res1->end <= res2->end &&
0345                  res1->start >= res2->start) {
0346                 list_del(&res1->list);
0347                 kfree(res1);
0348                 break;
0349             } else if (res1->end > res2->end &&
0350                    res1->start < res2->start) {
0351                 res = kmalloc(sizeof(*res), GFP_KERNEL);
0352                 if (!res)
0353                     return -ENOMEM;
0354                 res->start = res2->end;
0355                 res->end = res1->end;
0356                 res1->end = res2->start;
0357                 list_add(&res->list, &res1->list);
0358                 resn1 = res;
0359             } else {
0360                 if (res1->start < res2->start)
0361                     res1->end = res2->start;
0362                 else
0363                     res1->start = res2->end;
0364             }
0365         }
0366         res1 = resn1;
0367         resn1 = list_entry(resn1->list.next, struct apei_res, list);
0368     }
0369 
0370     return 0;
0371 }
0372 
0373 static void apei_res_clean(struct list_head *res_list)
0374 {
0375     struct apei_res *res, *resn;
0376 
0377     list_for_each_entry_safe(res, resn, res_list, list) {
0378         list_del(&res->list);
0379         kfree(res);
0380     }
0381 }
0382 
0383 void apei_resources_fini(struct apei_resources *resources)
0384 {
0385     apei_res_clean(&resources->iomem);
0386     apei_res_clean(&resources->ioport);
0387 }
0388 EXPORT_SYMBOL_GPL(apei_resources_fini);
0389 
0390 static int apei_resources_merge(struct apei_resources *resources1,
0391                 struct apei_resources *resources2)
0392 {
0393     int rc;
0394     struct apei_res *res;
0395 
0396     list_for_each_entry(res, &resources2->iomem, list) {
0397         rc = apei_res_add(&resources1->iomem, res->start,
0398                   res->end - res->start);
0399         if (rc)
0400             return rc;
0401     }
0402     list_for_each_entry(res, &resources2->ioport, list) {
0403         rc = apei_res_add(&resources1->ioport, res->start,
0404                   res->end - res->start);
0405         if (rc)
0406             return rc;
0407     }
0408 
0409     return 0;
0410 }
0411 
0412 int apei_resources_add(struct apei_resources *resources,
0413                unsigned long start, unsigned long size,
0414                bool iomem)
0415 {
0416     if (iomem)
0417         return apei_res_add(&resources->iomem, start, size);
0418     else
0419         return apei_res_add(&resources->ioport, start, size);
0420 }
0421 EXPORT_SYMBOL_GPL(apei_resources_add);
0422 
0423 /*
0424  * EINJ has two groups of GARs (EINJ table entry and trigger table
0425  * entry), so common resources are subtracted from the trigger table
0426  * resources before the second requesting.
0427  */
0428 int apei_resources_sub(struct apei_resources *resources1,
0429                struct apei_resources *resources2)
0430 {
0431     int rc;
0432 
0433     rc = apei_res_sub(&resources1->iomem, &resources2->iomem);
0434     if (rc)
0435         return rc;
0436     return apei_res_sub(&resources1->ioport, &resources2->ioport);
0437 }
0438 EXPORT_SYMBOL_GPL(apei_resources_sub);
0439 
0440 static int apei_get_res_callback(__u64 start, __u64 size, void *data)
0441 {
0442     struct apei_resources *resources = data;
0443     return apei_res_add(&resources->iomem, start, size);
0444 }
0445 
0446 static int apei_get_nvs_resources(struct apei_resources *resources)
0447 {
0448     return acpi_nvs_for_each_region(apei_get_res_callback, resources);
0449 }
0450 
0451 int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
0452                      void *data), void *data);
0453 static int apei_get_arch_resources(struct apei_resources *resources)
0454 
0455 {
0456     return arch_apei_filter_addr(apei_get_res_callback, resources);
0457 }
0458 
0459 /*
0460  * IO memory/port resource management mechanism is used to check
0461  * whether memory/port area used by GARs conflicts with normal memory
0462  * or IO memory/port of devices.
0463  */
0464 int apei_resources_request(struct apei_resources *resources,
0465                const char *desc)
0466 {
0467     struct apei_res *res, *res_bak = NULL;
0468     struct resource *r;
0469     struct apei_resources nvs_resources, arch_res;
0470     int rc;
0471 
0472     rc = apei_resources_sub(resources, &apei_resources_all);
0473     if (rc)
0474         return rc;
0475 
0476     /*
0477      * Some firmware uses ACPI NVS region, that has been marked as
0478      * busy, so exclude it from APEI resources to avoid false
0479      * conflict.
0480      */
0481     apei_resources_init(&nvs_resources);
0482     rc = apei_get_nvs_resources(&nvs_resources);
0483     if (rc)
0484         goto nvs_res_fini;
0485     rc = apei_resources_sub(resources, &nvs_resources);
0486     if (rc)
0487         goto nvs_res_fini;
0488 
0489     if (arch_apei_filter_addr) {
0490         apei_resources_init(&arch_res);
0491         rc = apei_get_arch_resources(&arch_res);
0492         if (rc)
0493             goto arch_res_fini;
0494         rc = apei_resources_sub(resources, &arch_res);
0495         if (rc)
0496             goto arch_res_fini;
0497     }
0498 
0499     rc = -EINVAL;
0500     list_for_each_entry(res, &resources->iomem, list) {
0501         r = request_mem_region(res->start, res->end - res->start,
0502                        desc);
0503         if (!r) {
0504             pr_err(APEI_PFX
0505         "Can not request [mem %#010llx-%#010llx] for %s registers\n",
0506                    (unsigned long long)res->start,
0507                    (unsigned long long)res->end - 1, desc);
0508             res_bak = res;
0509             goto err_unmap_iomem;
0510         }
0511     }
0512 
0513     list_for_each_entry(res, &resources->ioport, list) {
0514         r = request_region(res->start, res->end - res->start, desc);
0515         if (!r) {
0516             pr_err(APEI_PFX
0517         "Can not request [io  %#06llx-%#06llx] for %s registers\n",
0518                    (unsigned long long)res->start,
0519                    (unsigned long long)res->end - 1, desc);
0520             res_bak = res;
0521             goto err_unmap_ioport;
0522         }
0523     }
0524 
0525     rc = apei_resources_merge(&apei_resources_all, resources);
0526     if (rc) {
0527         pr_err(APEI_PFX "Fail to merge resources!\n");
0528         goto err_unmap_ioport;
0529     }
0530 
0531     goto arch_res_fini;
0532 
0533 err_unmap_ioport:
0534     list_for_each_entry(res, &resources->ioport, list) {
0535         if (res == res_bak)
0536             break;
0537         release_region(res->start, res->end - res->start);
0538     }
0539     res_bak = NULL;
0540 err_unmap_iomem:
0541     list_for_each_entry(res, &resources->iomem, list) {
0542         if (res == res_bak)
0543             break;
0544         release_mem_region(res->start, res->end - res->start);
0545     }
0546 arch_res_fini:
0547     if (arch_apei_filter_addr)
0548         apei_resources_fini(&arch_res);
0549 nvs_res_fini:
0550     apei_resources_fini(&nvs_resources);
0551     return rc;
0552 }
0553 EXPORT_SYMBOL_GPL(apei_resources_request);
0554 
0555 void apei_resources_release(struct apei_resources *resources)
0556 {
0557     int rc;
0558     struct apei_res *res;
0559 
0560     list_for_each_entry(res, &resources->iomem, list)
0561         release_mem_region(res->start, res->end - res->start);
0562     list_for_each_entry(res, &resources->ioport, list)
0563         release_region(res->start, res->end - res->start);
0564 
0565     rc = apei_resources_sub(&apei_resources_all, resources);
0566     if (rc)
0567         pr_err(APEI_PFX "Fail to sub resources!\n");
0568 }
0569 EXPORT_SYMBOL_GPL(apei_resources_release);
0570 
0571 static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
0572                 u32 *access_bit_width)
0573 {
0574     u32 bit_width, bit_offset, access_size_code, space_id;
0575 
0576     bit_width = reg->bit_width;
0577     bit_offset = reg->bit_offset;
0578     access_size_code = reg->access_width;
0579     space_id = reg->space_id;
0580     *paddr = get_unaligned(&reg->address);
0581     if (!*paddr) {
0582         pr_warn(FW_BUG APEI_PFX
0583             "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
0584             *paddr, bit_width, bit_offset, access_size_code,
0585             space_id);
0586         return -EINVAL;
0587     }
0588 
0589     if (access_size_code < 1 || access_size_code > 4) {
0590         pr_warn(FW_BUG APEI_PFX
0591             "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
0592             *paddr, bit_width, bit_offset, access_size_code,
0593             space_id);
0594         return -EINVAL;
0595     }
0596     *access_bit_width = 1UL << (access_size_code + 2);
0597 
0598     /* Fixup common BIOS bug */
0599     if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 &&
0600         *access_bit_width < 32)
0601         *access_bit_width = 32;
0602     else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 &&
0603         *access_bit_width < 64)
0604         *access_bit_width = 64;
0605 
0606     if ((bit_width + bit_offset) > *access_bit_width) {
0607         pr_warn(FW_BUG APEI_PFX
0608             "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
0609             *paddr, bit_width, bit_offset, access_size_code,
0610             space_id);
0611         return -EINVAL;
0612     }
0613 
0614     if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
0615         space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
0616         pr_warn(FW_BUG APEI_PFX
0617             "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
0618             *paddr, bit_width, bit_offset, access_size_code,
0619             space_id);
0620         return -EINVAL;
0621     }
0622 
0623     return 0;
0624 }
0625 
0626 int apei_map_generic_address(struct acpi_generic_address *reg)
0627 {
0628     int rc;
0629     u32 access_bit_width;
0630     u64 address;
0631 
0632     rc = apei_check_gar(reg, &address, &access_bit_width);
0633     if (rc)
0634         return rc;
0635 
0636     /* IO space doesn't need mapping */
0637     if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
0638         return 0;
0639 
0640     if (!acpi_os_map_generic_address(reg))
0641         return -ENXIO;
0642 
0643     return 0;
0644 }
0645 EXPORT_SYMBOL_GPL(apei_map_generic_address);
0646 
0647 /* read GAR in interrupt (including NMI) or process context */
0648 int apei_read(u64 *val, struct acpi_generic_address *reg)
0649 {
0650     int rc;
0651     u32 access_bit_width;
0652     u64 address;
0653     acpi_status status;
0654 
0655     rc = apei_check_gar(reg, &address, &access_bit_width);
0656     if (rc)
0657         return rc;
0658 
0659     *val = 0;
0660     switch(reg->space_id) {
0661     case ACPI_ADR_SPACE_SYSTEM_MEMORY:
0662         status = acpi_os_read_memory((acpi_physical_address) address,
0663                            val, access_bit_width);
0664         if (ACPI_FAILURE(status))
0665             return -EIO;
0666         break;
0667     case ACPI_ADR_SPACE_SYSTEM_IO:
0668         status = acpi_os_read_port(address, (u32 *)val,
0669                        access_bit_width);
0670         if (ACPI_FAILURE(status))
0671             return -EIO;
0672         break;
0673     default:
0674         return -EINVAL;
0675     }
0676 
0677     return 0;
0678 }
0679 EXPORT_SYMBOL_GPL(apei_read);
0680 
0681 /* write GAR in interrupt (including NMI) or process context */
0682 int apei_write(u64 val, struct acpi_generic_address *reg)
0683 {
0684     int rc;
0685     u32 access_bit_width;
0686     u64 address;
0687     acpi_status status;
0688 
0689     rc = apei_check_gar(reg, &address, &access_bit_width);
0690     if (rc)
0691         return rc;
0692 
0693     switch (reg->space_id) {
0694     case ACPI_ADR_SPACE_SYSTEM_MEMORY:
0695         status = acpi_os_write_memory((acpi_physical_address) address,
0696                         val, access_bit_width);
0697         if (ACPI_FAILURE(status))
0698             return -EIO;
0699         break;
0700     case ACPI_ADR_SPACE_SYSTEM_IO:
0701         status = acpi_os_write_port(address, val, access_bit_width);
0702         if (ACPI_FAILURE(status))
0703             return -EIO;
0704         break;
0705     default:
0706         return -EINVAL;
0707     }
0708 
0709     return 0;
0710 }
0711 EXPORT_SYMBOL_GPL(apei_write);
0712 
0713 static int collect_res_callback(struct apei_exec_context *ctx,
0714                 struct acpi_whea_header *entry,
0715                 void *data)
0716 {
0717     struct apei_resources *resources = data;
0718     struct acpi_generic_address *reg = &entry->register_region;
0719     u8 ins = entry->instruction;
0720     u32 access_bit_width;
0721     u64 paddr;
0722     int rc;
0723 
0724     if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
0725         return 0;
0726 
0727     rc = apei_check_gar(reg, &paddr, &access_bit_width);
0728     if (rc)
0729         return rc;
0730 
0731     switch (reg->space_id) {
0732     case ACPI_ADR_SPACE_SYSTEM_MEMORY:
0733         return apei_res_add(&resources->iomem, paddr,
0734                     access_bit_width / 8);
0735     case ACPI_ADR_SPACE_SYSTEM_IO:
0736         return apei_res_add(&resources->ioport, paddr,
0737                     access_bit_width / 8);
0738     default:
0739         return -EINVAL;
0740     }
0741 }
0742 
0743 /*
0744  * Same register may be used by multiple instructions in GARs, so
0745  * resources are collected before requesting.
0746  */
0747 int apei_exec_collect_resources(struct apei_exec_context *ctx,
0748                 struct apei_resources *resources)
0749 {
0750     return apei_exec_for_each_entry(ctx, collect_res_callback,
0751                     resources, NULL);
0752 }
0753 EXPORT_SYMBOL_GPL(apei_exec_collect_resources);
0754 
0755 struct dentry *apei_get_debugfs_dir(void)
0756 {
0757     static struct dentry *dapei;
0758 
0759     if (!dapei)
0760         dapei = debugfs_create_dir("apei", NULL);
0761 
0762     return dapei;
0763 }
0764 EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
0765 
0766 int __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr,
0767                   void *data)
0768 {
0769     return 1;
0770 }
0771 EXPORT_SYMBOL_GPL(arch_apei_enable_cmcff);
0772 
0773 void __weak arch_apei_report_mem_error(int sev,
0774                        struct cper_sec_mem_err *mem_err)
0775 {
0776 }
0777 EXPORT_SYMBOL_GPL(arch_apei_report_mem_error);
0778 
0779 int apei_osc_setup(void)
0780 {
0781     static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
0782     acpi_handle handle;
0783     u32 capbuf[3];
0784     struct acpi_osc_context context = {
0785         .uuid_str   = whea_uuid_str,
0786         .rev        = 1,
0787         .cap.length = sizeof(capbuf),
0788         .cap.pointer    = capbuf,
0789     };
0790 
0791     capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
0792     capbuf[OSC_SUPPORT_DWORD] = 1;
0793     capbuf[OSC_CONTROL_DWORD] = 0;
0794 
0795     if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
0796         || ACPI_FAILURE(acpi_run_osc(handle, &context)))
0797         return -EIO;
0798     else {
0799         kfree(context.ret.pointer);
0800         return 0;
0801     }
0802 }
0803 EXPORT_SYMBOL_GPL(apei_osc_setup);