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