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