• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6 #include <linux/arm-smccc.h>
7 #include <linux/gunyah.h>
8 #include <linux/mm.h>
9 #include <linux/module.h>
10 #include <linux/firmware/qcom/qcom_scm.h>
11 #include <linux/types.h>
12 #include <linux/uuid.h>
13 
14 #define QCOM_SCM_RM_MANAGED_VMID 0x3A
15 #define QCOM_SCM_MAX_MANAGED_VMID 0x3F
16 
17 static int
qcom_scm_gunyah_rm_pre_mem_share(struct gunyah_rm * rm,struct gunyah_rm_mem_parcel * mem_parcel)18 qcom_scm_gunyah_rm_pre_mem_share(struct gunyah_rm *rm,
19 				 struct gunyah_rm_mem_parcel *mem_parcel)
20 {
21 	struct qcom_scm_vmperm *new_perms __free(kfree) = NULL;
22 	u64 src, src_cpy;
23 	int ret = 0, i, n;
24 	u16 vmid;
25 
26 	new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms),
27 			    GFP_KERNEL);
28 	if (!new_perms)
29 		return -ENOMEM;
30 
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 & GUNYAH_RM_ACL_X)
38 			new_perms[n].perm |= QCOM_SCM_PERM_EXEC;
39 		if (mem_parcel->acl_entries[n].perms & GUNYAH_RM_ACL_W)
40 			new_perms[n].perm |= QCOM_SCM_PERM_WRITE;
41 		if (mem_parcel->acl_entries[n].perms & GUNYAH_RM_ACL_R)
42 			new_perms[n].perm |= QCOM_SCM_PERM_READ;
43 	}
44 
45 	src = BIT_ULL(QCOM_SCM_VMID_HLOS);
46 
47 	for (i = 0; i < mem_parcel->n_mem_entries; i++) {
48 		src_cpy = src;
49 		ret = qcom_scm_assign_mem(
50 			le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
51 			le64_to_cpu(mem_parcel->mem_entries[i].size), &src_cpy,
52 			new_perms, mem_parcel->n_acl_entries);
53 		if (ret)
54 			break;
55 	}
56 
57 	/* Did it work ok? */
58 	if (!ret)
59 		return 0;
60 
61 	src = 0;
62 	for (n = 0; n < mem_parcel->n_acl_entries; n++) {
63 		vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid);
64 		if (vmid <= QCOM_SCM_MAX_MANAGED_VMID)
65 			src |= BIT_ULL(vmid);
66 		else
67 			src |= BIT_ULL(QCOM_SCM_RM_MANAGED_VMID);
68 	}
69 
70 	new_perms[0].vmid = QCOM_SCM_VMID_HLOS;
71 	new_perms[0].perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE |
72 			    QCOM_SCM_PERM_READ;
73 
74 	for (i--; i >= 0; i--) {
75 		src_cpy = src;
76 		WARN_ON_ONCE(qcom_scm_assign_mem(
77 			le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
78 			le64_to_cpu(mem_parcel->mem_entries[i].size), &src_cpy,
79 			new_perms, 1));
80 	}
81 
82 	return ret;
83 }
84 
85 static int
qcom_scm_gunyah_rm_post_mem_reclaim(struct gunyah_rm * rm,struct gunyah_rm_mem_parcel * mem_parcel)86 qcom_scm_gunyah_rm_post_mem_reclaim(struct gunyah_rm *rm,
87 				    struct gunyah_rm_mem_parcel *mem_parcel)
88 {
89 	struct qcom_scm_vmperm new_perms;
90 	u64 src = 0, src_cpy;
91 	int ret = 0, i, n;
92 	u16 vmid;
93 
94 	new_perms.vmid = QCOM_SCM_VMID_HLOS;
95 	new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE |
96 			 QCOM_SCM_PERM_READ;
97 
98 	for (n = 0; n < mem_parcel->n_acl_entries; n++) {
99 		vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid);
100 		if (vmid <= QCOM_SCM_MAX_MANAGED_VMID)
101 			src |= (1ull << vmid);
102 		else
103 			src |= (1ull << QCOM_SCM_RM_MANAGED_VMID);
104 	}
105 
106 	for (i = 0; i < mem_parcel->n_mem_entries; i++) {
107 		src_cpy = src;
108 		ret = qcom_scm_assign_mem(
109 			le64_to_cpu(mem_parcel->mem_entries[i].phys_addr),
110 			le64_to_cpu(mem_parcel->mem_entries[i].size), &src_cpy,
111 			&new_perms, 1);
112 		WARN_ON_ONCE(ret);
113 	}
114 
115 	return ret;
116 }
117 
118 static int
qcom_scm_gunyah_rm_pre_demand_page(struct gunyah_rm * rm,u16 vmid,enum gunyah_pagetable_access access,struct folio * folio)119 qcom_scm_gunyah_rm_pre_demand_page(struct gunyah_rm *rm, u16 vmid,
120 				   enum gunyah_pagetable_access access,
121 				   struct folio *folio)
122 {
123 	struct qcom_scm_vmperm new_perms[2];
124 	unsigned int n = 1;
125 	u64 src;
126 
127 	new_perms[0].vmid = QCOM_SCM_RM_MANAGED_VMID;
128 	new_perms[0].perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE |
129 			    QCOM_SCM_PERM_READ;
130 	if (access != GUNYAH_PAGETABLE_ACCESS_X &&
131 	    access != GUNYAH_PAGETABLE_ACCESS_RX &&
132 	    access != GUNYAH_PAGETABLE_ACCESS_RWX) {
133 		new_perms[1].vmid = QCOM_SCM_VMID_HLOS;
134 		new_perms[1].perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE |
135 				    QCOM_SCM_PERM_READ;
136 		n++;
137 	}
138 
139 	src = BIT_ULL(QCOM_SCM_VMID_HLOS);
140 
141 	return qcom_scm_assign_mem(__pfn_to_phys(folio_pfn(folio)),
142 				   folio_size(folio), &src, new_perms, n);
143 }
144 
145 static int
qcom_scm_gunyah_rm_release_demand_page(struct gunyah_rm * rm,u16 vmid,enum gunyah_pagetable_access access,struct folio * folio)146 qcom_scm_gunyah_rm_release_demand_page(struct gunyah_rm *rm, u16 vmid,
147 				       enum gunyah_pagetable_access access,
148 				       struct folio *folio)
149 {
150 	struct qcom_scm_vmperm new_perms;
151 	u64 src;
152 
153 	new_perms.vmid = QCOM_SCM_VMID_HLOS;
154 	new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE |
155 			 QCOM_SCM_PERM_READ;
156 
157 	src = BIT_ULL(QCOM_SCM_RM_MANAGED_VMID);
158 
159 	if (access != GUNYAH_PAGETABLE_ACCESS_X &&
160 	    access != GUNYAH_PAGETABLE_ACCESS_RX &&
161 	    access != GUNYAH_PAGETABLE_ACCESS_RWX)
162 		src |= BIT_ULL(QCOM_SCM_VMID_HLOS);
163 
164 	return qcom_scm_assign_mem(__pfn_to_phys(folio_pfn(folio)),
165 				   folio_size(folio), &src, &new_perms, 1);
166 }
167 
168 static struct gunyah_rm_platform_ops qcom_scm_gunyah_rm_platform_ops = {
169 	.pre_mem_share = qcom_scm_gunyah_rm_pre_mem_share,
170 	.post_mem_reclaim = qcom_scm_gunyah_rm_post_mem_reclaim,
171 	.pre_demand_page = qcom_scm_gunyah_rm_pre_demand_page,
172 	.release_demand_page = qcom_scm_gunyah_rm_release_demand_page,
173 };
174 
175 /* {19bd54bd-0b37-571b-946f-609b54539de6} */
176 static const uuid_t QCOM_EXT_UUID = UUID_INIT(0x19bd54bd, 0x0b37, 0x571b, 0x94,
177 					      0x6f, 0x60, 0x9b, 0x54, 0x53,
178 					      0x9d, 0xe6);
179 
180 #define GUNYAH_QCOM_EXT_CALL_UUID_ID                              \
181 	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
182 			   ARM_SMCCC_OWNER_VENDOR_HYP, 0x3f01)
183 
gunyah_has_qcom_extensions(void)184 static bool gunyah_has_qcom_extensions(void)
185 {
186 	struct arm_smccc_res res;
187 	uuid_t uuid;
188 	u32 *up;
189 
190 	arm_smccc_1_1_smc(GUNYAH_QCOM_EXT_CALL_UUID_ID, &res);
191 
192 	up = (u32 *)&uuid.b[0];
193 	up[0] = lower_32_bits(res.a0);
194 	up[1] = lower_32_bits(res.a1);
195 	up[2] = lower_32_bits(res.a2);
196 	up[3] = lower_32_bits(res.a3);
197 
198 	return uuid_equal(&uuid, &QCOM_EXT_UUID);
199 }
200 
qcom_gunyah_platform_hooks_register(void)201 static int __init qcom_gunyah_platform_hooks_register(void)
202 {
203 	if (!gunyah_has_qcom_extensions())
204 		return -ENODEV;
205 
206 	pr_info("Enabling Gunyah hooks for Qualcomm platforms.\n");
207 
208 	return gunyah_rm_register_platform_ops(
209 		&qcom_scm_gunyah_rm_platform_ops);
210 }
211 
qcom_gunyah_platform_hooks_unregister(void)212 static void __exit qcom_gunyah_platform_hooks_unregister(void)
213 {
214 	gunyah_rm_unregister_platform_ops(&qcom_scm_gunyah_rm_platform_ops);
215 }
216 
217 module_init(qcom_gunyah_platform_hooks_register);
218 module_exit(qcom_gunyah_platform_hooks_unregister);
219 MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Platform Hooks for Gunyah");
220 MODULE_LICENSE("GPL");
221