|
13 | 13 | #define pr_fmt(fmt) "ACPI: " fmt
|
14 | 14 |
|
15 | 15 | #include <linux/acpi.h>
|
| 16 | +#include <linux/arm-smccc.h> |
16 | 17 | #include <linux/cpumask.h>
|
17 | 18 | #include <linux/efi.h>
|
18 | 19 | #include <linux/efi-bgrt.h>
|
@@ -411,3 +412,108 @@ void arch_reserve_mem_area(acpi_physical_address addr, size_t size)
|
411 | 412 | {
|
412 | 413 | memblock_mark_nomap(addr, size);
|
413 | 414 | }
|
| 415 | + |
| 416 | +#ifdef CONFIG_ACPI_FFH |
| 417 | +/* |
| 418 | + * Implements ARM64 specific callbacks to support ACPI FFH Operation Region as |
| 419 | + * specified in https://developer.arm.com/docs/den0048/latest |
| 420 | + */ |
| 421 | +struct acpi_ffh_data { |
| 422 | + struct acpi_ffh_info info; |
| 423 | + void (*invoke_ffh_fn)(unsigned long a0, unsigned long a1, |
| 424 | + unsigned long a2, unsigned long a3, |
| 425 | + unsigned long a4, unsigned long a5, |
| 426 | + unsigned long a6, unsigned long a7, |
| 427 | + struct arm_smccc_res *args, |
| 428 | + struct arm_smccc_quirk *res); |
| 429 | + void (*invoke_ffh64_fn)(const struct arm_smccc_1_2_regs *args, |
| 430 | + struct arm_smccc_1_2_regs *res); |
| 431 | +}; |
| 432 | + |
| 433 | +int acpi_ffh_address_space_arch_setup(void *handler_ctxt, void **region_ctxt) |
| 434 | +{ |
| 435 | + enum arm_smccc_conduit conduit; |
| 436 | + struct acpi_ffh_data *ffh_ctxt; |
| 437 | + |
| 438 | + ffh_ctxt = kzalloc(sizeof(*ffh_ctxt), GFP_KERNEL); |
| 439 | + if (!ffh_ctxt) |
| 440 | + return -ENOMEM; |
| 441 | + |
| 442 | + if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2) |
| 443 | + return -EOPNOTSUPP; |
| 444 | + |
| 445 | + conduit = arm_smccc_1_1_get_conduit(); |
| 446 | + if (conduit == SMCCC_CONDUIT_NONE) { |
| 447 | + pr_err("%s: invalid SMCCC conduit\n", __func__); |
| 448 | + return -EOPNOTSUPP; |
| 449 | + } |
| 450 | + |
| 451 | + if (conduit == SMCCC_CONDUIT_SMC) { |
| 452 | + ffh_ctxt->invoke_ffh_fn = __arm_smccc_smc; |
| 453 | + ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_smc; |
| 454 | + } else { |
| 455 | + ffh_ctxt->invoke_ffh_fn = __arm_smccc_hvc; |
| 456 | + ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_hvc; |
| 457 | + } |
| 458 | + |
| 459 | + memcpy(ffh_ctxt, handler_ctxt, sizeof(ffh_ctxt->info)); |
| 460 | + |
| 461 | + *region_ctxt = ffh_ctxt; |
| 462 | + return AE_OK; |
| 463 | +} |
| 464 | + |
| 465 | +static bool acpi_ffh_smccc_owner_allowed(u32 fid) |
| 466 | +{ |
| 467 | + int owner = ARM_SMCCC_OWNER_NUM(fid); |
| 468 | + |
| 469 | + if (owner == ARM_SMCCC_OWNER_STANDARD || |
| 470 | + owner == ARM_SMCCC_OWNER_SIP || owner == ARM_SMCCC_OWNER_OEM) |
| 471 | + return true; |
| 472 | + |
| 473 | + return false; |
| 474 | +} |
| 475 | + |
| 476 | +int acpi_ffh_address_space_arch_handler(acpi_integer *value, void *region_context) |
| 477 | +{ |
| 478 | + int ret = 0; |
| 479 | + struct acpi_ffh_data *ffh_ctxt = region_context; |
| 480 | + |
| 481 | + if (ffh_ctxt->info.offset == 0) { |
| 482 | + /* SMC/HVC 32bit call */ |
| 483 | + struct arm_smccc_res res; |
| 484 | + u32 a[8] = { 0 }, *ptr = (u32 *)value; |
| 485 | + |
| 486 | + if (!ARM_SMCCC_IS_FAST_CALL(*ptr) || ARM_SMCCC_IS_64(*ptr) || |
| 487 | + !acpi_ffh_smccc_owner_allowed(*ptr) || |
| 488 | + ffh_ctxt->info.length > 32) { |
| 489 | + ret = AE_ERROR; |
| 490 | + } else { |
| 491 | + int idx, len = ffh_ctxt->info.length >> 2; |
| 492 | + |
| 493 | + for (idx = 0; idx < len; idx++) |
| 494 | + a[idx] = *(ptr + idx); |
| 495 | + |
| 496 | + ffh_ctxt->invoke_ffh_fn(a[0], a[1], a[2], a[3], a[4], |
| 497 | + a[5], a[6], a[7], &res, NULL); |
| 498 | + memcpy(value, &res, sizeof(res)); |
| 499 | + } |
| 500 | + |
| 501 | + } else if (ffh_ctxt->info.offset == 1) { |
| 502 | + /* SMC/HVC 64bit call */ |
| 503 | + struct arm_smccc_1_2_regs *r = (struct arm_smccc_1_2_regs *)value; |
| 504 | + |
| 505 | + if (!ARM_SMCCC_IS_FAST_CALL(r->a0) || !ARM_SMCCC_IS_64(r->a0) || |
| 506 | + !acpi_ffh_smccc_owner_allowed(r->a0) || |
| 507 | + ffh_ctxt->info.length > sizeof(*r)) { |
| 508 | + ret = AE_ERROR; |
| 509 | + } else { |
| 510 | + ffh_ctxt->invoke_ffh64_fn(r, r); |
| 511 | + memcpy(value, r, ffh_ctxt->info.length); |
| 512 | + } |
| 513 | + } else { |
| 514 | + ret = AE_ERROR; |
| 515 | + } |
| 516 | + |
| 517 | + return ret; |
| 518 | +} |
| 519 | +#endif /* CONFIG_ACPI_FFH */ |
0 commit comments