/* Check whether a mapped section contains an address. * * Returns zero if @msec contains @vaddr. * Returns a negative error code otherwise. * Returns -pte_nomap if @msec does not contain @vaddr. */ static inline int pt_image_check_msec(const struct pt_mapped_section *msec, const struct pt_asid *asid, uint64_t vaddr) { const struct pt_asid *masid; uint64_t begin, end; int errcode; if (!msec) return -pte_internal; begin = pt_msec_begin(msec); end = pt_msec_end(msec); if (vaddr < begin || end <= vaddr) return -pte_nomap; masid = pt_msec_asid(msec); errcode = pt_asid_match(masid, asid); if (errcode <= 0) { if (!errcode) errcode = -pte_nomap; return errcode; } return 0; }
static struct ptunit_result asid_null(void) { const struct pt_asid *asid; asid = pt_msec_asid(NULL); ptu_null(asid); return ptu_passed(); }
static int pt_image_clone(struct pt_section_list **list, const struct pt_mapped_section *msec, uint64_t begin, uint64_t end, int isid) { const struct pt_asid *masid; struct pt_section_list *next; struct pt_section *section, *sec; uint64_t mbegin, sbegin, offset, size; int errcode; if (!list || !msec) return -pte_internal; sec = pt_msec_section(msec); masid = pt_msec_asid(msec); mbegin = pt_msec_begin(msec); sbegin = pt_section_offset(sec); if (end <= begin) return -pte_internal; if (begin < mbegin) return -pte_internal; offset = begin - mbegin; size = end - begin; errcode = pt_section_clone(§ion, sec, sbegin + offset, size); if (errcode < 0) return errcode; next = pt_mk_section_list(section, masid, begin, isid); if (!next) { (void) pt_section_put(section); return -pte_nomem; } /* The image list got its own reference; let's drop ours. */ errcode = pt_section_put(section); if (errcode < 0) { pt_section_list_free(next); return errcode; } /* Add the new section. */ next->next = *list; *list = next; return 0; }
int pt_image_remove_by_filename(struct pt_image *image, const char *filename, const struct pt_asid *uasid) { struct pt_section_list **list; struct pt_asid asid; int errcode, removed; if (!image || !filename) return -pte_invalid; errcode = pt_asid_from_user(&asid, uasid); if (errcode < 0) return errcode; removed = 0; for (list = &image->sections; *list;) { struct pt_mapped_section *msec; const struct pt_section *sec; const struct pt_asid *masid; struct pt_section_list *trash; const char *tname; trash = *list; msec = &trash->section; masid = pt_msec_asid(msec); errcode = pt_asid_match(masid, &asid); if (errcode < 0) return errcode; if (!errcode) { list = &trash->next; continue; } sec = pt_msec_section(msec); tname = pt_section_filename(sec); if (tname && (strcmp(tname, filename) == 0)) { *list = trash->next; pt_section_list_free(trash); removed += 1; } else list = &trash->next; } return removed; }
static struct ptunit_result asid(void) { struct pt_mapped_section msec; struct pt_asid asid; const struct pt_asid *pasid; pt_asid_init(&asid); asid.cr3 = 0xa00; pt_msec_init(&msec, NULL, &asid, 0ull); pasid = pt_msec_asid(&msec); ptu_uint_eq(pasid->size, asid.size); ptu_uint_eq(pasid->cr3, asid.cr3); return ptu_passed(); }
int pt_image_remove_by_asid(struct pt_image *image, const struct pt_asid *uasid) { struct pt_section_list **list; struct pt_asid asid; int errcode, removed; if (!image) return -pte_invalid; errcode = pt_asid_from_user(&asid, uasid); if (errcode < 0) return errcode; removed = 0; for (list = &image->sections; *list;) { struct pt_mapped_section *msec; const struct pt_asid *masid; struct pt_section_list *trash; trash = *list; msec = &trash->section; masid = pt_msec_asid(msec); errcode = pt_asid_match(masid, &asid); if (errcode < 0) return errcode; if (!errcode) { list = &trash->next; continue; } *list = trash->next; pt_section_list_free(trash); removed += 1; } return removed; }
int pt_image_remove(struct pt_image *image, struct pt_section *section, const struct pt_asid *asid, uint64_t vaddr) { struct pt_section_list **list; if (!image || !section) return -pte_internal; for (list = &image->sections; *list; list = &((*list)->next)) { struct pt_mapped_section *msec; const struct pt_section *sec; const struct pt_asid *masid; struct pt_section_list *trash; uint64_t begin; int errcode; trash = *list; msec = &trash->section; masid = pt_msec_asid(msec); errcode = pt_asid_match(masid, asid); if (errcode < 0) return errcode; if (!errcode) continue; begin = pt_msec_begin(msec); sec = pt_msec_section(msec); if (sec == section && begin == vaddr) { *list = trash->next; pt_section_list_free(trash); return 0; } } return -pte_bad_image; }
int pt_image_add(struct pt_image *image, struct pt_section *section, const struct pt_asid *asid, uint64_t vaddr, int isid) { struct pt_section_list **list, *next, *removed; uint64_t begin, end; int errcode; if (!image || !section) return -pte_internal; next = pt_mk_section_list(section, asid, vaddr, isid); if (!next) return -pte_nomem; removed = NULL; errcode = 0; begin = vaddr; end = begin + pt_section_size(section); /* Check for overlaps while we move to the end of the list. */ list = &(image->sections); while (*list) { const struct pt_mapped_section *msec; const struct pt_asid *masid; struct pt_section_list *current; struct pt_section *lsec; uint64_t lbegin, lend; current = *list; msec = ¤t->section; masid = pt_msec_asid(msec); errcode = pt_asid_match(masid, asid); if (errcode < 0) break; if (!errcode) { list = &((*list)->next); continue; } lbegin = pt_msec_begin(msec); lend = pt_msec_end(msec); if ((end <= lbegin) || (lend <= begin)) { list = &((*list)->next); continue; } /* The new section overlaps with @msec's section. */ lsec = pt_msec_section(msec); /* Let's check for an identical overlap that may be the result * of repeatedly copying images or repeatedly adding the same * file. */ if ((begin == lbegin) && (end == lend) && (isid == current->isid)) { const char *fname, *lfname; fname = pt_section_filename(section); lfname = pt_section_filename(lsec); if (!fname || !lfname) { errcode = -pte_internal; break; } if (strcmp(fname, lfname) == 0) { /* There should not have been any removals or * additions. */ if (removed || next->next) { errcode = -pte_internal; break; } pt_section_list_free(next); return 0; } } /* We remove @msec and insert new sections for the remaining * parts, if any. Those new sections are not mapped initially * and need to be added to the end of the section list. */ *list = current->next; /* Keep a list of removed sections so we can re-add them in case * of errors. */ current->next = removed; removed = current; /* Unmap the removed section. If we need to re-add it, it will * be moved to the end of the section list where the unmapped * sections are. */ if (current->mapped) { pt_section_unmap(lsec); current->mapped = 0; } /* Add a section covering the remaining bytes at the front. * * We preserve the section identifier to indicate that the new * section originated from the original section. */ if (lbegin < begin) { errcode = pt_image_clone(&next, msec, lbegin, begin, current->isid); if (errcode < 0) break; } /* Add a section covering the remaining bytes at the back. * * We preserve the section identifier to indicate that the new * section originated from the original section. */ if (end < lend) { errcode = pt_image_clone(&next, msec, end, lend, current->isid); if (errcode < 0) break; } } if (errcode < 0) { pt_section_list_free_tail(next); /* Re-add removed sections to the tail of the section list. */ for (; *list; list = &((*list)->next)) ; *list = removed; return errcode; } pt_section_list_free_tail(removed); *list = next; return 0; }