0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
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
0039
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
0146
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
0160
0161
0162
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
0241
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
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
0282
0283 struct apei_res {
0284 struct list_head list;
0285 unsigned long start;
0286 unsigned long end;
0287 };
0288
0289
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
0425
0426
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
0461
0462
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
0478
0479
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(®->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
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
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
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
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
0745
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);