1 /*
2 * Copyright (C) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <linux/slab.h>
20 #include <linux/dma-mapping.h>
21 #include <linux/semaphore.h>
22 #include <linux/semaphore.h>
23 #include <asm/cacheflush.h>
24 #include <linux/securec.h>
25 #include "drv_mmz.h"
26 #include "drv_tzsmmu.h"
27 #include "drv_media_mem.h"
28
29 #define HI_ZERO 0
30
31 #define MAX_MEMBLOCK_NUM (1024 * 1024)
32
33 /* func: set secure flag and create sec smmu map
34 * input no-secure mem info(smmu addr or phys addr,indicated by iommu),
35 * and return secure address( sec smmu addr or phys addr)
36 */
secmem_alloc(mmb_addr_t phyaddr,unsigned int iommu)37 u32 secmem_alloc(mmb_addr_t phyaddr, unsigned int iommu)
38 {
39 u32 size;
40 int ret;
41 struct tee_mem_info teebuf = {0};
42 struct tee_mem_addr mem_addr = {0};
43 u32 base_addr = 0;
44
45 teebuf.table = get_meminfo(phyaddr, iommu, &size, &base_addr);
46 if (teebuf.table == NULL) {
47 hi_mmz_warn("Cannot get meminfo and check parameter!\n");
48 goto out;
49 }
50 teebuf.size = PAGE_ALIGN(size);
51
52 mem_addr.iommu = iommu;
53 mem_addr.addr = base_addr;
54 ret = hisi_secmem_alloc(&teebuf, &mem_addr, NULL, 0); /* 0 do not used */
55 if (ret || !mem_addr.sec_addr) {
56 goto out;
57 }
58
59 /* set mmb flag to indicate that the mem is secure */
60 set_sec_mmb_flag(phyaddr, iommu);
61 sec_mmb_get(phyaddr, iommu, mem_addr.sec_addr);
62
63 return iommu ? mem_addr.sec_addr : phyaddr;
64
65 out:
66 return MMB_ADDR_INVALID;
67 }
68
69 /* func: clear secure flag and unmap the sec smmu
70 * input sec mem info (sec smmu addr or phys addr, indicated by iommu),
71 * and output non-sec info (smmu addr or phys addr)
72 */
secmem_free(mmb_addr_t sec_addr,unsigned int iommu)73 u32 secmem_free(mmb_addr_t sec_addr, unsigned int iommu)
74 {
75 int ret;
76 u32 addr;
77 int sec_smmu_ref;
78 struct tee_mem_info teebuf = {0};
79 u32 size = 0;
80 u32 base_addr = 0;
81 u32 offset;
82 u32 sec_base_addr;
83
84 if (iommu) {
85 addr = (u32)get_nonsecsmmu_by_secsmmu(sec_addr);
86 } else {
87 addr = (u32)sec_addr;
88 }
89
90 teebuf.table = get_meminfo(addr, iommu, &size, &base_addr);
91 if (teebuf.table == NULL) {
92 hi_mmz_warn("Cannot get meminfo and check parameter!\n");
93 goto exit;
94 }
95 offset = addr - base_addr;
96 sec_base_addr = sec_addr - offset;
97 sec_smmu_ref = sec_mmb_put(base_addr, iommu);
98 if (sec_smmu_ref) {
99 /*
100 * free later. warning: map sec-smmu many times
101 */
102 goto exit;
103 }
104
105 ret = hisi_secmem_free(sec_base_addr, iommu);
106 if (ret) {
107 hi_mmz_warn("hisi_secmem_free failed!\n");
108 goto out;
109 }
110
111 clr_sec_mmb_flag(base_addr, iommu);
112
113 return base_addr;
114
115 out:
116 sec_mmb_get(addr, iommu, sec_base_addr);
117 exit:
118 return MMB_ADDR_INVALID;
119 }
120
121 /* func: map to sec smmu
122 * addr:input, smmu or phy address in normal world
123 * iommu: address type
124 * sec smmu address is returned when exec success
125 * MMB_ADDR_INVALID is returned when exec failed
126 */
secmem_map_to_secsmmu(HI_U32 phyaddr,int iommu)127 u32 secmem_map_to_secsmmu(HI_U32 phyaddr, int iommu)
128 {
129 struct tee_mem_info teebuf = {0};
130 struct tee_mem_addr mem_addr = {0};
131 u32 base_addr = 0;
132 u32 size = 0;
133 int ret;
134 u32 offset;
135
136 if (iommu) {
137 mem_addr.sec_addr = get_sec_smmu_by_nosmmu(phyaddr);
138 } else {
139 mem_addr.sec_addr = get_sec_smmu_by_phys(phyaddr);
140 }
141 if (mem_addr.sec_addr != MMB_ADDR_INVALID) {
142 /* map to sec smmu before,and give the previous value */
143 offset = 0;
144 goto out;
145 }
146
147 teebuf.table = get_meminfo(phyaddr, iommu, &size, &base_addr);
148 if (teebuf.table == NULL) {
149 hi_mmz_warn("Cannot get meminfo and check parameter!\n");
150 goto exit;
151 }
152 teebuf.size = PAGE_ALIGN(size);
153
154 mem_addr.iommu = iommu;
155 mem_addr.addr = base_addr;
156 ret = hisi_secmem_mapto_secsmmu(&teebuf, &mem_addr, NULL, 0); /* 0 not used */
157 if (ret) {
158 hi_mmz_warn("hisi_secmem_mapto_secsmmu failed!\n");
159 goto exit;
160 }
161 offset = phyaddr - mem_addr.addr;
162 out:
163 sec_mmb_get(phyaddr, iommu, mem_addr.sec_addr);
164
165 return (mem_addr.sec_addr + offset);
166
167 exit:
168 return MMB_ADDR_INVALID;
169 }
170
171 /* func: unmap from sec smmu
172 * secsmmu:input, smmu address in sec world
173 * iommu: memtype indicate the mem attr(mmz or system)
174 * 0 is returned when exec success
175 * non-zero is returned when exec failed
176 */
secmem_unmap_from_secsmmu(HI_U32 sec_addr,int iommu)177 int secmem_unmap_from_secsmmu(HI_U32 sec_addr, int iommu)
178 {
179 int ret;
180 int sec_smmu_ref;
181 unsigned int addr;
182 struct tee_mem_info teebuf = {0};
183 u32 size = 0;
184 u32 base_addr = 0;
185 u32 sec_addr_base;
186
187 if (iommu) {
188 /* the mem is system mem */
189 addr = get_nonsecsmmu_by_secsmmu(sec_addr);
190 } else {
191 /* the mem is system mem */
192 addr = get_phys_by_secsmmu(sec_addr);
193 }
194 teebuf.table = get_meminfo(addr, iommu, &size, &base_addr);
195 if (teebuf.table == NULL) {
196 hi_mmz_warn("Cannot get meminfo and check parameter!\n");
197 goto err;
198 }
199
200 sec_addr_base = sec_addr - (addr - base_addr);
201 sec_smmu_ref = sec_mmb_put(base_addr, iommu);
202 if (sec_smmu_ref) {
203 /* free later. warning: map sec-smmu many times */
204 goto out;
205 }
206
207 if (is_sec_mem(base_addr, iommu)) {
208 /* mem is secure mem, and should be clear sec flag and unmap sec smmu. */
209 ret = hisi_secmem_free(sec_addr_base, 1);
210 if (ret != 0) {
211 hi_mmz_warn("hisi_secmem_free failed!\n");
212 goto exit;
213 }
214 clr_sec_mmb_flag(base_addr, iommu);
215 } else {
216 /*
217 * mem is normal mem, and just should be unmap sec smmu.
218 */
219 ret = hisi_secmem_unmap_from_secsmmu(sec_addr_base);
220 if (ret) {
221 hi_mmz_warn("hisi_secmem_unmap_from_secsmmu failed!\n");
222 goto exit;
223 }
224 }
225
226 /*
227 * the mem may not be free here, but it is just a repair the mistake for the exceptional calling order like:
228 * 1 secmem_alloc --- secmem_map_to_secsmmu --- secmem_free --- secmem_unmap_from_secsmmu
229 * 2 nomal alloc --- secmem_map_to_secsmmu --- normal free --- secmem_unmap_from_secsmmu
230 */
231 sec_delay_release_for_mem(base_addr, iommu);
232 out:
233 return 0;
234 exit:
235 sec_mmb_get(base_addr, iommu, sec_addr_base);
236 err:
237 return -1;
238 }
239
hi_tee_agent_end(void)240 int hi_tee_agent_end(void)
241 {
242 return hisi_secmem_agent_end();
243 }
244
sec_mem_get(HI_U32 sec_addr,int iommu)245 int sec_mem_get(HI_U32 sec_addr, int iommu)
246 {
247 HI_U32 addr = MMB_ADDR_INVALID;
248
249 if (iommu) {
250 addr = get_nonsecsmmu_by_secsmmu(sec_addr);
251 } else {
252 addr = sec_addr;
253 }
254
255 if (addr == MMB_ADDR_INVALID) {
256 hi_mmz_warn("err args:addr:0x%x iommu:%d!\n", sec_addr, iommu);
257 return HI_FAILURE;
258 }
259
260 if (!is_sec_mem(addr, iommu)) {
261 hi_mmz_warn("The mem should be sec!\n");
262 return HI_FAILURE;
263 }
264
265 /* mem sec_smmu_ref is not 0, ignore sec_addr */
266 return sec_mmb_get(addr, iommu, sec_addr);
267 }
268
sec_mem_put(HI_U32 sec_addr,int iommu)269 int sec_mem_put(HI_U32 sec_addr, int iommu)
270 {
271 HI_U32 addr = MMB_ADDR_INVALID;
272 int ret;
273
274 if (iommu) {
275 addr = get_nonsecsmmu_by_secsmmu(sec_addr);
276 } else {
277 addr = sec_addr;
278 }
279
280 if (addr == MMB_ADDR_INVALID) {
281 hi_mmz_warn("err args: addr:0x%x iommu:%d!\n", sec_addr, iommu);
282 return HI_FAILURE;
283 }
284
285 if (!is_sec_mem(addr, iommu)) {
286 hi_mmz_warn("The mem should be sec!\n");
287 return HI_FAILURE;
288 }
289
290 ret = sec_mmb_put(addr, iommu);
291 if (ret < 0) {
292 hi_mmz_warn("call wrong times, the sec ref is %d.\n", ret);
293 return HI_FAILURE;
294 } else if (ret > 0) {
295 /* just decrement the ref */
296 return HI_SUCCESS;
297 } else {
298 ret = hisi_secmem_free(sec_addr, iommu);
299 if (ret) {
300 hi_mmz_warn("hisi_secmem_free failed!\n");
301 return HI_FAILURE;
302 }
303
304 clr_sec_mmb_flag(addr, iommu);
305 delete_mmb(addr, iommu);
306 }
307
308 return HI_SUCCESS;
309 }
310
sec_mem_buf_query_ref(HI_U32 sec_addr,int iommu,HI_U32 * ref)311 int sec_mem_buf_query_ref(HI_U32 sec_addr, int iommu, HI_U32 *ref)
312 {
313 if (ref == NULL) {
314 hi_mmz_warn("ref should not be null!\n");
315 return HI_FAILURE;
316 }
317
318 return sec_mmb_query_ref(sec_addr, iommu, ref);
319 }
320
321 EXPORT_SYMBOL(secmem_alloc);
322 EXPORT_SYMBOL(secmem_free);
323 EXPORT_SYMBOL(secmem_map_to_secsmmu);
324 EXPORT_SYMBOL(secmem_unmap_from_secsmmu);
325 EXPORT_SYMBOL(hi_tee_agent_end);
326 EXPORT_SYMBOL(sec_mem_get);
327 EXPORT_SYMBOL(sec_mem_put);
328 EXPORT_SYMBOL(sec_mem_buf_query_ref);
329
330