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