gunyah_qcom.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #include <linux/arm-smccc.h>
  6. #include <linux/gunyah_rsc_mgr.h>
  7. #include <linux/module.h>
  8. #include <linux/qcom_scm.h>
  9. #include <linux/types.h>
  10. #include <linux/uuid.h>
  11. #include <linux/mm.h>
  12. #define QCOM_SCM_RM_MANAGED_VMID 0x3A
  13. #define QCOM_SCM_MAX_MANAGED_VMID 0x3F
  14. static void qcom_scm_gh_pin_pages(phys_addr_t phys_addr, size_t size)
  15. {
  16. struct page *page = pfn_to_page(__phys_to_pfn(phys_addr));
  17. while (size) {
  18. get_page(page++);
  19. size -= PAGE_SIZE;
  20. }
  21. }
  22. static int qcom_scm_gh_rm_pre_mem_share(void *rm, struct gh_rm_mem_parcel *mem_parcel)
  23. {
  24. struct qcom_scm_vmperm *new_perms;
  25. u64 src, src_cpy;
  26. int ret = 0, i, n, rb_ret;
  27. u16 vmid;
  28. new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms), GFP_KERNEL);
  29. if (!new_perms)
  30. return -ENOMEM;
  31. for (n = 0; n < mem_parcel->n_acl_entries; n++) {
  32. vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid);
  33. if (vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  34. new_perms[n].vmid = vmid;
  35. else
  36. new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID;
  37. if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_X)
  38. new_perms[n].perm |= QCOM_SCM_PERM_EXEC;
  39. if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_W)
  40. new_perms[n].perm |= QCOM_SCM_PERM_WRITE;
  41. if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_R)
  42. new_perms[n].perm |= QCOM_SCM_PERM_READ;
  43. }
  44. src = BIT_ULL(QCOM_SCM_VMID_HLOS);
  45. for (i = 0; i < mem_parcel->n_mem_entries; i++) {
  46. src_cpy = src;
  47. ret = qcom_scm_assign_mem(le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
  48. le64_to_cpu(mem_parcel->mem_entries[i].size),
  49. &src_cpy, new_perms, mem_parcel->n_acl_entries);
  50. if (ret)
  51. break;
  52. }
  53. if (!ret)
  54. goto out;
  55. src = 0;
  56. for (n = 0; n < mem_parcel->n_acl_entries; n++) {
  57. vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid);
  58. if (vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  59. src |= BIT_ULL(vmid);
  60. else
  61. src |= BIT_ULL(QCOM_SCM_RM_MANAGED_VMID);
  62. }
  63. new_perms[0].vmid = QCOM_SCM_VMID_HLOS;
  64. for (i--; i >= 0; i--) {
  65. src_cpy = src;
  66. rb_ret = qcom_scm_assign_mem(
  67. le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
  68. le64_to_cpu(mem_parcel->mem_entries[i].size),
  69. &src_cpy, new_perms, 1);
  70. WARN_ON_ONCE(rb_ret);
  71. if (rb_ret)
  72. /*
  73. * We have failed to assign pages back to the host.
  74. * Keep the pages pinned forever as they shouldn't be
  75. * accessed by host.
  76. */
  77. qcom_scm_gh_pin_pages(
  78. le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
  79. le64_to_cpu(mem_parcel->mem_entries[i].size));
  80. }
  81. out:
  82. kfree(new_perms);
  83. return ret;
  84. }
  85. static int qcom_scm_gh_rm_post_mem_reclaim(void *rm, struct gh_rm_mem_parcel *mem_parcel)
  86. {
  87. struct qcom_scm_vmperm new_perms;
  88. u64 src = 0, src_cpy;
  89. int ret = 0, i, n;
  90. u16 vmid;
  91. new_perms.vmid = QCOM_SCM_VMID_HLOS;
  92. new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | QCOM_SCM_PERM_READ;
  93. for (n = 0; n < mem_parcel->n_acl_entries; n++) {
  94. vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid);
  95. if (vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  96. src |= (1ull << vmid);
  97. else
  98. src |= (1ull << QCOM_SCM_RM_MANAGED_VMID);
  99. }
  100. for (i = 0; i < mem_parcel->n_mem_entries; i++) {
  101. src_cpy = src;
  102. ret = qcom_scm_assign_mem(le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
  103. le64_to_cpu(mem_parcel->mem_entries[i].size),
  104. &src_cpy, &new_perms, 1);
  105. WARN_ON_ONCE(ret);
  106. if (ret)
  107. /*
  108. * We have failed to assign pages back to the host.
  109. * Keep the pages pinned forever as they shouldn't be
  110. * accessed by host.
  111. */
  112. qcom_scm_gh_pin_pages(
  113. le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
  114. le64_to_cpu(mem_parcel->mem_entries[i].size));
  115. }
  116. return ret;
  117. }
  118. static struct gh_rm_platform_ops qcom_scm_gh_rm_platform_ops = {
  119. .pre_mem_share = qcom_scm_gh_rm_pre_mem_share,
  120. .post_mem_reclaim = qcom_scm_gh_rm_post_mem_reclaim,
  121. };
  122. /* {19bd54bd-0b37-571b-946f-609b54539de6} */
  123. static const uuid_t QCOM_EXT_UUID =
  124. UUID_INIT(0x19bd54bd, 0x0b37, 0x571b, 0x94, 0x6f, 0x60, 0x9b, 0x54, 0x53, 0x9d, 0xe6);
  125. #define GH_QCOM_EXT_CALL_UUID_ID ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
  126. ARM_SMCCC_OWNER_VENDOR_HYP, 0x3f01)
  127. static bool gh_has_qcom_extensions(void)
  128. {
  129. struct arm_smccc_res res;
  130. uuid_t uuid;
  131. u32 *up;
  132. arm_smccc_1_1_smc(GH_QCOM_EXT_CALL_UUID_ID, &res);
  133. up = (u32 *)&uuid.b[0];
  134. up[0] = lower_32_bits(res.a0);
  135. up[1] = lower_32_bits(res.a1);
  136. up[2] = lower_32_bits(res.a2);
  137. up[3] = lower_32_bits(res.a3);
  138. return uuid_equal(&uuid, &QCOM_EXT_UUID);
  139. }
  140. static int __init qcom_gh_platform_hooks_register(void)
  141. {
  142. if (!gh_has_qcom_extensions())
  143. return -ENODEV;
  144. return gh_rm_register_platform_ops(&qcom_scm_gh_rm_platform_ops);
  145. }
  146. static void __exit qcom_gh_platform_hooks_unregister(void)
  147. {
  148. gh_rm_unregister_platform_ops(&qcom_scm_gh_rm_platform_ops);
  149. }
  150. module_init(qcom_gh_platform_hooks_register);
  151. module_exit(qcom_gh_platform_hooks_unregister);
  152. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Platform Hooks for Gunyah");
  153. MODULE_LICENSE("GPL");