1 /*
2 * Copyright (c) 2013-2018, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * * Neither the name of Intel Corporation nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "pt_image.h"
30 #include "pt_section.h"
31 #include "pt_asid.h"
32 #include "pt_image_section_cache.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36
37
dupstr(const char * str)38 static char *dupstr(const char *str)
39 {
40 char *dup;
41 size_t len;
42
43 if (!str)
44 return NULL;
45
46 len = strlen(str);
47 dup = malloc(len + 1);
48 if (!dup)
49 return NULL;
50
51 return strcpy(dup, str);
52 }
53
pt_mk_section_list(struct pt_section * section,const struct pt_asid * asid,uint64_t vaddr,uint64_t offset,uint64_t size,int isid)54 static struct pt_section_list *pt_mk_section_list(struct pt_section *section,
55 const struct pt_asid *asid,
56 uint64_t vaddr,
57 uint64_t offset,
58 uint64_t size, int isid)
59 {
60 struct pt_section_list *list;
61 int errcode;
62
63 list = malloc(sizeof(*list));
64 if (!list)
65 return NULL;
66
67 memset(list, 0, sizeof(*list));
68
69 errcode = pt_section_get(section);
70 if (errcode < 0)
71 goto out_mem;
72
73 pt_msec_init(&list->section, section, asid, vaddr, offset, size);
74 list->isid = isid;
75
76 return list;
77
78 out_mem:
79 free(list);
80 return NULL;
81 }
82
pt_section_list_free(struct pt_section_list * list)83 static void pt_section_list_free(struct pt_section_list *list)
84 {
85 if (!list)
86 return;
87
88 pt_section_put(list->section.section);
89 pt_msec_fini(&list->section);
90 free(list);
91 }
92
pt_section_list_free_tail(struct pt_section_list * list)93 static void pt_section_list_free_tail(struct pt_section_list *list)
94 {
95 while (list) {
96 struct pt_section_list *trash;
97
98 trash = list;
99 list = list->next;
100
101 pt_section_list_free(trash);
102 }
103 }
104
pt_image_init(struct pt_image * image,const char * name)105 void pt_image_init(struct pt_image *image, const char *name)
106 {
107 if (!image)
108 return;
109
110 memset(image, 0, sizeof(*image));
111
112 image->name = dupstr(name);
113 }
114
pt_image_fini(struct pt_image * image)115 void pt_image_fini(struct pt_image *image)
116 {
117 if (!image)
118 return;
119
120 pt_section_list_free_tail(image->sections);
121 free(image->name);
122
123 memset(image, 0, sizeof(*image));
124 }
125
pt_image_alloc(const char * name)126 struct pt_image *pt_image_alloc(const char *name)
127 {
128 struct pt_image *image;
129
130 image = malloc(sizeof(*image));
131 if (image)
132 pt_image_init(image, name);
133
134 return image;
135 }
136
pt_image_free(struct pt_image * image)137 void pt_image_free(struct pt_image *image)
138 {
139 pt_image_fini(image);
140 free(image);
141 }
142
pt_image_name(const struct pt_image * image)143 const char *pt_image_name(const struct pt_image *image)
144 {
145 if (!image)
146 return NULL;
147
148 return image->name;
149 }
150
pt_image_add(struct pt_image * image,struct pt_section * section,const struct pt_asid * asid,uint64_t vaddr,int isid)151 int pt_image_add(struct pt_image *image, struct pt_section *section,
152 const struct pt_asid *asid, uint64_t vaddr, int isid)
153 {
154 struct pt_section_list **list, *next, *removed, *new;
155 uint64_t size, begin, end;
156 int errcode;
157
158 if (!image || !section)
159 return -pte_internal;
160
161 size = pt_section_size(section);
162 begin = vaddr;
163 end = begin + size;
164
165 next = pt_mk_section_list(section, asid, begin, 0ull, size, isid);
166 if (!next)
167 return -pte_nomem;
168
169 removed = NULL;
170 errcode = 0;
171
172 /* Check for overlaps while we move to the end of the list. */
173 list = &(image->sections);
174 while (*list) {
175 const struct pt_mapped_section *msec;
176 const struct pt_asid *masid;
177 struct pt_section_list *current;
178 struct pt_section *lsec;
179 uint64_t lbegin, lend, loff;
180
181 current = *list;
182 msec = ¤t->section;
183 masid = pt_msec_asid(msec);
184
185 errcode = pt_asid_match(masid, asid);
186 if (errcode < 0)
187 break;
188
189 if (!errcode) {
190 list = &((*list)->next);
191 continue;
192 }
193
194 lbegin = pt_msec_begin(msec);
195 lend = pt_msec_end(msec);
196
197 if ((end <= lbegin) || (lend <= begin)) {
198 list = &((*list)->next);
199 continue;
200 }
201
202 /* The new section overlaps with @msec's section. */
203 lsec = pt_msec_section(msec);
204 loff = pt_msec_offset(msec);
205
206 /* We remove @msec and insert new sections for the remaining
207 * parts, if any. Those new sections are not mapped initially
208 * and need to be added to the end of the section list.
209 */
210 *list = current->next;
211
212 /* Keep a list of removed sections so we can re-add them in case
213 * of errors.
214 */
215 current->next = removed;
216 removed = current;
217
218 /* Add a section covering the remaining bytes at the front. */
219 if (lbegin < begin) {
220 new = pt_mk_section_list(lsec, masid, lbegin, loff,
221 begin - lbegin, current->isid);
222 if (!new) {
223 errcode = -pte_nomem;
224 break;
225 }
226
227 new->next = next;
228 next = new;
229 }
230
231 /* Add a section covering the remaining bytes at the back. */
232 if (end < lend) {
233 new = pt_mk_section_list(lsec, masid, end,
234 loff + (end - lbegin),
235 lend - end, current->isid);
236 if (!new) {
237 errcode = -pte_nomem;
238 break;
239 }
240
241 new->next = next;
242 next = new;
243 }
244 }
245
246 if (errcode < 0) {
247 pt_section_list_free_tail(next);
248
249 /* Re-add removed sections to the tail of the section list. */
250 for (; *list; list = &((*list)->next))
251 ;
252
253 *list = removed;
254 return errcode;
255 }
256
257 pt_section_list_free_tail(removed);
258
259 *list = next;
260 return 0;
261 }
262
pt_image_remove(struct pt_image * image,struct pt_section * section,const struct pt_asid * asid,uint64_t vaddr)263 int pt_image_remove(struct pt_image *image, struct pt_section *section,
264 const struct pt_asid *asid, uint64_t vaddr)
265 {
266 struct pt_section_list **list;
267
268 if (!image || !section)
269 return -pte_internal;
270
271 for (list = &image->sections; *list; list = &((*list)->next)) {
272 struct pt_mapped_section *msec;
273 const struct pt_section *sec;
274 const struct pt_asid *masid;
275 struct pt_section_list *trash;
276 uint64_t begin;
277 int errcode;
278
279 trash = *list;
280 msec = &trash->section;
281 masid = pt_msec_asid(msec);
282
283 errcode = pt_asid_match(masid, asid);
284 if (errcode < 0)
285 return errcode;
286
287 if (!errcode)
288 continue;
289
290 begin = pt_msec_begin(msec);
291 sec = pt_msec_section(msec);
292 if (sec == section && begin == vaddr) {
293 *list = trash->next;
294 pt_section_list_free(trash);
295
296 return 0;
297 }
298 }
299
300 return -pte_bad_image;
301 }
302
pt_image_add_file(struct pt_image * image,const char * filename,uint64_t offset,uint64_t size,const struct pt_asid * uasid,uint64_t vaddr)303 int pt_image_add_file(struct pt_image *image, const char *filename,
304 uint64_t offset, uint64_t size,
305 const struct pt_asid *uasid, uint64_t vaddr)
306 {
307 struct pt_section *section;
308 struct pt_asid asid;
309 int errcode;
310
311 if (!image || !filename)
312 return -pte_invalid;
313
314 errcode = pt_asid_from_user(&asid, uasid);
315 if (errcode < 0)
316 return errcode;
317
318 section = pt_mk_section(filename, offset, size);
319 if (!section)
320 return -pte_invalid;
321
322 errcode = pt_image_add(image, section, &asid, vaddr, 0);
323 if (errcode < 0) {
324 (void) pt_section_put(section);
325 return errcode;
326 }
327
328 /* The image list got its own reference; let's drop ours. */
329 errcode = pt_section_put(section);
330 if (errcode < 0)
331 return errcode;
332
333 return 0;
334 }
335
pt_image_copy(struct pt_image * image,const struct pt_image * src)336 int pt_image_copy(struct pt_image *image, const struct pt_image *src)
337 {
338 struct pt_section_list *list;
339 int ignored;
340
341 if (!image || !src)
342 return -pte_invalid;
343
344 /* There is nothing to do if we copy an image to itself.
345 *
346 * Besides, pt_image_add() may move sections around, which would
347 * interfere with our section iteration.
348 */
349 if (image == src)
350 return 0;
351
352 ignored = 0;
353 for (list = src->sections; list; list = list->next) {
354 int errcode;
355
356 errcode = pt_image_add(image, list->section.section,
357 &list->section.asid,
358 list->section.vaddr,
359 list->isid);
360 if (errcode < 0)
361 ignored += 1;
362 }
363
364 return ignored;
365 }
366
pt_image_remove_by_filename(struct pt_image * image,const char * filename,const struct pt_asid * uasid)367 int pt_image_remove_by_filename(struct pt_image *image, const char *filename,
368 const struct pt_asid *uasid)
369 {
370 struct pt_section_list **list;
371 struct pt_asid asid;
372 int errcode, removed;
373
374 if (!image || !filename)
375 return -pte_invalid;
376
377 errcode = pt_asid_from_user(&asid, uasid);
378 if (errcode < 0)
379 return errcode;
380
381 removed = 0;
382 for (list = &image->sections; *list;) {
383 struct pt_mapped_section *msec;
384 const struct pt_section *sec;
385 const struct pt_asid *masid;
386 struct pt_section_list *trash;
387 const char *tname;
388
389 trash = *list;
390 msec = &trash->section;
391 masid = pt_msec_asid(msec);
392
393 errcode = pt_asid_match(masid, &asid);
394 if (errcode < 0)
395 return errcode;
396
397 if (!errcode) {
398 list = &trash->next;
399 continue;
400 }
401
402 sec = pt_msec_section(msec);
403 tname = pt_section_filename(sec);
404
405 if (tname && (strcmp(tname, filename) == 0)) {
406 *list = trash->next;
407 pt_section_list_free(trash);
408
409 removed += 1;
410 } else
411 list = &trash->next;
412 }
413
414 return removed;
415 }
416
pt_image_remove_by_asid(struct pt_image * image,const struct pt_asid * uasid)417 int pt_image_remove_by_asid(struct pt_image *image,
418 const struct pt_asid *uasid)
419 {
420 struct pt_section_list **list;
421 struct pt_asid asid;
422 int errcode, removed;
423
424 if (!image)
425 return -pte_invalid;
426
427 errcode = pt_asid_from_user(&asid, uasid);
428 if (errcode < 0)
429 return errcode;
430
431 removed = 0;
432 for (list = &image->sections; *list;) {
433 struct pt_mapped_section *msec;
434 const struct pt_asid *masid;
435 struct pt_section_list *trash;
436
437 trash = *list;
438 msec = &trash->section;
439 masid = pt_msec_asid(msec);
440
441 errcode = pt_asid_match(masid, &asid);
442 if (errcode < 0)
443 return errcode;
444
445 if (!errcode) {
446 list = &trash->next;
447 continue;
448 }
449
450 *list = trash->next;
451 pt_section_list_free(trash);
452
453 removed += 1;
454 }
455
456 return removed;
457 }
458
pt_image_set_callback(struct pt_image * image,read_memory_callback_t * callback,void * context)459 int pt_image_set_callback(struct pt_image *image,
460 read_memory_callback_t *callback, void *context)
461 {
462 if (!image)
463 return -pte_invalid;
464
465 image->readmem.callback = callback;
466 image->readmem.context = context;
467
468 return 0;
469 }
470
pt_image_read_callback(struct pt_image * image,int * isid,uint8_t * buffer,uint16_t size,const struct pt_asid * asid,uint64_t addr)471 static int pt_image_read_callback(struct pt_image *image, int *isid,
472 uint8_t *buffer, uint16_t size,
473 const struct pt_asid *asid, uint64_t addr)
474 {
475 read_memory_callback_t *callback;
476
477 if (!image || !isid)
478 return -pte_internal;
479
480 callback = image->readmem.callback;
481 if (!callback)
482 return -pte_nomap;
483
484 *isid = 0;
485
486 return callback(buffer, size, asid, addr, image->readmem.context);
487 }
488
489 /* Check whether a mapped section contains an address.
490 *
491 * Returns zero if @msec contains @vaddr.
492 * Returns a negative error code otherwise.
493 * Returns -pte_nomap if @msec does not contain @vaddr.
494 */
pt_image_check_msec(const struct pt_mapped_section * msec,const struct pt_asid * asid,uint64_t vaddr)495 static inline int pt_image_check_msec(const struct pt_mapped_section *msec,
496 const struct pt_asid *asid,
497 uint64_t vaddr)
498 {
499 const struct pt_asid *masid;
500 uint64_t begin, end;
501 int errcode;
502
503 if (!msec)
504 return -pte_internal;
505
506 begin = pt_msec_begin(msec);
507 end = pt_msec_end(msec);
508 if (vaddr < begin || end <= vaddr)
509 return -pte_nomap;
510
511 masid = pt_msec_asid(msec);
512 errcode = pt_asid_match(masid, asid);
513 if (errcode <= 0) {
514 if (!errcode)
515 errcode = -pte_nomap;
516
517 return errcode;
518 }
519
520 return 0;
521 }
522
523 /* Find the section containing a given address in a given address space.
524 *
525 * On success, the found section is moved to the front of the section list.
526 * If caching is enabled, maps the section.
527 *
528 * Returns zero on success, a negative error code otherwise.
529 */
pt_image_fetch_section(struct pt_image * image,const struct pt_asid * asid,uint64_t vaddr)530 static int pt_image_fetch_section(struct pt_image *image,
531 const struct pt_asid *asid, uint64_t vaddr)
532 {
533 struct pt_section_list **start, **list;
534
535 if (!image)
536 return -pte_internal;
537
538 start = &image->sections;
539 for (list = start; *list;) {
540 struct pt_mapped_section *msec;
541 struct pt_section_list *elem;
542 int errcode;
543
544 elem = *list;
545 msec = &elem->section;
546
547 errcode = pt_image_check_msec(msec, asid, vaddr);
548 if (errcode < 0) {
549 if (errcode != -pte_nomap)
550 return errcode;
551
552 list = &elem->next;
553 continue;
554 }
555
556 /* Move the section to the front if it isn't already. */
557 if (list != start) {
558 *list = elem->next;
559 elem->next = *start;
560 *start = elem;
561 }
562
563 return 0;
564 }
565
566 return -pte_nomap;
567 }
568
pt_image_read(struct pt_image * image,int * isid,uint8_t * buffer,uint16_t size,const struct pt_asid * asid,uint64_t addr)569 int pt_image_read(struct pt_image *image, int *isid, uint8_t *buffer,
570 uint16_t size, const struct pt_asid *asid, uint64_t addr)
571 {
572 struct pt_mapped_section *msec;
573 struct pt_section_list *slist;
574 struct pt_section *section;
575 int errcode, status;
576
577 if (!image || !isid)
578 return -pte_internal;
579
580 errcode = pt_image_fetch_section(image, asid, addr);
581 if (errcode < 0) {
582 if (errcode != -pte_nomap)
583 return errcode;
584
585 return pt_image_read_callback(image, isid, buffer, size, asid,
586 addr);
587 }
588
589 slist = image->sections;
590 if (!slist)
591 return -pte_internal;
592
593 *isid = slist->isid;
594 msec = &slist->section;
595
596 section = pt_msec_section(msec);
597
598 errcode = pt_section_map(section);
599 if (errcode < 0)
600 return errcode;
601
602 status = pt_msec_read(msec, buffer, size, addr);
603
604 errcode = pt_section_unmap(section);
605 if (errcode < 0)
606 return errcode;
607
608 if (status < 0) {
609 if (status != -pte_nomap)
610 return status;
611
612 return pt_image_read_callback(image, isid, buffer, size, asid,
613 addr);
614 }
615
616 return status;
617 }
618
pt_image_add_cached(struct pt_image * image,struct pt_image_section_cache * iscache,int isid,const struct pt_asid * uasid)619 int pt_image_add_cached(struct pt_image *image,
620 struct pt_image_section_cache *iscache, int isid,
621 const struct pt_asid *uasid)
622 {
623 struct pt_section *section;
624 struct pt_asid asid;
625 uint64_t vaddr;
626 int errcode, status;
627
628 if (!image || !iscache)
629 return -pte_invalid;
630
631 errcode = pt_iscache_lookup(iscache, §ion, &vaddr, isid);
632 if (errcode < 0)
633 return errcode;
634
635 errcode = pt_asid_from_user(&asid, uasid);
636 if (errcode < 0)
637 return errcode;
638
639 status = pt_image_add(image, section, &asid, vaddr, isid);
640
641 /* We grab a reference when we add the section. Drop the one we
642 * obtained from cache lookup.
643 */
644 errcode = pt_section_put(section);
645 if (errcode < 0)
646 return errcode;
647
648 return status;
649 }
650
pt_image_find(struct pt_image * image,struct pt_mapped_section * usec,const struct pt_asid * asid,uint64_t vaddr)651 int pt_image_find(struct pt_image *image, struct pt_mapped_section *usec,
652 const struct pt_asid *asid, uint64_t vaddr)
653 {
654 struct pt_mapped_section *msec;
655 struct pt_section_list *slist;
656 struct pt_section *section;
657 int errcode;
658
659 if (!image || !usec)
660 return -pte_internal;
661
662 errcode = pt_image_fetch_section(image, asid, vaddr);
663 if (errcode < 0)
664 return errcode;
665
666 slist = image->sections;
667 if (!slist)
668 return -pte_internal;
669
670 msec = &slist->section;
671 section = pt_msec_section(msec);
672
673 errcode = pt_section_get(section);
674 if (errcode < 0)
675 return errcode;
676
677 *usec = *msec;
678
679 return slist->isid;
680 }
681
pt_image_validate(const struct pt_image * image,const struct pt_mapped_section * usec,uint64_t vaddr,int isid)682 int pt_image_validate(const struct pt_image *image,
683 const struct pt_mapped_section *usec, uint64_t vaddr,
684 int isid)
685 {
686 const struct pt_section_list *slist;
687 uint64_t begin, end;
688 int status;
689
690 if (!image || !usec)
691 return -pte_internal;
692
693 /* Check that @vaddr lies within @usec. */
694 begin = pt_msec_begin(usec);
695 end = pt_msec_end(usec);
696 if (vaddr < begin || end <= vaddr)
697 return -pte_nomap;
698
699 /* We assume that @usec is a copy of the top of our stack and accept
700 * sporadic validation fails if it isn't, e.g. because it has moved
701 * down.
702 *
703 * A failed validation requires decoders to re-fetch the section so it
704 * only results in a (relatively small) performance loss.
705 */
706 slist = image->sections;
707 if (!slist)
708 return -pte_nomap;
709
710 if (slist->isid != isid)
711 return -pte_nomap;
712
713 status = memcmp(&slist->section, usec, sizeof(*usec));
714 if (status)
715 return -pte_nomap;
716
717 return 0;
718 }
719