Blame view

drivers/trusty/trusty-test.c 10.6 KB
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2020 Google, Inc.
   */
  
  #include <linux/ctype.h>
  #include <linux/list.h>
  #include <linux/platform_device.h>
  #include <linux/trusty/smcall.h>
  #include <linux/trusty/trusty.h>
  #include <linux/scatterlist.h>
  #include <linux/slab.h>
  #include <linux/mm.h>
  #include <linux/mod_devicetable.h>
  #include <linux/module.h>
  
  #include "trusty-test.h"
  
  struct trusty_test_state {
  	struct device *dev;
  	struct device *trusty_dev;
  };
  
  struct trusty_test_shmem_obj {
  	struct list_head node;
  	size_t page_count;
  	struct page **pages;
  	void *buf;
  	struct sg_table sgt;
  	trusty_shared_mem_id_t mem_id;
  };
  
  /*
   * Allocate a test object with @page_count number of pages, map it and add it to
   * @list.
   * For multi-page allocations, order the pages so they are not contiguous.
   */
  static int trusty_test_alloc_obj(struct trusty_test_state *s,
  				 size_t page_count,
  				 struct list_head *list)
  {
  	size_t i;
  	int ret = -ENOMEM;
  	struct trusty_test_shmem_obj *obj;
  
  	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
  	if (!obj)
  		goto err_alloc_obj;
  	obj->page_count = page_count;
  
  	obj->pages = kmalloc_array(page_count, sizeof(*obj->pages), GFP_KERNEL);
  	if (!obj->pages) {
  		ret = -ENOMEM;
  		dev_err(s->dev, "failed to allocate page array, count %zd
  ",
  			page_count);
  		goto err_alloc_pages;
  	}
  
  	for (i = 0; i < page_count; i++) {
  		obj->pages[i] = alloc_page(GFP_KERNEL);
  		if (!obj->pages[i]) {
  			ret = -ENOMEM;
  			dev_err(s->dev, "failed to allocate page %zd/%zd
  ",
  				i, page_count);
  			goto err_alloc_page;
  		}
  		if (i > 0 && obj->pages[i - 1] + 1 == obj->pages[i]) {
  			/* swap adacent pages to increase fragmentation */
  			swap(obj->pages[i - 1], obj->pages[i]);
  		}
  	}
  
  	obj->buf = vmap(obj->pages, page_count, VM_MAP, PAGE_KERNEL);
  	if (!obj->buf) {
  		ret = -ENOMEM;
  		dev_err(s->dev, "failed to map test buffer page count %zd
  ",
  			page_count);
  		goto err_map_pages;
  	}
  
  	ret = sg_alloc_table_from_pages(&obj->sgt, obj->pages, page_count,
  					0, page_count * PAGE_SIZE, GFP_KERNEL);
  	if (ret) {
  		dev_err(s->dev, "sg_alloc_table_from_pages failed: %d
  ", ret);
  		goto err_alloc_sgt;
  	}
  	list_add_tail(&obj->node, list);
  	dev_dbg(s->dev, "buffer has %d page runs
  ", obj->sgt.nents);
  	return 0;
  
  err_alloc_sgt:
  	vunmap(obj->buf);
  err_map_pages:
  	for (i = page_count; i > 0; i--) {
  		__free_page(obj->pages[i - 1]);
  err_alloc_page:
  		;
  	}
  	kfree(obj->pages);
  err_alloc_pages:
  	kfree(obj);
  err_alloc_obj:
  	return ret;
  }
  
  /* Unlink, unmap and free a test object and its pages */
  static void trusty_test_free_obj(struct trusty_test_state *s,
  				 struct trusty_test_shmem_obj *obj)
  {
  	size_t i;
  
  	list_del(&obj->node);
  	sg_free_table(&obj->sgt);
  	vunmap(obj->buf);
  	for (i = obj->page_count; i > 0; i--)
  		__free_page(obj->pages[i - 1]);
  	kfree(obj->pages);
  	kfree(obj);
  }
  
  /*
   * Share all the pages of all the test object in &obj_list.
   * If sharing a test object fails, free it so that every test object that
   * remains in @obj_list has been shared when this function returns.
   * Return a error if any test object failed to be shared.
   */
  static int trusty_test_share_objs(struct trusty_test_state *s,
  				  struct list_head *obj_list, size_t size)
  {
  	int ret = 0;
  	int tmpret;
  	struct trusty_test_shmem_obj *obj;
  	struct trusty_test_shmem_obj *next_obj;
  	ktime_t t1;
  	ktime_t t2;
  
  	list_for_each_entry_safe(obj, next_obj, obj_list, node) {
  		t1 = ktime_get();
  		tmpret = trusty_share_memory(s->trusty_dev, &obj->mem_id,
  					     obj->sgt.sgl, obj->sgt.nents,
  					     PAGE_KERNEL);
  		t2 = ktime_get();
  		if (tmpret) {
  			ret = tmpret;
  			dev_err(s->dev,
  				"trusty_share_memory failed: %d, size=%zd
  ",
  				ret, size);
  
  			/*
  			 * Free obj and continue, so we can revoke the
  			 * whole list in trusty_test_reclaim_objs.
  			 */
  			trusty_test_free_obj(s, obj);
  		}
  		dev_dbg(s->dev, "share id=0x%llx, size=%zu took %lld ns
  ",
  			obj->mem_id, size,
  			ktime_to_ns(ktime_sub(t2, t1)));
  	}
  
  	return ret;
  }
  
  /* Reclaim memory shared with trusty for all test objects in @obj_list. */
  static int trusty_test_reclaim_objs(struct trusty_test_state *s,
  				    struct list_head *obj_list, size_t size)
  {
  	int ret = 0;
  	int tmpret;
  	struct trusty_test_shmem_obj *obj;
  	struct trusty_test_shmem_obj *next_obj;
  	ktime_t t1;
  	ktime_t t2;
  
  	list_for_each_entry_safe(obj, next_obj, obj_list, node) {
  		t1 = ktime_get();
  		tmpret = trusty_reclaim_memory(s->trusty_dev, obj->mem_id,
  					       obj->sgt.sgl, obj->sgt.nents);
  		t2 = ktime_get();
  		if (tmpret) {
  			ret = tmpret;
  			dev_err(s->dev,
  				"trusty_reclaim_memory failed: %d, id=0x%llx
  ",
  				ret, obj->mem_id);
  
  			/*
  			 * It is not safe to free this memory if
  			 * trusty_reclaim_memory fails. Leak it in that
  			 * case.
  			 */
  			list_del(&obj->node);
  		}
  		dev_dbg(s->dev, "revoke id=0x%llx, size=%zu took %lld ns
  ",
  			obj->mem_id, size,
  			ktime_to_ns(ktime_sub(t2, t1)));
  	}
  
  	return ret;
  }
  
  /*
   * Test a test object. First, initialize the memory, then make a std call into
   * trusty which will read it and return an error if the initialized value does
   * not match what it expects. If trusty reads the correct values, it will modify
   * the memory and return 0. This function then checks that it can read the
   * correct modified value.
   */
  static int trusty_test_rw(struct trusty_test_state *s,
  			  struct trusty_test_shmem_obj *obj)
  {
  	size_t size = obj->page_count * PAGE_SIZE;
  	int ret;
  	size_t i;
  	u64 *buf = obj->buf;
  	ktime_t t1;
  	ktime_t t2;
  
  	for (i = 0; i < size / sizeof(*buf); i++)
  		buf[i] = i;
  
  	t1 = ktime_get();
  	ret = trusty_std_call32(s->trusty_dev, SMC_SC_TEST_SHARED_MEM_RW,
  				(u32)(obj->mem_id), (u32)(obj->mem_id >> 32),
  				size);
  	t2 = ktime_get();
  	if (ret < 0) {
  		dev_err(s->dev,
  			"trusty std call (SMC_SC_TEST_SHARED_MEM_RW) failed: %d 0x%llx
  ",
  			ret, obj->mem_id);
  		return ret;
  	}
  
  	for (i = 0; i < size / sizeof(*buf); i++) {
  		if (buf[i] != size - i) {
  			dev_err(s->dev,
  				"input mismatch at %zd, got 0x%llx instead of 0x%zx
  ",
  				i, buf[i], size - i);
  			return -EIO;
  		}
  	}
  
  	dev_dbg(s->dev, "rw id=0x%llx, size=%zu took %lld ns
  ", obj->mem_id,
  		size, ktime_to_ns(ktime_sub(t2, t1)));
  
  	return 0;
  }
  
  /*
   * Run test on every test object in @obj_list. Repeat @repeat_access times.
   */
  static int trusty_test_rw_objs(struct trusty_test_state *s,
  			       struct list_head *obj_list,
  			       size_t repeat_access)
  {
  	int ret;
  	size_t i;
  	struct trusty_test_shmem_obj *obj;
  
  	for (i = 0; i < repeat_access; i++) {
  		/*
  		 * Repeat test in case the memory attributes don't match
  		 * and either side see old data.
  		 */
  		list_for_each_entry(obj, obj_list, node) {
  			ret = trusty_test_rw(s, obj);
  			if (ret)
  				return ret;
  		}
  	}
  
  	return 0;
  }
  
  /*
   * Allocate @obj_count test object that each have @page_count pages. Share each
   * object @repeat_share times, each time running tests on every object
   * @repeat_access times.
   */
  static int trusty_test_run(struct trusty_test_state *s, size_t page_count,
  			   size_t obj_count, size_t repeat_share,
  			   size_t repeat_access)
  {
  	int ret = 0;
  	int tmpret;
  	size_t i;
  	size_t size = page_count * PAGE_SIZE;
  	LIST_HEAD(obj_list);
  	struct trusty_test_shmem_obj *obj;
  	struct trusty_test_shmem_obj *next_obj;
  
  	for (i = 0; i < obj_count && !ret; i++)
  		ret = trusty_test_alloc_obj(s, page_count, &obj_list);
  
  	for (i = 0; i < repeat_share && !ret; i++) {
  		ret = trusty_test_share_objs(s, &obj_list, size);
  		if (ret) {
  			dev_err(s->dev,
  				"trusty_share_memory failed: %d, i=%zd/%zd, size=%zd
  ",
  				ret, i, repeat_share, size);
  		} else {
  			ret = trusty_test_rw_objs(s, &obj_list, repeat_access);
  			if (ret)
  				dev_err(s->dev,
  					"test failed: %d, i=%zd/%zd, size=%zd
  ",
  					ret, i, repeat_share, size);
  		}
  		tmpret = trusty_test_reclaim_objs(s, &obj_list, size);
  		if (tmpret) {
  			ret = tmpret;
  			dev_err(s->dev,
14914ebdb   Tri Vo   ANDROID: trusty: ...
324
325
326
  				"trusty_reclaim_memory failed: %d, i=%zd/%zd
  ",
  				ret, i, repeat_share);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  		}
  	}
  
  	list_for_each_entry_safe(obj, next_obj, &obj_list, node)
  		trusty_test_free_obj(s, obj);
  
  	dev_info(s->dev, "[ %s ] size %zd, obj_count %zd, repeat_share %zd, repeat_access %zd
  ",
  		 ret ? "FAILED" : "PASSED", size, obj_count, repeat_share,
  		 repeat_access);
  
  	return ret;
  }
  
  /*
   * Get an optional numeric argument from @buf, update @buf and return the value.
   * If @buf does not start with ",", return @default_val instead.
   */
  static size_t trusty_test_get_arg(const char **buf, size_t default_val)
  {
  	char *buf_next;
  	size_t ret;
  
  	if (**buf != ',')
  		return default_val;
  
  	(*buf)++;
  	ret = simple_strtoul(*buf, &buf_next, 0);
  	if (buf_next == *buf)
  		return default_val;
  
  	*buf = buf_next;
  
  	return ret;
  }
  
  /*
   * Run tests described by a string in this format:
   * <obj_size>,<obj_count=1>,<repeat_share=1>,<repeat_access=3>
   */
  static ssize_t trusty_test_run_store(struct device *dev,
  				     struct device_attribute *attr,
  				     const char *buf, size_t count)
  {
  	struct platform_device *pdev = to_platform_device(dev);
  	struct trusty_test_state *s = platform_get_drvdata(pdev);
  	size_t size;
  	size_t obj_count;
  	size_t repeat_share;
  	size_t repeat_access;
  	int ret;
  	char *buf_next;
  
  	while (true) {
  		while (isspace(*buf))
  			buf++;
  		size = simple_strtoul(buf, &buf_next, 0);
  		if (buf_next == buf)
  			return count;
  		buf = buf_next;
  		obj_count = trusty_test_get_arg(&buf, 1);
  		repeat_share = trusty_test_get_arg(&buf, 1);
  		repeat_access = trusty_test_get_arg(&buf, 3);
  
  		ret = trusty_test_run(s, DIV_ROUND_UP(size, PAGE_SIZE),
  				      obj_count, repeat_share, repeat_access);
  		if (ret)
  			return ret;
  	}
  }
ae60ab0af   Eric Biggers   ANDROID: trusty: ...
397
  static DEVICE_ATTR_WO(trusty_test_run);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
398

a08363afe   Eric Biggers   ANDROID: trusty-t...
399
400
401
402
403
  static struct attribute *trusty_test_attrs[] = {
  	&dev_attr_trusty_test_run.attr,
  	NULL,
  };
  ATTRIBUTE_GROUPS(trusty_test);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
404
405
406
407
  static int trusty_test_probe(struct platform_device *pdev)
  {
  	struct trusty_test_state *s;
  	int ret;
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
408
409
  	ret = trusty_std_call32(pdev->dev.parent, SMC_SC_TEST_VERSION,
  				TRUSTY_STDCALLTEST_API_VERSION, 0, 0);
a08363afe   Eric Biggers   ANDROID: trusty-t...
410
411
  	if (ret != TRUSTY_STDCALLTEST_API_VERSION)
  		return -ENOENT;
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
412
413
  
  	s = kzalloc(sizeof(*s), GFP_KERNEL);
a08363afe   Eric Biggers   ANDROID: trusty-t...
414
415
  	if (!s)
  		return -ENOMEM;
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
416
417
418
419
420
  
  	s->dev = &pdev->dev;
  	s->trusty_dev = s->dev->parent;
  
  	platform_set_drvdata(pdev, s);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
421
  	return 0;
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
422
423
424
425
426
  }
  
  static int trusty_test_remove(struct platform_device *pdev)
  {
  	struct trusty_log_state *s = platform_get_drvdata(pdev);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
427
  	kfree(s);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
428
429
430
431
432
433
434
  	return 0;
  }
  
  static const struct of_device_id trusty_test_of_match[] = {
  	{ .compatible = "android,trusty-test-v1", },
  	{},
  };
3c514f16a   Tri Vo   ANDROID: trusty: ...
435
  MODULE_DEVICE_TABLE(trusty, trusty_test_of_match);
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
436
437
438
439
440
  static struct platform_driver trusty_test_driver = {
  	.probe = trusty_test_probe,
  	.remove = trusty_test_remove,
  	.driver = {
  		.name = "trusty-test",
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
441
  		.of_match_table = trusty_test_of_match,
a08363afe   Eric Biggers   ANDROID: trusty-t...
442
  		.dev_groups = trusty_test_groups,
bf4d7b00c   Arve Hjønnevåg   ANDROID: trusty: ...
443
444
445
446
  	},
  };
  
  module_platform_driver(trusty_test_driver);
3c514f16a   Tri Vo   ANDROID: trusty: ...
447
448
449
  
  MODULE_LICENSE("GPL v2");
  MODULE_DESCRIPTION("Trusty test driver");