static int section_match(const struct pt_section *lhs, const struct pt_section *rhs) { const char *lfilename, *rfilename; if (!lhs || !rhs) return -pte_internal; if (pt_section_offset(lhs) != pt_section_offset(rhs)) return 0; if (pt_section_size(lhs) != pt_section_size(rhs)) return 0; lfilename = pt_section_filename(lhs); rfilename = pt_section_filename(rhs); if (!lfilename || !rfilename) return -pte_internal; if (strcmp(lfilename, rfilename) != 0) return 0; return 1; }
int pt_section_clone(struct pt_section **pclone, const struct pt_section *section, uint64_t offset, uint64_t size) { struct pt_section *clone; uint64_t begin, end, sbegin, send; if (!pclone || !section) return -pte_internal; begin = offset; end = begin + size; sbegin = pt_section_offset(section); send = sbegin + pt_section_size(section); if (begin < sbegin || send < end) return -pte_internal; clone = pt_mk_section(pt_section_filename(section), offset, size); if (!clone) return -pte_nomem; *pclone = clone; return 0; }
int pt_image_remove_by_name(struct pt_image *image, const char *name) { struct pt_section_list **list; int removed; if (!image || !name) return -pte_invalid; removed = 0; for (list = &image->sections; *list;) { struct pt_section_list *trash; struct pt_section *section; const char *tname; trash = *list; section = trash->section; tname = pt_section_filename(section); if (tname && (strcmp(tname, name) == 0)) { if (image->cache == section) image->cache = NULL; *list = trash->next; pt_section_list_free(trash); removed += 1; } else list = &trash->next; } return removed; }
static struct ptunit_result filename_null(void) { const char *name; name = pt_section_filename(NULL); ptu_null(name); return ptu_passed(); }
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 int pt_iscache_find_locked(struct pt_image_section_cache *iscache, const char *filename, uint64_t offset, uint64_t size, uint64_t laddr) { uint16_t idx, end; if (!iscache || !filename) return -pte_internal; end = iscache->size; for (idx = 0; idx < end; ++idx) { const struct pt_iscache_entry *entry; const struct pt_section *section; const char *sec_filename; uint64_t sec_offset, sec_size; entry = &iscache->entries[idx]; /* We do not zero-initialize the array - a NULL check is * pointless. */ section = entry->section; sec_filename = pt_section_filename(section); sec_offset = pt_section_offset(section); sec_size = pt_section_size(section); if (entry->laddr != laddr) continue; if (sec_offset != offset) continue; if (sec_size != size) continue; /* We should not have a section without a filename. */ if (!sec_filename) return -pte_internal; if (strcmp(sec_filename, filename) != 0) continue; return isid_from_index(idx); } return 0; }
static struct ptunit_result create(struct section_fixture *sfix) { const char *name; uint8_t bytes[] = { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }; uint64_t size; sfix_write(sfix, bytes); sfix->section = pt_mk_section(sfix->name, 0x1ull, 0x3ull); ptu_ptr(sfix->section); name = pt_section_filename(sfix->section); ptu_str_eq(name, sfix->name); size = pt_section_size(sfix->section); ptu_uint_eq(size, 0x3ull); return ptu_passed(); }
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; }
int pt_iscache_add(struct pt_image_section_cache *iscache, struct pt_section *section, uint64_t laddr) { uint16_t idx, end; int errcode; if (!iscache || !section) return -pte_internal; /* We must have a filename for @section. */ if (!pt_section_filename(section)) return -pte_internal; errcode = pt_iscache_lock(iscache); if (errcode < 0) return errcode; end = iscache->size; for (idx = 0; idx < end; ++idx) { const struct pt_iscache_entry *entry; struct pt_section *sec; entry = &iscache->entries[idx]; /* We do not zero-initialize the array - a NULL check is * pointless. */ sec = entry->section; errcode = section_match(section, sec); if (errcode <= 0) { if (errcode < 0) goto out_unlock; continue; } /* Use the cached section instead of the argument section. * * We'll be able to drop the argument section in this case and * only keep one copy around and, more importantly, mapped. */ section = sec; /* If we also find a matching load address, we're done. */ if (laddr == entry->laddr) break; } /* If we have not found a matching entry, add one. */ if (idx == end) { struct pt_iscache_entry *entry; /* Expand the cache, if necessary. */ if (iscache->capacity <= iscache->size) { /* We must never exceed the capacity. */ if (iscache->capacity < iscache->size) { errcode = -pte_internal; goto out_unlock; } errcode = pt_iscache_expand(iscache); if (errcode < 0) goto out_unlock; /* Make sure it is big enough, now. */ if (iscache->capacity <= iscache->size) { errcode = -pte_internal; goto out_unlock; } } errcode = pt_section_get(section); if (errcode < 0) goto out_unlock; idx = iscache->size++; entry = &iscache->entries[idx]; entry->section = section; entry->laddr = laddr; } errcode = pt_iscache_unlock(iscache); if (errcode < 0) return errcode; return isid_from_index(idx); out_unlock: (void) pt_iscache_unlock(iscache); return errcode; }