ffh.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/acpi.h>
  3. #include <linux/arm-smccc.h>
  4. #include <linux/slab.h>
  5. /*
  6. * Implements ARM64 specific callbacks to support ACPI FFH Operation Region as
  7. * specified in https://developer.arm.com/docs/den0048/latest
  8. */
  9. struct acpi_ffh_data {
  10. struct acpi_ffh_info info;
  11. void (*invoke_ffh_fn)(unsigned long a0, unsigned long a1,
  12. unsigned long a2, unsigned long a3,
  13. unsigned long a4, unsigned long a5,
  14. unsigned long a6, unsigned long a7,
  15. struct arm_smccc_res *args,
  16. struct arm_smccc_quirk *res);
  17. void (*invoke_ffh64_fn)(const struct arm_smccc_1_2_regs *args,
  18. struct arm_smccc_1_2_regs *res);
  19. };
  20. int acpi_ffh_address_space_arch_setup(void *handler_ctxt, void **region_ctxt)
  21. {
  22. enum arm_smccc_conduit conduit;
  23. struct acpi_ffh_data *ffh_ctxt;
  24. if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
  25. return -EOPNOTSUPP;
  26. conduit = arm_smccc_1_1_get_conduit();
  27. if (conduit == SMCCC_CONDUIT_NONE) {
  28. pr_err("%s: invalid SMCCC conduit\n", __func__);
  29. return -EOPNOTSUPP;
  30. }
  31. ffh_ctxt = kzalloc_obj(*ffh_ctxt);
  32. if (!ffh_ctxt)
  33. return -ENOMEM;
  34. if (conduit == SMCCC_CONDUIT_SMC) {
  35. ffh_ctxt->invoke_ffh_fn = __arm_smccc_smc;
  36. ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_smc;
  37. } else {
  38. ffh_ctxt->invoke_ffh_fn = __arm_smccc_hvc;
  39. ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_hvc;
  40. }
  41. memcpy(ffh_ctxt, handler_ctxt, sizeof(ffh_ctxt->info));
  42. *region_ctxt = ffh_ctxt;
  43. return AE_OK;
  44. }
  45. static bool acpi_ffh_smccc_owner_allowed(u32 fid)
  46. {
  47. int owner = ARM_SMCCC_OWNER_NUM(fid);
  48. if (owner == ARM_SMCCC_OWNER_STANDARD ||
  49. owner == ARM_SMCCC_OWNER_SIP || owner == ARM_SMCCC_OWNER_OEM)
  50. return true;
  51. return false;
  52. }
  53. int acpi_ffh_address_space_arch_handler(acpi_integer *value, void *region_context)
  54. {
  55. int ret = 0;
  56. struct acpi_ffh_data *ffh_ctxt = region_context;
  57. if (ffh_ctxt->info.offset == 0) {
  58. /* SMC/HVC 32bit call */
  59. struct arm_smccc_res res;
  60. u32 a[8] = { 0 }, *ptr = (u32 *)value;
  61. if (!ARM_SMCCC_IS_FAST_CALL(*ptr) || ARM_SMCCC_IS_64(*ptr) ||
  62. !acpi_ffh_smccc_owner_allowed(*ptr) ||
  63. ffh_ctxt->info.length > 32) {
  64. ret = AE_ERROR;
  65. } else {
  66. int idx, len = ffh_ctxt->info.length >> 2;
  67. for (idx = 0; idx < len; idx++)
  68. a[idx] = *(ptr + idx);
  69. ffh_ctxt->invoke_ffh_fn(a[0], a[1], a[2], a[3], a[4],
  70. a[5], a[6], a[7], &res, NULL);
  71. memcpy(value, &res, sizeof(res));
  72. }
  73. } else if (ffh_ctxt->info.offset == 1) {
  74. /* SMC/HVC 64bit call */
  75. struct arm_smccc_1_2_regs *r = (struct arm_smccc_1_2_regs *)value;
  76. if (!ARM_SMCCC_IS_FAST_CALL(r->a0) || !ARM_SMCCC_IS_64(r->a0) ||
  77. !acpi_ffh_smccc_owner_allowed(r->a0) ||
  78. ffh_ctxt->info.length > sizeof(*r)) {
  79. ret = AE_ERROR;
  80. } else {
  81. ffh_ctxt->invoke_ffh64_fn(r, r);
  82. memcpy(value, r, ffh_ctxt->info.length);
  83. }
  84. } else {
  85. ret = AE_ERROR;
  86. }
  87. return ret;
  88. }