174fe6c29SRuslan Bukin /*
285f87cf4SRuslan Bukin  * Copyright (c) 2013-2019, Intel Corporation
374fe6c29SRuslan Bukin  *
474fe6c29SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
574fe6c29SRuslan Bukin  * modification, are permitted provided that the following conditions are met:
674fe6c29SRuslan Bukin  *
774fe6c29SRuslan Bukin  *  * Redistributions of source code must retain the above copyright notice,
874fe6c29SRuslan Bukin  *    this list of conditions and the following disclaimer.
974fe6c29SRuslan Bukin  *  * Redistributions in binary form must reproduce the above copyright notice,
1074fe6c29SRuslan Bukin  *    this list of conditions and the following disclaimer in the documentation
1174fe6c29SRuslan Bukin  *    and/or other materials provided with the distribution.
1274fe6c29SRuslan Bukin  *  * Neither the name of Intel Corporation nor the names of its contributors
1374fe6c29SRuslan Bukin  *    may be used to endorse or promote products derived from this software
1474fe6c29SRuslan Bukin  *    without specific prior written permission.
1574fe6c29SRuslan Bukin  *
1674fe6c29SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1774fe6c29SRuslan Bukin  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1874fe6c29SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1974fe6c29SRuslan Bukin  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2074fe6c29SRuslan Bukin  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2174fe6c29SRuslan Bukin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2274fe6c29SRuslan Bukin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2374fe6c29SRuslan Bukin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2474fe6c29SRuslan Bukin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2574fe6c29SRuslan Bukin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2674fe6c29SRuslan Bukin  * POSSIBILITY OF SUCH DAMAGE.
2774fe6c29SRuslan Bukin  */
2874fe6c29SRuslan Bukin 
2974fe6c29SRuslan Bukin #include "pt_image.h"
3074fe6c29SRuslan Bukin #include "pt_section.h"
3174fe6c29SRuslan Bukin #include "pt_asid.h"
3274fe6c29SRuslan Bukin #include "pt_image_section_cache.h"
3374fe6c29SRuslan Bukin 
3474fe6c29SRuslan Bukin #include <stdlib.h>
3574fe6c29SRuslan Bukin #include <string.h>
3674fe6c29SRuslan Bukin 
3774fe6c29SRuslan Bukin 
dupstr(const char * str)3874fe6c29SRuslan Bukin static char *dupstr(const char *str)
3974fe6c29SRuslan Bukin {
4074fe6c29SRuslan Bukin 	char *dup;
4174fe6c29SRuslan Bukin 	size_t len;
4274fe6c29SRuslan Bukin 
4374fe6c29SRuslan Bukin 	if (!str)
4474fe6c29SRuslan Bukin 		return NULL;
4574fe6c29SRuslan Bukin 
4685f87cf4SRuslan Bukin 	/* Silently truncate the name if it gets too big. */
4785f87cf4SRuslan Bukin 	len = strnlen(str, 4096ul);
4885f87cf4SRuslan Bukin 
4974fe6c29SRuslan Bukin 	dup = malloc(len + 1);
5074fe6c29SRuslan Bukin 	if (!dup)
5174fe6c29SRuslan Bukin 		return NULL;
5274fe6c29SRuslan Bukin 
5385f87cf4SRuslan Bukin 	dup[len] = 0;
5485f87cf4SRuslan Bukin 
5585f87cf4SRuslan Bukin 	return memcpy(dup, str, len);
5674fe6c29SRuslan Bukin }
5774fe6c29SRuslan Bukin 
pt_mk_section_list(struct pt_section * section,const struct pt_asid * asid,uint64_t vaddr,uint64_t offset,uint64_t size,int isid)5874fe6c29SRuslan Bukin static struct pt_section_list *pt_mk_section_list(struct pt_section *section,
5974fe6c29SRuslan Bukin 						  const struct pt_asid *asid,
6074fe6c29SRuslan Bukin 						  uint64_t vaddr,
6174fe6c29SRuslan Bukin 						  uint64_t offset,
6274fe6c29SRuslan Bukin 						  uint64_t size, int isid)
6374fe6c29SRuslan Bukin {
6474fe6c29SRuslan Bukin 	struct pt_section_list *list;
6574fe6c29SRuslan Bukin 	int errcode;
6674fe6c29SRuslan Bukin 
6774fe6c29SRuslan Bukin 	list = malloc(sizeof(*list));
6874fe6c29SRuslan Bukin 	if (!list)
6974fe6c29SRuslan Bukin 		return NULL;
7074fe6c29SRuslan Bukin 
7174fe6c29SRuslan Bukin 	memset(list, 0, sizeof(*list));
7274fe6c29SRuslan Bukin 
7374fe6c29SRuslan Bukin 	errcode = pt_section_get(section);
7474fe6c29SRuslan Bukin 	if (errcode < 0)
7574fe6c29SRuslan Bukin 		goto out_mem;
7674fe6c29SRuslan Bukin 
7774fe6c29SRuslan Bukin 	pt_msec_init(&list->section, section, asid, vaddr, offset, size);
7874fe6c29SRuslan Bukin 	list->isid = isid;
7974fe6c29SRuslan Bukin 
8074fe6c29SRuslan Bukin 	return list;
8174fe6c29SRuslan Bukin 
8274fe6c29SRuslan Bukin out_mem:
8374fe6c29SRuslan Bukin 	free(list);
8474fe6c29SRuslan Bukin 	return NULL;
8574fe6c29SRuslan Bukin }
8674fe6c29SRuslan Bukin 
pt_section_list_free(struct pt_section_list * list)8774fe6c29SRuslan Bukin static void pt_section_list_free(struct pt_section_list *list)
8874fe6c29SRuslan Bukin {
8974fe6c29SRuslan Bukin 	if (!list)
9074fe6c29SRuslan Bukin 		return;
9174fe6c29SRuslan Bukin 
9274fe6c29SRuslan Bukin 	pt_section_put(list->section.section);
9374fe6c29SRuslan Bukin 	pt_msec_fini(&list->section);
9474fe6c29SRuslan Bukin 	free(list);
9574fe6c29SRuslan Bukin }
9674fe6c29SRuslan Bukin 
pt_section_list_free_tail(struct pt_section_list * list)9774fe6c29SRuslan Bukin static void pt_section_list_free_tail(struct pt_section_list *list)
9874fe6c29SRuslan Bukin {
9974fe6c29SRuslan Bukin 	while (list) {
10074fe6c29SRuslan Bukin 		struct pt_section_list *trash;
10174fe6c29SRuslan Bukin 
10274fe6c29SRuslan Bukin 		trash = list;
10374fe6c29SRuslan Bukin 		list = list->next;
10474fe6c29SRuslan Bukin 
10574fe6c29SRuslan Bukin 		pt_section_list_free(trash);
10674fe6c29SRuslan Bukin 	}
10774fe6c29SRuslan Bukin }
10874fe6c29SRuslan Bukin 
pt_image_init(struct pt_image * image,const char * name)10974fe6c29SRuslan Bukin void pt_image_init(struct pt_image *image, const char *name)
11074fe6c29SRuslan Bukin {
11174fe6c29SRuslan Bukin 	if (!image)
11274fe6c29SRuslan Bukin 		return;
11374fe6c29SRuslan Bukin 
11474fe6c29SRuslan Bukin 	memset(image, 0, sizeof(*image));
11574fe6c29SRuslan Bukin 
11674fe6c29SRuslan Bukin 	image->name = dupstr(name);
11774fe6c29SRuslan Bukin }
11874fe6c29SRuslan Bukin 
pt_image_fini(struct pt_image * image)11974fe6c29SRuslan Bukin void pt_image_fini(struct pt_image *image)
12074fe6c29SRuslan Bukin {
12174fe6c29SRuslan Bukin 	if (!image)
12274fe6c29SRuslan Bukin 		return;
12374fe6c29SRuslan Bukin 
12474fe6c29SRuslan Bukin 	pt_section_list_free_tail(image->sections);
12574fe6c29SRuslan Bukin 	free(image->name);
12674fe6c29SRuslan Bukin 
12774fe6c29SRuslan Bukin 	memset(image, 0, sizeof(*image));
12874fe6c29SRuslan Bukin }
12974fe6c29SRuslan Bukin 
pt_image_alloc(const char * name)13074fe6c29SRuslan Bukin struct pt_image *pt_image_alloc(const char *name)
13174fe6c29SRuslan Bukin {
13274fe6c29SRuslan Bukin 	struct pt_image *image;
13374fe6c29SRuslan Bukin 
13474fe6c29SRuslan Bukin 	image = malloc(sizeof(*image));
13574fe6c29SRuslan Bukin 	if (image)
13674fe6c29SRuslan Bukin 		pt_image_init(image, name);
13774fe6c29SRuslan Bukin 
13874fe6c29SRuslan Bukin 	return image;
13974fe6c29SRuslan Bukin }
14074fe6c29SRuslan Bukin 
pt_image_free(struct pt_image * image)14174fe6c29SRuslan Bukin void pt_image_free(struct pt_image *image)
14274fe6c29SRuslan Bukin {
14374fe6c29SRuslan Bukin 	pt_image_fini(image);
14474fe6c29SRuslan Bukin 	free(image);
14574fe6c29SRuslan Bukin }
14674fe6c29SRuslan Bukin 
pt_image_name(const struct pt_image * image)14774fe6c29SRuslan Bukin const char *pt_image_name(const struct pt_image *image)
14874fe6c29SRuslan Bukin {
14974fe6c29SRuslan Bukin 	if (!image)
15074fe6c29SRuslan Bukin 		return NULL;
15174fe6c29SRuslan Bukin 
15274fe6c29SRuslan Bukin 	return image->name;
15374fe6c29SRuslan Bukin }
15474fe6c29SRuslan Bukin 
pt_image_add(struct pt_image * image,struct pt_section * section,const struct pt_asid * asid,uint64_t vaddr,int isid)15574fe6c29SRuslan Bukin int pt_image_add(struct pt_image *image, struct pt_section *section,
15674fe6c29SRuslan Bukin 		 const struct pt_asid *asid, uint64_t vaddr, int isid)
15774fe6c29SRuslan Bukin {
15874fe6c29SRuslan Bukin 	struct pt_section_list **list, *next, *removed, *new;
15974fe6c29SRuslan Bukin 	uint64_t size, begin, end;
16074fe6c29SRuslan Bukin 	int errcode;
16174fe6c29SRuslan Bukin 
16274fe6c29SRuslan Bukin 	if (!image || !section)
16374fe6c29SRuslan Bukin 		return -pte_internal;
16474fe6c29SRuslan Bukin 
16574fe6c29SRuslan Bukin 	size = pt_section_size(section);
16674fe6c29SRuslan Bukin 	begin = vaddr;
16774fe6c29SRuslan Bukin 	end = begin + size;
16874fe6c29SRuslan Bukin 
16974fe6c29SRuslan Bukin 	next = pt_mk_section_list(section, asid, begin, 0ull, size, isid);
17074fe6c29SRuslan Bukin 	if (!next)
17174fe6c29SRuslan Bukin 		return -pte_nomem;
17274fe6c29SRuslan Bukin 
17374fe6c29SRuslan Bukin 	removed = NULL;
17474fe6c29SRuslan Bukin 	errcode = 0;
17574fe6c29SRuslan Bukin 
17674fe6c29SRuslan Bukin 	/* Check for overlaps while we move to the end of the list. */
17774fe6c29SRuslan Bukin 	list = &(image->sections);
17874fe6c29SRuslan Bukin 	while (*list) {
17974fe6c29SRuslan Bukin 		const struct pt_mapped_section *msec;
18074fe6c29SRuslan Bukin 		const struct pt_asid *masid;
18174fe6c29SRuslan Bukin 		struct pt_section_list *current;
18274fe6c29SRuslan Bukin 		struct pt_section *lsec;
18374fe6c29SRuslan Bukin 		uint64_t lbegin, lend, loff;
18474fe6c29SRuslan Bukin 
18574fe6c29SRuslan Bukin 		current = *list;
18674fe6c29SRuslan Bukin 		msec = &current->section;
18774fe6c29SRuslan Bukin 		masid = pt_msec_asid(msec);
18874fe6c29SRuslan Bukin 
18974fe6c29SRuslan Bukin 		errcode = pt_asid_match(masid, asid);
19074fe6c29SRuslan Bukin 		if (errcode < 0)
19174fe6c29SRuslan Bukin 			break;
19274fe6c29SRuslan Bukin 
19374fe6c29SRuslan Bukin 		if (!errcode) {
19474fe6c29SRuslan Bukin 			list = &((*list)->next);
19574fe6c29SRuslan Bukin 			continue;
19674fe6c29SRuslan Bukin 		}
19774fe6c29SRuslan Bukin 
19874fe6c29SRuslan Bukin 		lbegin = pt_msec_begin(msec);
19974fe6c29SRuslan Bukin 		lend = pt_msec_end(msec);
20074fe6c29SRuslan Bukin 
20174fe6c29SRuslan Bukin 		if ((end <= lbegin) || (lend <= begin)) {
20274fe6c29SRuslan Bukin 			list = &((*list)->next);
20374fe6c29SRuslan Bukin 			continue;
20474fe6c29SRuslan Bukin 		}
20574fe6c29SRuslan Bukin 
20674fe6c29SRuslan Bukin 		/* The new section overlaps with @msec's section. */
20774fe6c29SRuslan Bukin 		lsec = pt_msec_section(msec);
20874fe6c29SRuslan Bukin 		loff = pt_msec_offset(msec);
20974fe6c29SRuslan Bukin 
21074fe6c29SRuslan Bukin 		/* We remove @msec and insert new sections for the remaining
21174fe6c29SRuslan Bukin 		 * parts, if any.  Those new sections are not mapped initially
21274fe6c29SRuslan Bukin 		 * and need to be added to the end of the section list.
21374fe6c29SRuslan Bukin 		 */
21474fe6c29SRuslan Bukin 		*list = current->next;
21574fe6c29SRuslan Bukin 
21674fe6c29SRuslan Bukin 		/* Keep a list of removed sections so we can re-add them in case
21774fe6c29SRuslan Bukin 		 * of errors.
21874fe6c29SRuslan Bukin 		 */
21974fe6c29SRuslan Bukin 		current->next = removed;
22074fe6c29SRuslan Bukin 		removed = current;
22174fe6c29SRuslan Bukin 
22274fe6c29SRuslan Bukin 		/* Add a section covering the remaining bytes at the front. */
22374fe6c29SRuslan Bukin 		if (lbegin < begin) {
22474fe6c29SRuslan Bukin 			new = pt_mk_section_list(lsec, masid, lbegin, loff,
22574fe6c29SRuslan Bukin 						 begin - lbegin, current->isid);
22674fe6c29SRuslan Bukin 			if (!new) {
22774fe6c29SRuslan Bukin 				errcode = -pte_nomem;
22874fe6c29SRuslan Bukin 				break;
22974fe6c29SRuslan Bukin 			}
23074fe6c29SRuslan Bukin 
23174fe6c29SRuslan Bukin 			new->next = next;
23274fe6c29SRuslan Bukin 			next = new;
23374fe6c29SRuslan Bukin 		}
23474fe6c29SRuslan Bukin 
23574fe6c29SRuslan Bukin 		/* Add a section covering the remaining bytes at the back. */
23674fe6c29SRuslan Bukin 		if (end < lend) {
23774fe6c29SRuslan Bukin 			new = pt_mk_section_list(lsec, masid, end,
23874fe6c29SRuslan Bukin 						 loff + (end - lbegin),
23974fe6c29SRuslan Bukin 						 lend - end, current->isid);
24074fe6c29SRuslan Bukin 			if (!new) {
24174fe6c29SRuslan Bukin 				errcode = -pte_nomem;
24274fe6c29SRuslan Bukin 				break;
24374fe6c29SRuslan Bukin 			}
24474fe6c29SRuslan Bukin 
24574fe6c29SRuslan Bukin 			new->next = next;
24674fe6c29SRuslan Bukin 			next = new;
24774fe6c29SRuslan Bukin 		}
24874fe6c29SRuslan Bukin 	}
24974fe6c29SRuslan Bukin 
25074fe6c29SRuslan Bukin 	if (errcode < 0) {
25174fe6c29SRuslan Bukin 		pt_section_list_free_tail(next);
25274fe6c29SRuslan Bukin 
25374fe6c29SRuslan Bukin 		/* Re-add removed sections to the tail of the section list. */
25474fe6c29SRuslan Bukin 		for (; *list; list = &((*list)->next))
25574fe6c29SRuslan Bukin 			;
25674fe6c29SRuslan Bukin 
25774fe6c29SRuslan Bukin 		*list = removed;
25874fe6c29SRuslan Bukin 		return errcode;
25974fe6c29SRuslan Bukin 	}
26074fe6c29SRuslan Bukin 
26174fe6c29SRuslan Bukin 	pt_section_list_free_tail(removed);
26274fe6c29SRuslan Bukin 
26374fe6c29SRuslan Bukin 	*list = next;
26474fe6c29SRuslan Bukin 	return 0;
26574fe6c29SRuslan Bukin }
26674fe6c29SRuslan Bukin 
pt_image_remove(struct pt_image * image,struct pt_section * section,const struct pt_asid * asid,uint64_t vaddr)26774fe6c29SRuslan Bukin int pt_image_remove(struct pt_image *image, struct pt_section *section,
26874fe6c29SRuslan Bukin 		    const struct pt_asid *asid, uint64_t vaddr)
26974fe6c29SRuslan Bukin {
27074fe6c29SRuslan Bukin 	struct pt_section_list **list;
27174fe6c29SRuslan Bukin 
27274fe6c29SRuslan Bukin 	if (!image || !section)
27374fe6c29SRuslan Bukin 		return -pte_internal;
27474fe6c29SRuslan Bukin 
27574fe6c29SRuslan Bukin 	for (list = &image->sections; *list; list = &((*list)->next)) {
27674fe6c29SRuslan Bukin 		struct pt_mapped_section *msec;
27774fe6c29SRuslan Bukin 		const struct pt_section *sec;
27874fe6c29SRuslan Bukin 		const struct pt_asid *masid;
27974fe6c29SRuslan Bukin 		struct pt_section_list *trash;
28074fe6c29SRuslan Bukin 		uint64_t begin;
28174fe6c29SRuslan Bukin 		int errcode;
28274fe6c29SRuslan Bukin 
28374fe6c29SRuslan Bukin 		trash = *list;
28474fe6c29SRuslan Bukin 		msec = &trash->section;
28574fe6c29SRuslan Bukin 		masid = pt_msec_asid(msec);
28674fe6c29SRuslan Bukin 
28774fe6c29SRuslan Bukin 		errcode = pt_asid_match(masid, asid);
28874fe6c29SRuslan Bukin 		if (errcode < 0)
28974fe6c29SRuslan Bukin 			return errcode;
29074fe6c29SRuslan Bukin 
29174fe6c29SRuslan Bukin 		if (!errcode)
29274fe6c29SRuslan Bukin 			continue;
29374fe6c29SRuslan Bukin 
29474fe6c29SRuslan Bukin 		begin = pt_msec_begin(msec);
29574fe6c29SRuslan Bukin 		sec = pt_msec_section(msec);
29674fe6c29SRuslan Bukin 		if (sec == section && begin == vaddr) {
29774fe6c29SRuslan Bukin 			*list = trash->next;
29874fe6c29SRuslan Bukin 			pt_section_list_free(trash);
29974fe6c29SRuslan Bukin 
30074fe6c29SRuslan Bukin 			return 0;
30174fe6c29SRuslan Bukin 		}
30274fe6c29SRuslan Bukin 	}
30374fe6c29SRuslan Bukin 
30474fe6c29SRuslan Bukin 	return -pte_bad_image;
30574fe6c29SRuslan Bukin }
30674fe6c29SRuslan Bukin 
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)30774fe6c29SRuslan Bukin int pt_image_add_file(struct pt_image *image, const char *filename,
30874fe6c29SRuslan Bukin 		      uint64_t offset, uint64_t size,
30974fe6c29SRuslan Bukin 		      const struct pt_asid *uasid, uint64_t vaddr)
31074fe6c29SRuslan Bukin {
31174fe6c29SRuslan Bukin 	struct pt_section *section;
31274fe6c29SRuslan Bukin 	struct pt_asid asid;
31374fe6c29SRuslan Bukin 	int errcode;
31474fe6c29SRuslan Bukin 
31574fe6c29SRuslan Bukin 	if (!image || !filename)
31674fe6c29SRuslan Bukin 		return -pte_invalid;
31774fe6c29SRuslan Bukin 
31874fe6c29SRuslan Bukin 	errcode = pt_asid_from_user(&asid, uasid);
31974fe6c29SRuslan Bukin 	if (errcode < 0)
32074fe6c29SRuslan Bukin 		return errcode;
32174fe6c29SRuslan Bukin 
32285f87cf4SRuslan Bukin 	section = NULL;
32385f87cf4SRuslan Bukin 	errcode = pt_mk_section(&section, filename, offset, size);
32485f87cf4SRuslan Bukin 	if (errcode < 0)
32585f87cf4SRuslan Bukin 		return errcode;
32674fe6c29SRuslan Bukin 
32774fe6c29SRuslan Bukin 	errcode = pt_image_add(image, section, &asid, vaddr, 0);
32874fe6c29SRuslan Bukin 	if (errcode < 0) {
32974fe6c29SRuslan Bukin 		(void) pt_section_put(section);
33074fe6c29SRuslan Bukin 		return errcode;
33174fe6c29SRuslan Bukin 	}
33274fe6c29SRuslan Bukin 
33374fe6c29SRuslan Bukin 	/* The image list got its own reference; let's drop ours. */
33474fe6c29SRuslan Bukin 	errcode = pt_section_put(section);
33574fe6c29SRuslan Bukin 	if (errcode < 0)
33674fe6c29SRuslan Bukin 		return errcode;
33774fe6c29SRuslan Bukin 
33874fe6c29SRuslan Bukin 	return 0;
33974fe6c29SRuslan Bukin }
34074fe6c29SRuslan Bukin 
pt_image_copy(struct pt_image * image,const struct pt_image * src)34174fe6c29SRuslan Bukin int pt_image_copy(struct pt_image *image, const struct pt_image *src)
34274fe6c29SRuslan Bukin {
34374fe6c29SRuslan Bukin 	struct pt_section_list *list;
34474fe6c29SRuslan Bukin 	int ignored;
34574fe6c29SRuslan Bukin 
34674fe6c29SRuslan Bukin 	if (!image || !src)
34774fe6c29SRuslan Bukin 		return -pte_invalid;
34874fe6c29SRuslan Bukin 
34974fe6c29SRuslan Bukin 	/* There is nothing to do if we copy an image to itself.
35074fe6c29SRuslan Bukin 	 *
35174fe6c29SRuslan Bukin 	 * Besides, pt_image_add() may move sections around, which would
35274fe6c29SRuslan Bukin 	 * interfere with our section iteration.
35374fe6c29SRuslan Bukin 	 */
35474fe6c29SRuslan Bukin 	if (image == src)
35574fe6c29SRuslan Bukin 		return 0;
35674fe6c29SRuslan Bukin 
35774fe6c29SRuslan Bukin 	ignored = 0;
35874fe6c29SRuslan Bukin 	for (list = src->sections; list; list = list->next) {
35974fe6c29SRuslan Bukin 		int errcode;
36074fe6c29SRuslan Bukin 
36174fe6c29SRuslan Bukin 		errcode = pt_image_add(image, list->section.section,
36274fe6c29SRuslan Bukin 				       &list->section.asid,
36374fe6c29SRuslan Bukin 				       list->section.vaddr,
36474fe6c29SRuslan Bukin 				       list->isid);
36574fe6c29SRuslan Bukin 		if (errcode < 0)
36674fe6c29SRuslan Bukin 			ignored += 1;
36774fe6c29SRuslan Bukin 	}
36874fe6c29SRuslan Bukin 
36974fe6c29SRuslan Bukin 	return ignored;
37074fe6c29SRuslan Bukin }
37174fe6c29SRuslan Bukin 
pt_image_remove_by_filename(struct pt_image * image,const char * filename,const struct pt_asid * uasid)37274fe6c29SRuslan Bukin int pt_image_remove_by_filename(struct pt_image *image, const char *filename,
37374fe6c29SRuslan Bukin 				const struct pt_asid *uasid)
37474fe6c29SRuslan Bukin {
37574fe6c29SRuslan Bukin 	struct pt_section_list **list;
37674fe6c29SRuslan Bukin 	struct pt_asid asid;
37774fe6c29SRuslan Bukin 	int errcode, removed;
37874fe6c29SRuslan Bukin 
37974fe6c29SRuslan Bukin 	if (!image || !filename)
38074fe6c29SRuslan Bukin 		return -pte_invalid;
38174fe6c29SRuslan Bukin 
38274fe6c29SRuslan Bukin 	errcode = pt_asid_from_user(&asid, uasid);
38374fe6c29SRuslan Bukin 	if (errcode < 0)
38474fe6c29SRuslan Bukin 		return errcode;
38574fe6c29SRuslan Bukin 
38674fe6c29SRuslan Bukin 	removed = 0;
38774fe6c29SRuslan Bukin 	for (list = &image->sections; *list;) {
38874fe6c29SRuslan Bukin 		struct pt_mapped_section *msec;
38974fe6c29SRuslan Bukin 		const struct pt_section *sec;
39074fe6c29SRuslan Bukin 		const struct pt_asid *masid;
39174fe6c29SRuslan Bukin 		struct pt_section_list *trash;
39274fe6c29SRuslan Bukin 		const char *tname;
39374fe6c29SRuslan Bukin 
39474fe6c29SRuslan Bukin 		trash = *list;
39574fe6c29SRuslan Bukin 		msec = &trash->section;
39674fe6c29SRuslan Bukin 		masid = pt_msec_asid(msec);
39774fe6c29SRuslan Bukin 
39874fe6c29SRuslan Bukin 		errcode = pt_asid_match(masid, &asid);
39974fe6c29SRuslan Bukin 		if (errcode < 0)
40074fe6c29SRuslan Bukin 			return errcode;
40174fe6c29SRuslan Bukin 
40274fe6c29SRuslan Bukin 		if (!errcode) {
40374fe6c29SRuslan Bukin 			list = &trash->next;
40474fe6c29SRuslan Bukin 			continue;
40574fe6c29SRuslan Bukin 		}
40674fe6c29SRuslan Bukin 
40774fe6c29SRuslan Bukin 		sec = pt_msec_section(msec);
40874fe6c29SRuslan Bukin 		tname = pt_section_filename(sec);
40974fe6c29SRuslan Bukin 
41074fe6c29SRuslan Bukin 		if (tname && (strcmp(tname, filename) == 0)) {
41174fe6c29SRuslan Bukin 			*list = trash->next;
41274fe6c29SRuslan Bukin 			pt_section_list_free(trash);
41374fe6c29SRuslan Bukin 
41474fe6c29SRuslan Bukin 			removed += 1;
41574fe6c29SRuslan Bukin 		} else
41674fe6c29SRuslan Bukin 			list = &trash->next;
41774fe6c29SRuslan Bukin 	}
41874fe6c29SRuslan Bukin 
41974fe6c29SRuslan Bukin 	return removed;
42074fe6c29SRuslan Bukin }
42174fe6c29SRuslan Bukin 
pt_image_remove_by_asid(struct pt_image * image,const struct pt_asid * uasid)42274fe6c29SRuslan Bukin int pt_image_remove_by_asid(struct pt_image *image,
42374fe6c29SRuslan Bukin 			    const struct pt_asid *uasid)
42474fe6c29SRuslan Bukin {
42574fe6c29SRuslan Bukin 	struct pt_section_list **list;
42674fe6c29SRuslan Bukin 	struct pt_asid asid;
42774fe6c29SRuslan Bukin 	int errcode, removed;
42874fe6c29SRuslan Bukin 
42974fe6c29SRuslan Bukin 	if (!image)
43074fe6c29SRuslan Bukin 		return -pte_invalid;
43174fe6c29SRuslan Bukin 
43274fe6c29SRuslan Bukin 	errcode = pt_asid_from_user(&asid, uasid);
43374fe6c29SRuslan Bukin 	if (errcode < 0)
43474fe6c29SRuslan Bukin 		return errcode;
43574fe6c29SRuslan Bukin 
43674fe6c29SRuslan Bukin 	removed = 0;
43774fe6c29SRuslan Bukin 	for (list = &image->sections; *list;) {
43874fe6c29SRuslan Bukin 		struct pt_mapped_section *msec;
43974fe6c29SRuslan Bukin 		const struct pt_asid *masid;
44074fe6c29SRuslan Bukin 		struct pt_section_list *trash;
44174fe6c29SRuslan Bukin 
44274fe6c29SRuslan Bukin 		trash = *list;
44374fe6c29SRuslan Bukin 		msec = &trash->section;
44474fe6c29SRuslan Bukin 		masid = pt_msec_asid(msec);
44574fe6c29SRuslan Bukin 
44674fe6c29SRuslan Bukin 		errcode = pt_asid_match(masid, &asid);
44774fe6c29SRuslan Bukin 		if (errcode < 0)
44874fe6c29SRuslan Bukin 			return errcode;
44974fe6c29SRuslan Bukin 
45074fe6c29SRuslan Bukin 		if (!errcode) {
45174fe6c29SRuslan Bukin 			list = &trash->next;
45274fe6c29SRuslan Bukin 			continue;
45374fe6c29SRuslan Bukin 		}
45474fe6c29SRuslan Bukin 
45574fe6c29SRuslan Bukin 		*list = trash->next;
45674fe6c29SRuslan Bukin 		pt_section_list_free(trash);
45774fe6c29SRuslan Bukin 
45874fe6c29SRuslan Bukin 		removed += 1;
45974fe6c29SRuslan Bukin 	}
46074fe6c29SRuslan Bukin 
46174fe6c29SRuslan Bukin 	return removed;
46274fe6c29SRuslan Bukin }
46374fe6c29SRuslan Bukin 
pt_image_set_callback(struct pt_image * image,read_memory_callback_t * callback,void * context)46474fe6c29SRuslan Bukin int pt_image_set_callback(struct pt_image *image,
46574fe6c29SRuslan Bukin 			  read_memory_callback_t *callback, void *context)
46674fe6c29SRuslan Bukin {
46774fe6c29SRuslan Bukin 	if (!image)
46874fe6c29SRuslan Bukin 		return -pte_invalid;
46974fe6c29SRuslan Bukin 
47074fe6c29SRuslan Bukin 	image->readmem.callback = callback;
47174fe6c29SRuslan Bukin 	image->readmem.context = context;
47274fe6c29SRuslan Bukin 
47374fe6c29SRuslan Bukin 	return 0;
47474fe6c29SRuslan Bukin }
47574fe6c29SRuslan Bukin 
pt_image_read_callback(struct pt_image * image,int * isid,uint8_t * buffer,uint16_t size,const struct pt_asid * asid,uint64_t addr)47674fe6c29SRuslan Bukin static int pt_image_read_callback(struct pt_image *image, int *isid,
47774fe6c29SRuslan Bukin 				  uint8_t *buffer, uint16_t size,
47874fe6c29SRuslan Bukin 				  const struct pt_asid *asid, uint64_t addr)
47974fe6c29SRuslan Bukin {
48074fe6c29SRuslan Bukin 	read_memory_callback_t *callback;
48174fe6c29SRuslan Bukin 
48274fe6c29SRuslan Bukin 	if (!image || !isid)
48374fe6c29SRuslan Bukin 		return -pte_internal;
48474fe6c29SRuslan Bukin 
48574fe6c29SRuslan Bukin 	callback = image->readmem.callback;
48674fe6c29SRuslan Bukin 	if (!callback)
48774fe6c29SRuslan Bukin 		return -pte_nomap;
48874fe6c29SRuslan Bukin 
48974fe6c29SRuslan Bukin 	*isid = 0;
49074fe6c29SRuslan Bukin 
49174fe6c29SRuslan Bukin 	return callback(buffer, size, asid, addr, image->readmem.context);
49274fe6c29SRuslan Bukin }
49374fe6c29SRuslan Bukin 
49474fe6c29SRuslan Bukin /* Check whether a mapped section contains an address.
49574fe6c29SRuslan Bukin  *
49674fe6c29SRuslan Bukin  * Returns zero if @msec contains @vaddr.
49774fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
49874fe6c29SRuslan Bukin  * Returns -pte_nomap if @msec does not contain @vaddr.
49974fe6c29SRuslan Bukin  */
pt_image_check_msec(const struct pt_mapped_section * msec,const struct pt_asid * asid,uint64_t vaddr)50074fe6c29SRuslan Bukin static inline int pt_image_check_msec(const struct pt_mapped_section *msec,
50174fe6c29SRuslan Bukin 				      const struct pt_asid *asid,
50274fe6c29SRuslan Bukin 				      uint64_t vaddr)
50374fe6c29SRuslan Bukin {
50474fe6c29SRuslan Bukin 	const struct pt_asid *masid;
50574fe6c29SRuslan Bukin 	uint64_t begin, end;
50674fe6c29SRuslan Bukin 	int errcode;
50774fe6c29SRuslan Bukin 
50874fe6c29SRuslan Bukin 	if (!msec)
50974fe6c29SRuslan Bukin 		return -pte_internal;
51074fe6c29SRuslan Bukin 
51174fe6c29SRuslan Bukin 	begin = pt_msec_begin(msec);
51274fe6c29SRuslan Bukin 	end = pt_msec_end(msec);
51374fe6c29SRuslan Bukin 	if (vaddr < begin || end <= vaddr)
51474fe6c29SRuslan Bukin 		return -pte_nomap;
51574fe6c29SRuslan Bukin 
51674fe6c29SRuslan Bukin 	masid = pt_msec_asid(msec);
51774fe6c29SRuslan Bukin 	errcode = pt_asid_match(masid, asid);
51874fe6c29SRuslan Bukin 	if (errcode <= 0) {
51974fe6c29SRuslan Bukin 		if (!errcode)
52074fe6c29SRuslan Bukin 			errcode = -pte_nomap;
52174fe6c29SRuslan Bukin 
52274fe6c29SRuslan Bukin 		return errcode;
52374fe6c29SRuslan Bukin 	}
52474fe6c29SRuslan Bukin 
52574fe6c29SRuslan Bukin 	return 0;
52674fe6c29SRuslan Bukin }
52774fe6c29SRuslan Bukin 
52874fe6c29SRuslan Bukin /* Find the section containing a given address in a given address space.
52974fe6c29SRuslan Bukin  *
53074fe6c29SRuslan Bukin  * On success, the found section is moved to the front of the section list.
53174fe6c29SRuslan Bukin  * If caching is enabled, maps the section.
53274fe6c29SRuslan Bukin  *
53374fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
53474fe6c29SRuslan Bukin  */
pt_image_fetch_section(struct pt_image * image,const struct pt_asid * asid,uint64_t vaddr)53574fe6c29SRuslan Bukin static int pt_image_fetch_section(struct pt_image *image,
53674fe6c29SRuslan Bukin 				  const struct pt_asid *asid, uint64_t vaddr)
53774fe6c29SRuslan Bukin {
53874fe6c29SRuslan Bukin 	struct pt_section_list **start, **list;
53974fe6c29SRuslan Bukin 
54074fe6c29SRuslan Bukin 	if (!image)
54174fe6c29SRuslan Bukin 		return -pte_internal;
54274fe6c29SRuslan Bukin 
54374fe6c29SRuslan Bukin 	start = &image->sections;
54474fe6c29SRuslan Bukin 	for (list = start; *list;) {
54574fe6c29SRuslan Bukin 		struct pt_mapped_section *msec;
54674fe6c29SRuslan Bukin 		struct pt_section_list *elem;
54774fe6c29SRuslan Bukin 		int errcode;
54874fe6c29SRuslan Bukin 
54974fe6c29SRuslan Bukin 		elem = *list;
55074fe6c29SRuslan Bukin 		msec = &elem->section;
55174fe6c29SRuslan Bukin 
55274fe6c29SRuslan Bukin 		errcode = pt_image_check_msec(msec, asid, vaddr);
55374fe6c29SRuslan Bukin 		if (errcode < 0) {
55474fe6c29SRuslan Bukin 			if (errcode != -pte_nomap)
55574fe6c29SRuslan Bukin 				return errcode;
55674fe6c29SRuslan Bukin 
55774fe6c29SRuslan Bukin 			list = &elem->next;
55874fe6c29SRuslan Bukin 			continue;
55974fe6c29SRuslan Bukin 		}
56074fe6c29SRuslan Bukin 
56174fe6c29SRuslan Bukin 		/* Move the section to the front if it isn't already. */
56274fe6c29SRuslan Bukin 		if (list != start) {
56374fe6c29SRuslan Bukin 			*list = elem->next;
56474fe6c29SRuslan Bukin 			elem->next = *start;
56574fe6c29SRuslan Bukin 			*start = elem;
56674fe6c29SRuslan Bukin 		}
56774fe6c29SRuslan Bukin 
56874fe6c29SRuslan Bukin 		return 0;
56974fe6c29SRuslan Bukin 	}
57074fe6c29SRuslan Bukin 
57174fe6c29SRuslan Bukin 	return -pte_nomap;
57274fe6c29SRuslan Bukin }
57374fe6c29SRuslan Bukin 
pt_image_read(struct pt_image * image,int * isid,uint8_t * buffer,uint16_t size,const struct pt_asid * asid,uint64_t addr)57474fe6c29SRuslan Bukin int pt_image_read(struct pt_image *image, int *isid, uint8_t *buffer,
57574fe6c29SRuslan Bukin 		  uint16_t size, const struct pt_asid *asid, uint64_t addr)
57674fe6c29SRuslan Bukin {
57774fe6c29SRuslan Bukin 	struct pt_mapped_section *msec;
57874fe6c29SRuslan Bukin 	struct pt_section_list *slist;
57974fe6c29SRuslan Bukin 	struct pt_section *section;
58074fe6c29SRuslan Bukin 	int errcode, status;
58174fe6c29SRuslan Bukin 
58274fe6c29SRuslan Bukin 	if (!image || !isid)
58374fe6c29SRuslan Bukin 		return -pte_internal;
58474fe6c29SRuslan Bukin 
58574fe6c29SRuslan Bukin 	errcode = pt_image_fetch_section(image, asid, addr);
58674fe6c29SRuslan Bukin 	if (errcode < 0) {
58774fe6c29SRuslan Bukin 		if (errcode != -pte_nomap)
58874fe6c29SRuslan Bukin 			return errcode;
58974fe6c29SRuslan Bukin 
59074fe6c29SRuslan Bukin 		return pt_image_read_callback(image, isid, buffer, size, asid,
59174fe6c29SRuslan Bukin 					      addr);
59274fe6c29SRuslan Bukin 	}
59374fe6c29SRuslan Bukin 
59474fe6c29SRuslan Bukin 	slist = image->sections;
59574fe6c29SRuslan Bukin 	if (!slist)
59674fe6c29SRuslan Bukin 		return -pte_internal;
59774fe6c29SRuslan Bukin 
59874fe6c29SRuslan Bukin 	*isid = slist->isid;
59974fe6c29SRuslan Bukin 	msec = &slist->section;
60074fe6c29SRuslan Bukin 
60174fe6c29SRuslan Bukin 	section = pt_msec_section(msec);
60274fe6c29SRuslan Bukin 
60374fe6c29SRuslan Bukin 	errcode = pt_section_map(section);
60474fe6c29SRuslan Bukin 	if (errcode < 0)
60574fe6c29SRuslan Bukin 		return errcode;
60674fe6c29SRuslan Bukin 
60774fe6c29SRuslan Bukin 	status = pt_msec_read(msec, buffer, size, addr);
60874fe6c29SRuslan Bukin 
60974fe6c29SRuslan Bukin 	errcode = pt_section_unmap(section);
61074fe6c29SRuslan Bukin 		if (errcode < 0)
61174fe6c29SRuslan Bukin 			return errcode;
61274fe6c29SRuslan Bukin 
61374fe6c29SRuslan Bukin 	if (status < 0) {
61474fe6c29SRuslan Bukin 		if (status != -pte_nomap)
61574fe6c29SRuslan Bukin 			return status;
61674fe6c29SRuslan Bukin 
61774fe6c29SRuslan Bukin 		return pt_image_read_callback(image, isid, buffer, size, asid,
61874fe6c29SRuslan Bukin 					      addr);
61974fe6c29SRuslan Bukin 	}
62074fe6c29SRuslan Bukin 
62174fe6c29SRuslan Bukin 	return status;
62274fe6c29SRuslan Bukin }
62374fe6c29SRuslan Bukin 
pt_image_add_cached(struct pt_image * image,struct pt_image_section_cache * iscache,int isid,const struct pt_asid * uasid)62474fe6c29SRuslan Bukin int pt_image_add_cached(struct pt_image *image,
62574fe6c29SRuslan Bukin 			struct pt_image_section_cache *iscache, int isid,
62674fe6c29SRuslan Bukin 			const struct pt_asid *uasid)
62774fe6c29SRuslan Bukin {
62874fe6c29SRuslan Bukin 	struct pt_section *section;
62974fe6c29SRuslan Bukin 	struct pt_asid asid;
63074fe6c29SRuslan Bukin 	uint64_t vaddr;
63174fe6c29SRuslan Bukin 	int errcode, status;
63274fe6c29SRuslan Bukin 
63374fe6c29SRuslan Bukin 	if (!image || !iscache)
63474fe6c29SRuslan Bukin 		return -pte_invalid;
63574fe6c29SRuslan Bukin 
63674fe6c29SRuslan Bukin 	errcode = pt_iscache_lookup(iscache, &section, &vaddr, isid);
63774fe6c29SRuslan Bukin 	if (errcode < 0)
63874fe6c29SRuslan Bukin 		return errcode;
63974fe6c29SRuslan Bukin 
64074fe6c29SRuslan Bukin 	errcode = pt_asid_from_user(&asid, uasid);
64174fe6c29SRuslan Bukin 	if (errcode < 0)
64274fe6c29SRuslan Bukin 		return errcode;
64374fe6c29SRuslan Bukin 
64474fe6c29SRuslan Bukin 	status = pt_image_add(image, section, &asid, vaddr, isid);
64574fe6c29SRuslan Bukin 
64674fe6c29SRuslan Bukin 	/* We grab a reference when we add the section.  Drop the one we
64774fe6c29SRuslan Bukin 	 * obtained from cache lookup.
64874fe6c29SRuslan Bukin 	 */
64974fe6c29SRuslan Bukin 	errcode = pt_section_put(section);
65074fe6c29SRuslan Bukin 	if (errcode < 0)
65174fe6c29SRuslan Bukin 		return errcode;
65274fe6c29SRuslan Bukin 
65374fe6c29SRuslan Bukin 	return status;
65474fe6c29SRuslan Bukin }
65574fe6c29SRuslan Bukin 
pt_image_find(struct pt_image * image,struct pt_mapped_section * usec,const struct pt_asid * asid,uint64_t vaddr)65674fe6c29SRuslan Bukin int pt_image_find(struct pt_image *image, struct pt_mapped_section *usec,
65774fe6c29SRuslan Bukin 		  const struct pt_asid *asid, uint64_t vaddr)
65874fe6c29SRuslan Bukin {
65974fe6c29SRuslan Bukin 	struct pt_mapped_section *msec;
66074fe6c29SRuslan Bukin 	struct pt_section_list *slist;
66174fe6c29SRuslan Bukin 	struct pt_section *section;
66274fe6c29SRuslan Bukin 	int errcode;
66374fe6c29SRuslan Bukin 
66474fe6c29SRuslan Bukin 	if (!image || !usec)
66574fe6c29SRuslan Bukin 		return -pte_internal;
66674fe6c29SRuslan Bukin 
66774fe6c29SRuslan Bukin 	errcode = pt_image_fetch_section(image, asid, vaddr);
66874fe6c29SRuslan Bukin 	if (errcode < 0)
66974fe6c29SRuslan Bukin 		return errcode;
67074fe6c29SRuslan Bukin 
67174fe6c29SRuslan Bukin 	slist = image->sections;
67274fe6c29SRuslan Bukin 	if (!slist)
67374fe6c29SRuslan Bukin 		return -pte_internal;
67474fe6c29SRuslan Bukin 
67574fe6c29SRuslan Bukin 	msec = &slist->section;
67674fe6c29SRuslan Bukin 	section = pt_msec_section(msec);
67774fe6c29SRuslan Bukin 
67874fe6c29SRuslan Bukin 	errcode = pt_section_get(section);
67974fe6c29SRuslan Bukin 	if (errcode < 0)
68074fe6c29SRuslan Bukin 		return errcode;
68174fe6c29SRuslan Bukin 
68274fe6c29SRuslan Bukin 	*usec = *msec;
68374fe6c29SRuslan Bukin 
68474fe6c29SRuslan Bukin 	return slist->isid;
68574fe6c29SRuslan Bukin }
68674fe6c29SRuslan Bukin 
pt_image_validate(const struct pt_image * image,const struct pt_mapped_section * usec,uint64_t vaddr,int isid)68774fe6c29SRuslan Bukin int pt_image_validate(const struct pt_image *image,
68874fe6c29SRuslan Bukin 		      const struct pt_mapped_section *usec, uint64_t vaddr,
68974fe6c29SRuslan Bukin 		      int isid)
69074fe6c29SRuslan Bukin {
69174fe6c29SRuslan Bukin 	const struct pt_section_list *slist;
69274fe6c29SRuslan Bukin 	uint64_t begin, end;
69374fe6c29SRuslan Bukin 	int status;
69474fe6c29SRuslan Bukin 
69574fe6c29SRuslan Bukin 	if (!image || !usec)
69674fe6c29SRuslan Bukin 		return -pte_internal;
69774fe6c29SRuslan Bukin 
69874fe6c29SRuslan Bukin 	/* Check that @vaddr lies within @usec. */
69974fe6c29SRuslan Bukin 	begin = pt_msec_begin(usec);
70074fe6c29SRuslan Bukin 	end = pt_msec_end(usec);
70174fe6c29SRuslan Bukin 	if (vaddr < begin || end <= vaddr)
70274fe6c29SRuslan Bukin 		return -pte_nomap;
70374fe6c29SRuslan Bukin 
70474fe6c29SRuslan Bukin 	/* We assume that @usec is a copy of the top of our stack and accept
70574fe6c29SRuslan Bukin 	 * sporadic validation fails if it isn't, e.g. because it has moved
70674fe6c29SRuslan Bukin 	 * down.
70774fe6c29SRuslan Bukin 	 *
70874fe6c29SRuslan Bukin 	 * A failed validation requires decoders to re-fetch the section so it
70974fe6c29SRuslan Bukin 	 * only results in a (relatively small) performance loss.
71074fe6c29SRuslan Bukin 	 */
71174fe6c29SRuslan Bukin 	slist = image->sections;
71274fe6c29SRuslan Bukin 	if (!slist)
71374fe6c29SRuslan Bukin 		return -pte_nomap;
71474fe6c29SRuslan Bukin 
71574fe6c29SRuslan Bukin 	if (slist->isid != isid)
71674fe6c29SRuslan Bukin 		return -pte_nomap;
71774fe6c29SRuslan Bukin 
71874fe6c29SRuslan Bukin 	status = memcmp(&slist->section, usec, sizeof(*usec));
71974fe6c29SRuslan Bukin 	if (status)
72074fe6c29SRuslan Bukin 		return -pte_nomap;
72174fe6c29SRuslan Bukin 
72274fe6c29SRuslan Bukin 	return 0;
72374fe6c29SRuslan Bukin }
724