blob: dc705579883f76131298990f7d6820b49bf86afb [file] [log] [blame]
Patrick Dalya125d5d2016-09-30 16:16:10 -07001/*
2 * Copyright (C) 2011 Google, Inc
3 * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
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 */
15
16#include <linux/highmem.h>
17#include <linux/kernel.h>
18#include <linux/kref.h>
Patrick Dalya125d5d2016-09-30 16:16:10 -070019#include <linux/mutex.h>
20#include <linux/scatterlist.h>
21#include <linux/slab.h>
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -070022#include <linux/dma-mapping.h>
Patrick Dalya125d5d2016-09-30 16:16:10 -070023#include <soc/qcom/scm.h>
Neeti Desai8dcc3642015-03-17 18:20:35 -070024#include <soc/qcom/secure_buffer.h>
Patrick Dalya125d5d2016-09-30 16:16:10 -070025
26DEFINE_MUTEX(secure_buffer_mutex);
27
28struct cp2_mem_chunks {
29 u32 chunk_list;
30 u32 chunk_list_size;
31 u32 chunk_size;
32} __attribute__ ((__packed__));
33
34struct cp2_lock_req {
35 struct cp2_mem_chunks chunks;
36 u32 mem_usage;
37 u32 lock;
38} __attribute__ ((__packed__));
39
40
41struct mem_prot_info {
42 phys_addr_t addr;
43 u64 size;
44};
45
46struct info_list {
47 struct mem_prot_info *list_head;
48 u64 list_size;
49};
50
Patrick Dalya125d5d2016-09-30 16:16:10 -070051#define MEM_PROT_ASSIGN_ID 0x16
52#define MEM_PROTECT_LOCK_ID2 0x0A
53#define MEM_PROTECT_LOCK_ID2_FLAT 0x11
54#define V2_CHUNK_SIZE SZ_1M
55#define FEATURE_ID_CP 12
56
Neeti Desai0e64e702015-03-31 15:33:54 -070057struct dest_vm_and_perm_info {
58 u32 vm;
59 u32 perm;
60 u32 *ctx;
61 u32 ctx_size;
62};
63
64struct dest_info_list {
65 struct dest_vm_and_perm_info *dest_info;
66 u64 list_size;
67};
68
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -070069static void *qcom_secure_mem;
70#define QCOM_SECURE_MEM_SIZE (512*1024)
71#define PADDING 32
72
Patrick Dalya125d5d2016-09-30 16:16:10 -070073static int secure_buffer_change_chunk(u32 chunks,
74 u32 nchunks,
75 u32 chunk_size,
76 int lock)
77{
78 struct cp2_lock_req request;
79 u32 resp;
80 int ret;
81 struct scm_desc desc = {0};
82
83 desc.args[0] = request.chunks.chunk_list = chunks;
84 desc.args[1] = request.chunks.chunk_list_size = nchunks;
85 desc.args[2] = request.chunks.chunk_size = chunk_size;
86 /* Usage is now always 0 */
87 desc.args[3] = request.mem_usage = 0;
88 desc.args[4] = request.lock = lock;
89 desc.args[5] = 0;
90 desc.arginfo = SCM_ARGS(6, SCM_RW, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL,
91 SCM_VAL);
92
93 kmap_flush_unused();
94 kmap_atomic_flush_unused();
95
96 if (!is_scm_armv8()) {
97 ret = scm_call(SCM_SVC_MP, MEM_PROTECT_LOCK_ID2,
98 &request, sizeof(request), &resp, sizeof(resp));
99 } else {
100 ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
101 MEM_PROTECT_LOCK_ID2_FLAT), &desc);
102 resp = desc.ret[0];
103 }
104
105 return ret;
106}
107
108
109
110static int secure_buffer_change_table(struct sg_table *table, int lock)
111{
112 int i, j;
113 int ret = -EINVAL;
114 u32 *chunk_list;
115 struct scatterlist *sg;
116
117 for_each_sg(table->sgl, sg, table->nents, i) {
118 int nchunks;
119 int size = sg->length;
120 int chunk_list_len;
121 phys_addr_t chunk_list_phys;
122
123 /*
124 * This should theoretically be a phys_addr_t but the protocol
125 * indicates this should be a u32.
126 */
127 u32 base;
128 u64 tmp = sg_dma_address(sg);
129
130 WARN((tmp >> 32) & 0xffffffff,
131 "%s: there are ones in the upper 32 bits of the sg at %p! They will be truncated! Address: 0x%llx\n",
132 __func__, sg, tmp);
133 if (unlikely(!size || (size % V2_CHUNK_SIZE))) {
134 WARN(1,
135 "%s: chunk %d has invalid size: 0x%x. Must be a multiple of 0x%x\n",
136 __func__, i, size, V2_CHUNK_SIZE);
137 return -EINVAL;
138 }
139
140 base = (u32)tmp;
141
142 nchunks = size / V2_CHUNK_SIZE;
143 chunk_list_len = sizeof(u32)*nchunks;
144
145 chunk_list = kzalloc(chunk_list_len, GFP_KERNEL);
146
147 if (!chunk_list)
148 return -ENOMEM;
149
150 chunk_list_phys = virt_to_phys(chunk_list);
151 for (j = 0; j < nchunks; j++)
152 chunk_list[j] = base + j * V2_CHUNK_SIZE;
153
154 /*
155 * Flush the chunk list before sending the memory to the
156 * secure environment to ensure the data is actually present
157 * in RAM
158 */
159 dmac_flush_range(chunk_list, chunk_list + chunk_list_len);
160
161 ret = secure_buffer_change_chunk(virt_to_phys(chunk_list),
162 nchunks, V2_CHUNK_SIZE, lock);
163
164 if (!ret) {
165 /*
166 * Set or clear the private page flag to communicate the
167 * status of the chunk to other entities
168 */
169 if (lock)
170 SetPagePrivate(sg_page(sg));
171 else
172 ClearPagePrivate(sg_page(sg));
173 }
174
175 kfree(chunk_list);
176 }
177
178 return ret;
179}
180
Neeti Desai8dcc3642015-03-17 18:20:35 -0700181int msm_secure_table(struct sg_table *table)
Patrick Dalya125d5d2016-09-30 16:16:10 -0700182{
183 int ret;
184
185 mutex_lock(&secure_buffer_mutex);
186 ret = secure_buffer_change_table(table, 1);
187 mutex_unlock(&secure_buffer_mutex);
188
189 return ret;
190
191}
192
Neeti Desai8dcc3642015-03-17 18:20:35 -0700193int msm_unsecure_table(struct sg_table *table)
Patrick Dalya125d5d2016-09-30 16:16:10 -0700194{
195 int ret;
196
197 mutex_lock(&secure_buffer_mutex);
198 ret = secure_buffer_change_table(table, 0);
199 mutex_unlock(&secure_buffer_mutex);
200 return ret;
201
202}
203
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700204static void populate_dest_info(int *dest_vmids, int nelements,
205 int *dest_perms, struct dest_info_list **list,
206 void *current_qcom_secure_mem)
Neeti Desai0e64e702015-03-31 15:33:54 -0700207{
208 struct dest_vm_and_perm_info *dest_info;
Neeti Desai0e64e702015-03-31 15:33:54 -0700209 int i;
210
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700211 dest_info = (struct dest_vm_and_perm_info *)current_qcom_secure_mem;
Neeti Desai0e64e702015-03-31 15:33:54 -0700212
213 for (i = 0; i < nelements; i++) {
214 dest_info[i].vm = dest_vmids[i];
215 dest_info[i].perm = dest_perms[i];
216 dest_info[i].ctx = NULL;
217 dest_info[i].ctx_size = 0;
218 }
Neeti Desai0e64e702015-03-31 15:33:54 -0700219
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700220 *list = (struct dest_info_list *)&dest_info[i];
Neeti Desai0e64e702015-03-31 15:33:54 -0700221
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700222 (*list)->dest_info = dest_info;
223 (*list)->list_size = nelements * sizeof(struct dest_vm_and_perm_info);
Neeti Desai0e64e702015-03-31 15:33:54 -0700224}
225
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700226static void get_info_list_from_table(struct sg_table *table,
227 struct info_list **list)
Patrick Dalya125d5d2016-09-30 16:16:10 -0700228{
229 int i;
230 struct scatterlist *sg;
231 struct mem_prot_info *info;
Patrick Dalya125d5d2016-09-30 16:16:10 -0700232
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700233 info = (struct mem_prot_info *)qcom_secure_mem;
Patrick Dalya125d5d2016-09-30 16:16:10 -0700234
235 for_each_sg(table->sgl, sg, table->nents, i) {
236 info[i].addr = page_to_phys(sg_page(sg));
237 info[i].size = sg->length;
238 }
239
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700240 *list = (struct info_list *)&(info[i]);
Patrick Dalya125d5d2016-09-30 16:16:10 -0700241
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700242 (*list)->list_head = info;
243 (*list)->list_size = table->nents * sizeof(struct mem_prot_info);
Neeti Desai0e64e702015-03-31 15:33:54 -0700244}
245
246int hyp_assign_table(struct sg_table *table,
247 u32 *source_vm_list, int source_nelems,
248 int *dest_vmids, int *dest_perms,
249 int dest_nelems)
250{
Patrick Dalya125d5d2016-09-30 16:16:10 -0700251 int ret;
Neeti Desai0e64e702015-03-31 15:33:54 -0700252 struct info_list *info_list = NULL;
253 struct dest_info_list *dest_info_list = NULL;
Patrick Dalya125d5d2016-09-30 16:16:10 -0700254 struct scm_desc desc = {0};
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800255 u32 *source_vm_copy;
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700256 void *current_qcom_secure_mem;
Patrick Dalya125d5d2016-09-30 16:16:10 -0700257
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700258 size_t reqd_size = dest_nelems * sizeof(struct dest_vm_and_perm_info) +
259 table->nents * sizeof(struct mem_prot_info) +
260 sizeof(dest_info_list) + sizeof(info_list) + PADDING;
261
262 if (!qcom_secure_mem) {
263 pr_err("%s is not functional as qcom_secure_mem is not allocated.\n",
264 __func__);
Neeti Desai0e64e702015-03-31 15:33:54 -0700265 return -ENOMEM;
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700266 }
Neeti Desai0e64e702015-03-31 15:33:54 -0700267
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700268 if (reqd_size > QCOM_SECURE_MEM_SIZE) {
269 pr_err("%s: Not enough memory allocated. Required size %zd\n",
270 __func__, reqd_size);
271 return -EINVAL;
Neeti Desai0e64e702015-03-31 15:33:54 -0700272 }
Patrick Dalya125d5d2016-09-30 16:16:10 -0700273
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800274 /*
275 * We can only pass cache-aligned sizes to hypervisor, so we need
276 * to kmalloc and memcpy the source_vm_list here.
277 */
278 source_vm_copy = kmalloc_array(
279 source_nelems, sizeof(*source_vm_copy), GFP_KERNEL);
280 if (!source_vm_copy) {
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700281 return -ENOMEM;
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800282 }
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700283
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800284 memcpy(source_vm_copy, source_vm_list,
285 sizeof(*source_vm_list) * source_nelems);
286
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700287 mutex_lock(&secure_buffer_mutex);
288
289 get_info_list_from_table(table, &info_list);
290
291 current_qcom_secure_mem = &(info_list[1]);
292 populate_dest_info(dest_vmids, dest_nelems, dest_perms,
293 &dest_info_list, current_qcom_secure_mem);
294
Patrick Dalya125d5d2016-09-30 16:16:10 -0700295 desc.args[0] = virt_to_phys(info_list->list_head);
296 desc.args[1] = info_list->list_size;
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800297 desc.args[2] = virt_to_phys(source_vm_copy);
298 desc.args[3] = sizeof(*source_vm_copy) * source_nelems;
Neeti Desai0e64e702015-03-31 15:33:54 -0700299 desc.args[4] = virt_to_phys(dest_info_list->dest_info);
300 desc.args[5] = dest_info_list->list_size;
Patrick Dalya125d5d2016-09-30 16:16:10 -0700301 desc.args[6] = 0;
Neeti Desai0e64e702015-03-31 15:33:54 -0700302
Patrick Dalya125d5d2016-09-30 16:16:10 -0700303 desc.arginfo = SCM_ARGS(7, SCM_RO, SCM_VAL, SCM_RO, SCM_VAL, SCM_RO,
304 SCM_VAL, SCM_VAL);
305
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800306 dmac_flush_range(source_vm_copy, source_vm_copy + source_nelems);
Patrick Dalya125d5d2016-09-30 16:16:10 -0700307 dmac_flush_range(info_list->list_head, info_list->list_head +
Neeti Desai0e64e702015-03-31 15:33:54 -0700308 (info_list->list_size / sizeof(*info_list->list_head)));
309 dmac_flush_range(dest_info_list->dest_info, dest_info_list->dest_info +
310 (dest_info_list->list_size /
311 sizeof(*dest_info_list->dest_info)));
Patrick Dalya125d5d2016-09-30 16:16:10 -0700312
313 ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
314 MEM_PROT_ASSIGN_ID), &desc);
315 if (ret)
316 pr_info("%s: Failed to assign memory protection, ret = %d\n",
317 __func__, ret);
Neeti Desai0e64e702015-03-31 15:33:54 -0700318
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700319 mutex_unlock(&secure_buffer_mutex);
Mitchel Humpherysa0183542015-11-23 13:18:47 -0800320 kfree(source_vm_copy);
Patrick Dalya125d5d2016-09-30 16:16:10 -0700321 return ret;
322}
323
Neeti Desaif85f5662015-06-05 18:44:24 -0700324int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list,
325 int source_nelems, int *dest_vmids,
326 int *dest_perms, int dest_nelems)
Neeti Desai0e64e702015-03-31 15:33:54 -0700327{
328 struct sg_table *table;
Neeti Desai0e64e702015-03-31 15:33:54 -0700329 int ret;
330
331 table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
332 if (!table)
333 return -ENOMEM;
334 ret = sg_alloc_table(table, 1, GFP_KERNEL);
335 if (ret)
336 goto err1;
337
338 sg_set_page(table->sgl, phys_to_page(addr), size, 0);
339
Neeti Desaif85f5662015-06-05 18:44:24 -0700340 ret = hyp_assign_table(table, source_vm_list, source_nelems, dest_vmids,
Neeti Desai0e64e702015-03-31 15:33:54 -0700341 dest_perms, dest_nelems);
342 if (ret)
343 goto err2;
344
345 return ret;
346err2:
347 sg_free_table(table);
348err1:
349 kfree(table);
350 return ret;
351}
352
Mitchel Humpherys5b8290a2015-07-30 19:24:15 -0700353const char *msm_secure_vmid_to_string(int secure_vmid)
354{
355 switch (secure_vmid) {
356 case VMID_HLOS:
357 return "VMID_HLOS";
358 case VMID_CP_TOUCH:
359 return "VMID_CP_TOUCH";
360 case VMID_CP_BITSTREAM:
361 return "VMID_CP_BITSTREAM";
362 case VMID_CP_PIXEL:
363 return "VMID_CP_PIXEL";
364 case VMID_CP_NON_PIXEL:
365 return "VMID_CP_NON_PIXEL";
366 case VMID_CP_CAMERA:
367 return "VMID_CP_CAMERA";
368 case VMID_HLOS_FREE:
369 return "VMID_HLOS_FREE";
370 case VMID_MSS_MSA:
371 return "VMID_MSS_MSA";
372 case VMID_MSS_NONMSA:
373 return "VMID_MSS_NONMSA";
374 case VMID_CP_SEC_DISPLAY:
375 return "VMID_CP_SEC_DISPLAY";
376 case VMID_CP_APP:
377 return "VMID_CP_APP";
378 case VMID_INVAL:
379 return "VMID_INVAL";
380 default:
381 return "Unknown VMID";
382 }
383}
384
Patrick Dalya125d5d2016-09-30 16:16:10 -0700385#define MAKE_CP_VERSION(major, minor, patch) \
386 (((major & 0x3FF) << 22) | ((minor & 0x3FF) << 12) | (patch & 0xFFF))
387
388bool msm_secure_v2_is_supported(void)
389{
390 int version = scm_get_feat_version(FEATURE_ID_CP);
391
392 /*
393 * if the version is < 1.1.0 then dynamic buffer allocation is
394 * not supported
395 */
396 return version >= MAKE_CP_VERSION(1, 1, 0);
397}
Rohit Vaswani78dfd5a2015-10-21 17:22:16 -0700398
399static int __init alloc_secure_shared_memory(void)
400{
401 int ret = 0;
402
403 qcom_secure_mem = kzalloc(QCOM_SECURE_MEM_SIZE, GFP_KERNEL);
404 if (!qcom_secure_mem) {
405 /* Fallback to CMA-DMA memory */
406 qcom_secure_mem = dma_alloc_coherent(NULL, QCOM_SECURE_MEM_SIZE,
407 NULL, GFP_KERNEL);
408 if (!qcom_secure_mem) {
409 pr_err("Couldn't allocate memory for secure use-cases. hyp_assign_table will not work\n");
410 return -ENOMEM;
411 }
412 }
413
414 return ret;
415}
416pure_initcall(alloc_secure_shared_memory);