root/drivers/acpi/apei/apei-base.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. apei_exec_ctx_init
  2. __apei_exec_read_register
  3. apei_exec_read_register
  4. apei_exec_read_register_value
  5. __apei_exec_write_register
  6. apei_exec_write_register
  7. apei_exec_write_register_value
  8. apei_exec_noop
  9. __apei_exec_run
  10. apei_exec_for_each_entry
  11. pre_map_gar_callback
  12. apei_exec_pre_map_gars
  13. post_unmap_gar_callback
  14. apei_exec_post_unmap_gars
  15. apei_res_add
  16. apei_res_sub
  17. apei_res_clean
  18. apei_resources_fini
  19. apei_resources_merge
  20. apei_resources_add
  21. apei_resources_sub
  22. apei_get_res_callback
  23. apei_get_nvs_resources
  24. apei_get_arch_resources
  25. apei_resources_request
  26. apei_resources_release
  27. apei_check_gar
  28. apei_map_generic_address
  29. apei_read
  30. apei_write
  31. collect_res_callback
  32. apei_exec_collect_resources
  33. apei_get_debugfs_dir
  34. arch_apei_enable_cmcff
  35. arch_apei_report_mem_error
  36. apei_osc_setup

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * apei-base.c - ACPI Platform Error Interface (APEI) supporting
   4  * infrastructure
   5  *
   6  * APEI allows to report errors (for example from the chipset) to the
   7  * the operating system. This improves NMI handling especially. In
   8  * addition it supports error serialization and error injection.
   9  *
  10  * For more information about APEI, please refer to ACPI Specification
  11  * version 4.0, chapter 17.
  12  *
  13  * This file has Common functions used by more than one APEI table,
  14  * including framework of interpreter for ERST and EINJ; resource
  15  * management for APEI registers.
  16  *
  17  * Copyright (C) 2009, Intel Corp.
  18  *      Author: Huang Ying <ying.huang@intel.com>
  19  */
  20 
  21 #include <linux/kernel.h>
  22 #include <linux/module.h>
  23 #include <linux/init.h>
  24 #include <linux/acpi.h>
  25 #include <linux/slab.h>
  26 #include <linux/io.h>
  27 #include <linux/kref.h>
  28 #include <linux/rculist.h>
  29 #include <linux/interrupt.h>
  30 #include <linux/debugfs.h>
  31 #include <asm/unaligned.h>
  32 
  33 #include "apei-internal.h"
  34 
  35 #define APEI_PFX "APEI: "
  36 
  37 /*
  38  * APEI ERST (Error Record Serialization Table) and EINJ (Error
  39  * INJection) interpreter framework.
  40  */
  41 
  42 #define APEI_EXEC_PRESERVE_REGISTER     0x1
  43 
  44 void apei_exec_ctx_init(struct apei_exec_context *ctx,
  45                         struct apei_exec_ins_type *ins_table,
  46                         u32 instructions,
  47                         struct acpi_whea_header *action_table,
  48                         u32 entries)
  49 {
  50         ctx->ins_table = ins_table;
  51         ctx->instructions = instructions;
  52         ctx->action_table = action_table;
  53         ctx->entries = entries;
  54 }
  55 EXPORT_SYMBOL_GPL(apei_exec_ctx_init);
  56 
  57 int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
  58 {
  59         int rc;
  60 
  61         rc = apei_read(val, &entry->register_region);
  62         if (rc)
  63                 return rc;
  64         *val >>= entry->register_region.bit_offset;
  65         *val &= entry->mask;
  66 
  67         return 0;
  68 }
  69 
  70 int apei_exec_read_register(struct apei_exec_context *ctx,
  71                             struct acpi_whea_header *entry)
  72 {
  73         int rc;
  74         u64 val = 0;
  75 
  76         rc = __apei_exec_read_register(entry, &val);
  77         if (rc)
  78                 return rc;
  79         ctx->value = val;
  80 
  81         return 0;
  82 }
  83 EXPORT_SYMBOL_GPL(apei_exec_read_register);
  84 
  85 int apei_exec_read_register_value(struct apei_exec_context *ctx,
  86                                   struct acpi_whea_header *entry)
  87 {
  88         int rc;
  89 
  90         rc = apei_exec_read_register(ctx, entry);
  91         if (rc)
  92                 return rc;
  93         ctx->value = (ctx->value == entry->value);
  94 
  95         return 0;
  96 }
  97 EXPORT_SYMBOL_GPL(apei_exec_read_register_value);
  98 
  99 int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
 100 {
 101         int rc;
 102 
 103         val &= entry->mask;
 104         val <<= entry->register_region.bit_offset;
 105         if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
 106                 u64 valr = 0;
 107                 rc = apei_read(&valr, &entry->register_region);
 108                 if (rc)
 109                         return rc;
 110                 valr &= ~(entry->mask << entry->register_region.bit_offset);
 111                 val |= valr;
 112         }
 113         rc = apei_write(val, &entry->register_region);
 114 
 115         return rc;
 116 }
 117 
 118 int apei_exec_write_register(struct apei_exec_context *ctx,
 119                              struct acpi_whea_header *entry)
 120 {
 121         return __apei_exec_write_register(entry, ctx->value);
 122 }
 123 EXPORT_SYMBOL_GPL(apei_exec_write_register);
 124 
 125 int apei_exec_write_register_value(struct apei_exec_context *ctx,
 126                                    struct acpi_whea_header *entry)
 127 {
 128         int rc;
 129 
 130         ctx->value = entry->value;
 131         rc = apei_exec_write_register(ctx, entry);
 132 
 133         return rc;
 134 }
 135 EXPORT_SYMBOL_GPL(apei_exec_write_register_value);
 136 
 137 int apei_exec_noop(struct apei_exec_context *ctx,
 138                    struct acpi_whea_header *entry)
 139 {
 140         return 0;
 141 }
 142 EXPORT_SYMBOL_GPL(apei_exec_noop);
 143 
 144 /*
 145  * Interpret the specified action. Go through whole action table,
 146  * execute all instructions belong to the action.
 147  */
 148 int __apei_exec_run(struct apei_exec_context *ctx, u8 action,
 149                     bool optional)
 150 {
 151         int rc = -ENOENT;
 152         u32 i, ip;
 153         struct acpi_whea_header *entry;
 154         apei_exec_ins_func_t run;
 155 
 156         ctx->ip = 0;
 157 
 158         /*
 159          * "ip" is the instruction pointer of current instruction,
 160          * "ctx->ip" specifies the next instruction to executed,
 161          * instruction "run" function may change the "ctx->ip" to
 162          * implement "goto" semantics.
 163          */
 164 rewind:
 165         ip = 0;
 166         for (i = 0; i < ctx->entries; i++) {
 167                 entry = &ctx->action_table[i];
 168                 if (entry->action != action)
 169                         continue;
 170                 if (ip == ctx->ip) {
 171                         if (entry->instruction >= ctx->instructions ||
 172                             !ctx->ins_table[entry->instruction].run) {
 173                                 pr_warning(FW_WARN APEI_PFX
 174                         "Invalid action table, unknown instruction type: %d\n",
 175                                            entry->instruction);
 176                                 return -EINVAL;
 177                         }
 178                         run = ctx->ins_table[entry->instruction].run;
 179                         rc = run(ctx, entry);
 180                         if (rc < 0)
 181                                 return rc;
 182                         else if (rc != APEI_EXEC_SET_IP)
 183                                 ctx->ip++;
 184                 }
 185                 ip++;
 186                 if (ctx->ip < ip)
 187                         goto rewind;
 188         }
 189 
 190         return !optional && rc < 0 ? rc : 0;
 191 }
 192 EXPORT_SYMBOL_GPL(__apei_exec_run);
 193 
 194 typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
 195                                       struct acpi_whea_header *entry,
 196                                       void *data);
 197 
 198 static int apei_exec_for_each_entry(struct apei_exec_context *ctx,
 199                                     apei_exec_entry_func_t func,
 200                                     void *data,
 201                                     int *end)
 202 {
 203         u8 ins;
 204         int i, rc;
 205         struct acpi_whea_header *entry;
 206         struct apei_exec_ins_type *ins_table = ctx->ins_table;
 207 
 208         for (i = 0; i < ctx->entries; i++) {
 209                 entry = ctx->action_table + i;
 210                 ins = entry->instruction;
 211                 if (end)
 212                         *end = i;
 213                 if (ins >= ctx->instructions || !ins_table[ins].run) {
 214                         pr_warning(FW_WARN APEI_PFX
 215                         "Invalid action table, unknown instruction type: %d\n",
 216                                    ins);
 217                         return -EINVAL;
 218                 }
 219                 rc = func(ctx, entry, data);
 220                 if (rc)
 221                         return rc;
 222         }
 223 
 224         return 0;
 225 }
 226 
 227 static int pre_map_gar_callback(struct apei_exec_context *ctx,
 228                                 struct acpi_whea_header *entry,
 229                                 void *data)
 230 {
 231         u8 ins = entry->instruction;
 232 
 233         if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
 234                 return apei_map_generic_address(&entry->register_region);
 235 
 236         return 0;
 237 }
 238 
 239 /*
 240  * Pre-map all GARs in action table to make it possible to access them
 241  * in NMI handler.
 242  */
 243 int apei_exec_pre_map_gars(struct apei_exec_context *ctx)
 244 {
 245         int rc, end;
 246 
 247         rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback,
 248                                       NULL, &end);
 249         if (rc) {
 250                 struct apei_exec_context ctx_unmap;
 251                 memcpy(&ctx_unmap, ctx, sizeof(*ctx));
 252                 ctx_unmap.entries = end;
 253                 apei_exec_post_unmap_gars(&ctx_unmap);
 254         }
 255 
 256         return rc;
 257 }
 258 EXPORT_SYMBOL_GPL(apei_exec_pre_map_gars);
 259 
 260 static int post_unmap_gar_callback(struct apei_exec_context *ctx,
 261                                    struct acpi_whea_header *entry,
 262                                    void *data)
 263 {
 264         u8 ins = entry->instruction;
 265 
 266         if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
 267                 apei_unmap_generic_address(&entry->register_region);
 268 
 269         return 0;
 270 }
 271 
 272 /* Post-unmap all GAR in action table. */
 273 int apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
 274 {
 275         return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
 276                                         NULL, NULL);
 277 }
 278 EXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars);
 279 
 280 /*
 281  * Resource management for GARs in APEI
 282  */
 283 struct apei_res {
 284         struct list_head list;
 285         unsigned long start;
 286         unsigned long end;
 287 };
 288 
 289 /* Collect all resources requested, to avoid conflict */
 290 struct apei_resources apei_resources_all = {
 291         .iomem = LIST_HEAD_INIT(apei_resources_all.iomem),
 292         .ioport = LIST_HEAD_INIT(apei_resources_all.ioport),
 293 };
 294 
 295 static int apei_res_add(struct list_head *res_list,
 296                         unsigned long start, unsigned long size)
 297 {
 298         struct apei_res *res, *resn, *res_ins = NULL;
 299         unsigned long end = start + size;
 300 
 301         if (end <= start)
 302                 return 0;
 303 repeat:
 304         list_for_each_entry_safe(res, resn, res_list, list) {
 305                 if (res->start > end || res->end < start)
 306                         continue;
 307                 else if (end <= res->end && start >= res->start) {
 308                         kfree(res_ins);
 309                         return 0;
 310                 }
 311                 list_del(&res->list);
 312                 res->start = start = min(res->start, start);
 313                 res->end = end = max(res->end, end);
 314                 kfree(res_ins);
 315                 res_ins = res;
 316                 goto repeat;
 317         }
 318 
 319         if (res_ins)
 320                 list_add(&res_ins->list, res_list);
 321         else {
 322                 res_ins = kmalloc(sizeof(*res), GFP_KERNEL);
 323                 if (!res_ins)
 324                         return -ENOMEM;
 325                 res_ins->start = start;
 326                 res_ins->end = end;
 327                 list_add(&res_ins->list, res_list);
 328         }
 329 
 330         return 0;
 331 }
 332 
 333 static int apei_res_sub(struct list_head *res_list1,
 334                         struct list_head *res_list2)
 335 {
 336         struct apei_res *res1, *resn1, *res2, *res;
 337         res1 = list_entry(res_list1->next, struct apei_res, list);
 338         resn1 = list_entry(res1->list.next, struct apei_res, list);
 339         while (&res1->list != res_list1) {
 340                 list_for_each_entry(res2, res_list2, list) {
 341                         if (res1->start >= res2->end ||
 342                             res1->end <= res2->start)
 343                                 continue;
 344                         else if (res1->end <= res2->end &&
 345                                  res1->start >= res2->start) {
 346                                 list_del(&res1->list);
 347                                 kfree(res1);
 348                                 break;
 349                         } else if (res1->end > res2->end &&
 350                                    res1->start < res2->start) {
 351                                 res = kmalloc(sizeof(*res), GFP_KERNEL);
 352                                 if (!res)
 353                                         return -ENOMEM;
 354                                 res->start = res2->end;
 355                                 res->end = res1->end;
 356                                 res1->end = res2->start;
 357                                 list_add(&res->list, &res1->list);
 358                                 resn1 = res;
 359                         } else {
 360                                 if (res1->start < res2->start)
 361                                         res1->end = res2->start;
 362                                 else
 363                                         res1->start = res2->end;
 364                         }
 365                 }
 366                 res1 = resn1;
 367                 resn1 = list_entry(resn1->list.next, struct apei_res, list);
 368         }
 369 
 370         return 0;
 371 }
 372 
 373 static void apei_res_clean(struct list_head *res_list)
 374 {
 375         struct apei_res *res, *resn;
 376 
 377         list_for_each_entry_safe(res, resn, res_list, list) {
 378                 list_del(&res->list);
 379                 kfree(res);
 380         }
 381 }
 382 
 383 void apei_resources_fini(struct apei_resources *resources)
 384 {
 385         apei_res_clean(&resources->iomem);
 386         apei_res_clean(&resources->ioport);
 387 }
 388 EXPORT_SYMBOL_GPL(apei_resources_fini);
 389 
 390 static int apei_resources_merge(struct apei_resources *resources1,
 391                                 struct apei_resources *resources2)
 392 {
 393         int rc;
 394         struct apei_res *res;
 395 
 396         list_for_each_entry(res, &resources2->iomem, list) {
 397                 rc = apei_res_add(&resources1->iomem, res->start,
 398                                   res->end - res->start);
 399                 if (rc)
 400                         return rc;
 401         }
 402         list_for_each_entry(res, &resources2->ioport, list) {
 403                 rc = apei_res_add(&resources1->ioport, res->start,
 404                                   res->end - res->start);
 405                 if (rc)
 406                         return rc;
 407         }
 408 
 409         return 0;
 410 }
 411 
 412 int apei_resources_add(struct apei_resources *resources,
 413                        unsigned long start, unsigned long size,
 414                        bool iomem)
 415 {
 416         if (iomem)
 417                 return apei_res_add(&resources->iomem, start, size);
 418         else
 419                 return apei_res_add(&resources->ioport, start, size);
 420 }
 421 EXPORT_SYMBOL_GPL(apei_resources_add);
 422 
 423 /*
 424  * EINJ has two groups of GARs (EINJ table entry and trigger table
 425  * entry), so common resources are subtracted from the trigger table
 426  * resources before the second requesting.
 427  */
 428 int apei_resources_sub(struct apei_resources *resources1,
 429                        struct apei_resources *resources2)
 430 {
 431         int rc;
 432 
 433         rc = apei_res_sub(&resources1->iomem, &resources2->iomem);
 434         if (rc)
 435                 return rc;
 436         return apei_res_sub(&resources1->ioport, &resources2->ioport);
 437 }
 438 EXPORT_SYMBOL_GPL(apei_resources_sub);
 439 
 440 static int apei_get_res_callback(__u64 start, __u64 size, void *data)
 441 {
 442         struct apei_resources *resources = data;
 443         return apei_res_add(&resources->iomem, start, size);
 444 }
 445 
 446 static int apei_get_nvs_resources(struct apei_resources *resources)
 447 {
 448         return acpi_nvs_for_each_region(apei_get_res_callback, resources);
 449 }
 450 
 451 int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
 452                                      void *data), void *data);
 453 static int apei_get_arch_resources(struct apei_resources *resources)
 454 
 455 {
 456         return arch_apei_filter_addr(apei_get_res_callback, resources);
 457 }
 458 
 459 /*
 460  * IO memory/port resource management mechanism is used to check
 461  * whether memory/port area used by GARs conflicts with normal memory
 462  * or IO memory/port of devices.
 463  */
 464 int apei_resources_request(struct apei_resources *resources,
 465                            const char *desc)
 466 {
 467         struct apei_res *res, *res_bak = NULL;
 468         struct resource *r;
 469         struct apei_resources nvs_resources, arch_res;
 470         int rc;
 471 
 472         rc = apei_resources_sub(resources, &apei_resources_all);
 473         if (rc)
 474                 return rc;
 475 
 476         /*
 477          * Some firmware uses ACPI NVS region, that has been marked as
 478          * busy, so exclude it from APEI resources to avoid false
 479          * conflict.
 480          */
 481         apei_resources_init(&nvs_resources);
 482         rc = apei_get_nvs_resources(&nvs_resources);
 483         if (rc)
 484                 goto nvs_res_fini;
 485         rc = apei_resources_sub(resources, &nvs_resources);
 486         if (rc)
 487                 goto nvs_res_fini;
 488 
 489         if (arch_apei_filter_addr) {
 490                 apei_resources_init(&arch_res);
 491                 rc = apei_get_arch_resources(&arch_res);
 492                 if (rc)
 493                         goto arch_res_fini;
 494                 rc = apei_resources_sub(resources, &arch_res);
 495                 if (rc)
 496                         goto arch_res_fini;
 497         }
 498 
 499         rc = -EINVAL;
 500         list_for_each_entry(res, &resources->iomem, list) {
 501                 r = request_mem_region(res->start, res->end - res->start,
 502                                        desc);
 503                 if (!r) {
 504                         pr_err(APEI_PFX
 505                 "Can not request [mem %#010llx-%#010llx] for %s registers\n",
 506                                (unsigned long long)res->start,
 507                                (unsigned long long)res->end - 1, desc);
 508                         res_bak = res;
 509                         goto err_unmap_iomem;
 510                 }
 511         }
 512 
 513         list_for_each_entry(res, &resources->ioport, list) {
 514                 r = request_region(res->start, res->end - res->start, desc);
 515                 if (!r) {
 516                         pr_err(APEI_PFX
 517                 "Can not request [io  %#06llx-%#06llx] for %s registers\n",
 518                                (unsigned long long)res->start,
 519                                (unsigned long long)res->end - 1, desc);
 520                         res_bak = res;
 521                         goto err_unmap_ioport;
 522                 }
 523         }
 524 
 525         rc = apei_resources_merge(&apei_resources_all, resources);
 526         if (rc) {
 527                 pr_err(APEI_PFX "Fail to merge resources!\n");
 528                 goto err_unmap_ioport;
 529         }
 530 
 531         goto arch_res_fini;
 532 
 533 err_unmap_ioport:
 534         list_for_each_entry(res, &resources->ioport, list) {
 535                 if (res == res_bak)
 536                         break;
 537                 release_region(res->start, res->end - res->start);
 538         }
 539         res_bak = NULL;
 540 err_unmap_iomem:
 541         list_for_each_entry(res, &resources->iomem, list) {
 542                 if (res == res_bak)
 543                         break;
 544                 release_mem_region(res->start, res->end - res->start);
 545         }
 546 arch_res_fini:
 547         if (arch_apei_filter_addr)
 548                 apei_resources_fini(&arch_res);
 549 nvs_res_fini:
 550         apei_resources_fini(&nvs_resources);
 551         return rc;
 552 }
 553 EXPORT_SYMBOL_GPL(apei_resources_request);
 554 
 555 void apei_resources_release(struct apei_resources *resources)
 556 {
 557         int rc;
 558         struct apei_res *res;
 559 
 560         list_for_each_entry(res, &resources->iomem, list)
 561                 release_mem_region(res->start, res->end - res->start);
 562         list_for_each_entry(res, &resources->ioport, list)
 563                 release_region(res->start, res->end - res->start);
 564 
 565         rc = apei_resources_sub(&apei_resources_all, resources);
 566         if (rc)
 567                 pr_err(APEI_PFX "Fail to sub resources!\n");
 568 }
 569 EXPORT_SYMBOL_GPL(apei_resources_release);
 570 
 571 static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
 572                                 u32 *access_bit_width)
 573 {
 574         u32 bit_width, bit_offset, access_size_code, space_id;
 575 
 576         bit_width = reg->bit_width;
 577         bit_offset = reg->bit_offset;
 578         access_size_code = reg->access_width;
 579         space_id = reg->space_id;
 580         *paddr = get_unaligned(&reg->address);
 581         if (!*paddr) {
 582                 pr_warning(FW_BUG APEI_PFX
 583                            "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
 584                            *paddr, bit_width, bit_offset, access_size_code,
 585                            space_id);
 586                 return -EINVAL;
 587         }
 588 
 589         if (access_size_code < 1 || access_size_code > 4) {
 590                 pr_warning(FW_BUG APEI_PFX
 591                            "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
 592                            *paddr, bit_width, bit_offset, access_size_code,
 593                            space_id);
 594                 return -EINVAL;
 595         }
 596         *access_bit_width = 1UL << (access_size_code + 2);
 597 
 598         /* Fixup common BIOS bug */
 599         if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 &&
 600             *access_bit_width < 32)
 601                 *access_bit_width = 32;
 602         else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 &&
 603             *access_bit_width < 64)
 604                 *access_bit_width = 64;
 605 
 606         if ((bit_width + bit_offset) > *access_bit_width) {
 607                 pr_warning(FW_BUG APEI_PFX
 608                            "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
 609                            *paddr, bit_width, bit_offset, access_size_code,
 610                            space_id);
 611                 return -EINVAL;
 612         }
 613 
 614         if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
 615             space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
 616                 pr_warning(FW_BUG APEI_PFX
 617                            "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
 618                            *paddr, bit_width, bit_offset, access_size_code,
 619                            space_id);
 620                 return -EINVAL;
 621         }
 622 
 623         return 0;
 624 }
 625 
 626 int apei_map_generic_address(struct acpi_generic_address *reg)
 627 {
 628         int rc;
 629         u32 access_bit_width;
 630         u64 address;
 631 
 632         rc = apei_check_gar(reg, &address, &access_bit_width);
 633         if (rc)
 634                 return rc;
 635         return acpi_os_map_generic_address(reg);
 636 }
 637 EXPORT_SYMBOL_GPL(apei_map_generic_address);
 638 
 639 /* read GAR in interrupt (including NMI) or process context */
 640 int apei_read(u64 *val, struct acpi_generic_address *reg)
 641 {
 642         int rc;
 643         u32 access_bit_width;
 644         u64 address;
 645         acpi_status status;
 646 
 647         rc = apei_check_gar(reg, &address, &access_bit_width);
 648         if (rc)
 649                 return rc;
 650 
 651         *val = 0;
 652         switch(reg->space_id) {
 653         case ACPI_ADR_SPACE_SYSTEM_MEMORY:
 654                 status = acpi_os_read_memory((acpi_physical_address) address,
 655                                                val, access_bit_width);
 656                 if (ACPI_FAILURE(status))
 657                         return -EIO;
 658                 break;
 659         case ACPI_ADR_SPACE_SYSTEM_IO:
 660                 status = acpi_os_read_port(address, (u32 *)val,
 661                                            access_bit_width);
 662                 if (ACPI_FAILURE(status))
 663                         return -EIO;
 664                 break;
 665         default:
 666                 return -EINVAL;
 667         }
 668 
 669         return 0;
 670 }
 671 EXPORT_SYMBOL_GPL(apei_read);
 672 
 673 /* write GAR in interrupt (including NMI) or process context */
 674 int apei_write(u64 val, struct acpi_generic_address *reg)
 675 {
 676         int rc;
 677         u32 access_bit_width;
 678         u64 address;
 679         acpi_status status;
 680 
 681         rc = apei_check_gar(reg, &address, &access_bit_width);
 682         if (rc)
 683                 return rc;
 684 
 685         switch (reg->space_id) {
 686         case ACPI_ADR_SPACE_SYSTEM_MEMORY:
 687                 status = acpi_os_write_memory((acpi_physical_address) address,
 688                                                 val, access_bit_width);
 689                 if (ACPI_FAILURE(status))
 690                         return -EIO;
 691                 break;
 692         case ACPI_ADR_SPACE_SYSTEM_IO:
 693                 status = acpi_os_write_port(address, val, access_bit_width);
 694                 if (ACPI_FAILURE(status))
 695                         return -EIO;
 696                 break;
 697         default:
 698                 return -EINVAL;
 699         }
 700 
 701         return 0;
 702 }
 703 EXPORT_SYMBOL_GPL(apei_write);
 704 
 705 static int collect_res_callback(struct apei_exec_context *ctx,
 706                                 struct acpi_whea_header *entry,
 707                                 void *data)
 708 {
 709         struct apei_resources *resources = data;
 710         struct acpi_generic_address *reg = &entry->register_region;
 711         u8 ins = entry->instruction;
 712         u32 access_bit_width;
 713         u64 paddr;
 714         int rc;
 715 
 716         if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
 717                 return 0;
 718 
 719         rc = apei_check_gar(reg, &paddr, &access_bit_width);
 720         if (rc)
 721                 return rc;
 722 
 723         switch (reg->space_id) {
 724         case ACPI_ADR_SPACE_SYSTEM_MEMORY:
 725                 return apei_res_add(&resources->iomem, paddr,
 726                                     access_bit_width / 8);
 727         case ACPI_ADR_SPACE_SYSTEM_IO:
 728                 return apei_res_add(&resources->ioport, paddr,
 729                                     access_bit_width / 8);
 730         default:
 731                 return -EINVAL;
 732         }
 733 }
 734 
 735 /*
 736  * Same register may be used by multiple instructions in GARs, so
 737  * resources are collected before requesting.
 738  */
 739 int apei_exec_collect_resources(struct apei_exec_context *ctx,
 740                                 struct apei_resources *resources)
 741 {
 742         return apei_exec_for_each_entry(ctx, collect_res_callback,
 743                                         resources, NULL);
 744 }
 745 EXPORT_SYMBOL_GPL(apei_exec_collect_resources);
 746 
 747 struct dentry *apei_get_debugfs_dir(void)
 748 {
 749         static struct dentry *dapei;
 750 
 751         if (!dapei)
 752                 dapei = debugfs_create_dir("apei", NULL);
 753 
 754         return dapei;
 755 }
 756 EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
 757 
 758 int __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr,
 759                                   void *data)
 760 {
 761         return 1;
 762 }
 763 EXPORT_SYMBOL_GPL(arch_apei_enable_cmcff);
 764 
 765 void __weak arch_apei_report_mem_error(int sev,
 766                                        struct cper_sec_mem_err *mem_err)
 767 {
 768 }
 769 EXPORT_SYMBOL_GPL(arch_apei_report_mem_error);
 770 
 771 int apei_osc_setup(void)
 772 {
 773         static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
 774         acpi_handle handle;
 775         u32 capbuf[3];
 776         struct acpi_osc_context context = {
 777                 .uuid_str       = whea_uuid_str,
 778                 .rev            = 1,
 779                 .cap.length     = sizeof(capbuf),
 780                 .cap.pointer    = capbuf,
 781         };
 782 
 783         capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
 784         capbuf[OSC_SUPPORT_DWORD] = 1;
 785         capbuf[OSC_CONTROL_DWORD] = 0;
 786 
 787         if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
 788             || ACPI_FAILURE(acpi_run_osc(handle, &context)))
 789                 return -EIO;
 790         else {
 791                 kfree(context.ret.pointer);
 792                 return 0;
 793         }
 794 }
 795 EXPORT_SYMBOL_GPL(apei_osc_setup);

/* [<][>][^][v][top][bottom][index][help] */