summaryrefslogtreecommitdiff
path: root/libipt/src/pt_section.c
diff options
context:
space:
mode:
Diffstat (limited to 'libipt/src/pt_section.c')
-rw-r--r--libipt/src/pt_section.c654
1 files changed, 654 insertions, 0 deletions
diff --git a/libipt/src/pt_section.c b/libipt/src/pt_section.c
new file mode 100644
index 000000000000..05f8ee4f7467
--- /dev/null
+++ b/libipt/src/pt_section.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 2013-2019, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pt_section.h"
+#include "pt_block_cache.h"
+#include "pt_image_section_cache.h"
+
+#include "intel-pt.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+int pt_mk_section(struct pt_section **psection, const char *filename,
+ uint64_t offset, uint64_t size)
+{
+ struct pt_section *section;
+ uint64_t fsize;
+ size_t flen;
+ void *status;
+ char *fname;
+ int errcode;
+
+ if (!psection)
+ return -pte_internal;
+
+ flen = strnlen(filename, FILENAME_MAX);
+ if (FILENAME_MAX <= flen)
+ return -pte_invalid;
+
+ flen += 1;
+
+ fname = malloc(flen);
+ if (!fname)
+ return -pte_nomem;
+
+ memcpy(fname, filename, flen);
+
+ errcode = pt_section_mk_status(&status, &fsize, fname);
+ if (errcode < 0)
+ goto out_fname;
+
+ /* Fail if the requested @offset lies beyond the end of @file. */
+ if (fsize <= offset) {
+ errcode = -pte_invalid;
+ goto out_status;
+ }
+
+ /* Truncate @size so the entire range lies within @file. */
+ fsize -= offset;
+ if (fsize < size)
+ size = fsize;
+
+ section = malloc(sizeof(*section));
+ if (!section) {
+ errcode = -pte_nomem;
+ goto out_status;
+ }
+
+ memset(section, 0, sizeof(*section));
+
+ section->filename = fname;
+ section->status = status;
+ section->offset = offset;
+ section->size = size;
+ section->ucount = 1;
+
+#if defined(FEATURE_THREADS)
+
+ errcode = mtx_init(&section->lock, mtx_plain);
+ if (errcode != thrd_success) {
+ free(section);
+
+ errcode = -pte_bad_lock;
+ goto out_status;
+ }
+
+ errcode = mtx_init(&section->alock, mtx_plain);
+ if (errcode != thrd_success) {
+ mtx_destroy(&section->lock);
+ free(section);
+
+ errcode = -pte_bad_lock;
+ goto out_status;
+ }
+
+#endif /* defined(FEATURE_THREADS) */
+
+ *psection = section;
+ return 0;
+
+out_status:
+ free(status);
+
+out_fname:
+ free(fname);
+ return errcode;
+}
+
+int pt_section_lock(struct pt_section *section)
+{
+ if (!section)
+ return -pte_internal;
+
+#if defined(FEATURE_THREADS)
+ {
+ int errcode;
+
+ errcode = mtx_lock(&section->lock);
+ if (errcode != thrd_success)
+ return -pte_bad_lock;
+ }
+#endif /* defined(FEATURE_THREADS) */
+
+ return 0;
+}
+
+int pt_section_unlock(struct pt_section *section)
+{
+ if (!section)
+ return -pte_internal;
+
+#if defined(FEATURE_THREADS)
+ {
+ int errcode;
+
+ errcode = mtx_unlock(&section->lock);
+ if (errcode != thrd_success)
+ return -pte_bad_lock;
+ }
+#endif /* defined(FEATURE_THREADS) */
+
+ return 0;
+}
+
+static void pt_section_free(struct pt_section *section)
+{
+ if (!section)
+ return;
+
+#if defined(FEATURE_THREADS)
+
+ mtx_destroy(&section->alock);
+ mtx_destroy(&section->lock);
+
+#endif /* defined(FEATURE_THREADS) */
+
+ free(section->filename);
+ free(section->status);
+ free(section);
+}
+
+int pt_section_get(struct pt_section *section)
+{
+ uint16_t ucount;
+ int errcode;
+
+ if (!section)
+ return -pte_internal;
+
+ errcode = pt_section_lock(section);
+ if (errcode < 0)
+ return errcode;
+
+ ucount = section->ucount + 1;
+ if (!ucount) {
+ (void) pt_section_unlock(section);
+ return -pte_overflow;
+ }
+
+ section->ucount = ucount;
+
+ return pt_section_unlock(section);
+}
+
+int pt_section_put(struct pt_section *section)
+{
+ uint16_t ucount, mcount;
+ int errcode;
+
+ if (!section)
+ return -pte_internal;
+
+ errcode = pt_section_lock(section);
+ if (errcode < 0)
+ return errcode;
+
+ mcount = section->mcount;
+ ucount = section->ucount;
+ if (ucount > 1) {
+ section->ucount = ucount - 1;
+ return pt_section_unlock(section);
+ }
+
+ errcode = pt_section_unlock(section);
+ if (errcode < 0)
+ return errcode;
+
+ if (!ucount || mcount)
+ return -pte_internal;
+
+ pt_section_free(section);
+ return 0;
+}
+
+static int pt_section_lock_attach(struct pt_section *section)
+{
+ if (!section)
+ return -pte_internal;
+
+#if defined(FEATURE_THREADS)
+ {
+ int errcode;
+
+ errcode = mtx_lock(&section->alock);
+ if (errcode != thrd_success)
+ return -pte_bad_lock;
+ }
+#endif /* defined(FEATURE_THREADS) */
+
+ return 0;
+}
+
+static int pt_section_unlock_attach(struct pt_section *section)
+{
+ if (!section)
+ return -pte_internal;
+
+#if defined(FEATURE_THREADS)
+ {
+ int errcode;
+
+ errcode = mtx_unlock(&section->alock);
+ if (errcode != thrd_success)
+ return -pte_bad_lock;
+ }
+#endif /* defined(FEATURE_THREADS) */
+
+ return 0;
+}
+
+int pt_section_attach(struct pt_section *section,
+ struct pt_image_section_cache *iscache)
+{
+ uint16_t acount, ucount;
+ int errcode;
+
+ if (!section || !iscache)
+ return -pte_internal;
+
+ errcode = pt_section_lock_attach(section);
+ if (errcode < 0)
+ return errcode;
+
+ ucount = section->ucount;
+ acount = section->acount;
+ if (!acount) {
+ if (section->iscache || !ucount)
+ goto out_unlock;
+
+ section->iscache = iscache;
+ section->acount = 1;
+
+ return pt_section_unlock_attach(section);
+ }
+
+ acount += 1;
+ if (!acount) {
+ (void) pt_section_unlock_attach(section);
+ return -pte_overflow;
+ }
+
+ if (ucount < acount)
+ goto out_unlock;
+
+ if (section->iscache != iscache)
+ goto out_unlock;
+
+ section->acount = acount;
+
+ return pt_section_unlock_attach(section);
+
+ out_unlock:
+ (void) pt_section_unlock_attach(section);
+ return -pte_internal;
+}
+
+int pt_section_detach(struct pt_section *section,
+ struct pt_image_section_cache *iscache)
+{
+ uint16_t acount, ucount;
+ int errcode;
+
+ if (!section || !iscache)
+ return -pte_internal;
+
+ errcode = pt_section_lock_attach(section);
+ if (errcode < 0)
+ return errcode;
+
+ if (section->iscache != iscache)
+ goto out_unlock;
+
+ acount = section->acount;
+ if (!acount)
+ goto out_unlock;
+
+ acount -= 1;
+ ucount = section->ucount;
+ if (ucount < acount)
+ goto out_unlock;
+
+ section->acount = acount;
+ if (!acount)
+ section->iscache = NULL;
+
+ return pt_section_unlock_attach(section);
+
+ out_unlock:
+ (void) pt_section_unlock_attach(section);
+ return -pte_internal;
+}
+
+const char *pt_section_filename(const struct pt_section *section)
+{
+ if (!section)
+ return NULL;
+
+ return section->filename;
+}
+
+uint64_t pt_section_size(const struct pt_section *section)
+{
+ if (!section)
+ return 0ull;
+
+ return section->size;
+}
+
+static int pt_section_bcache_memsize(const struct pt_section *section,
+ uint64_t *psize)
+{
+ struct pt_block_cache *bcache;
+
+ if (!section || !psize)
+ return -pte_internal;
+
+ bcache = section->bcache;
+ if (!bcache) {
+ *psize = 0ull;
+ return 0;
+ }
+
+ *psize = sizeof(*bcache) +
+ (bcache->nentries * sizeof(struct pt_bcache_entry));
+
+ return 0;
+}
+
+static int pt_section_memsize_locked(const struct pt_section *section,
+ uint64_t *psize)
+{
+ uint64_t msize, bcsize;
+ int (*memsize)(const struct pt_section *section, uint64_t *size);
+ int errcode;
+
+ if (!section || !psize)
+ return -pte_internal;
+
+ memsize = section->memsize;
+ if (!memsize) {
+ if (section->mcount)
+ return -pte_internal;
+
+ *psize = 0ull;
+ return 0;
+ }
+
+ errcode = memsize(section, &msize);
+ if (errcode < 0)
+ return errcode;
+
+ errcode = pt_section_bcache_memsize(section, &bcsize);
+ if (errcode < 0)
+ return errcode;
+
+ *psize = msize + bcsize;
+
+ return 0;
+}
+
+int pt_section_memsize(struct pt_section *section, uint64_t *size)
+{
+ int errcode, status;
+
+ errcode = pt_section_lock(section);
+ if (errcode < 0)
+ return errcode;
+
+ status = pt_section_memsize_locked(section, size);
+
+ errcode = pt_section_unlock(section);
+ if (errcode < 0)
+ return errcode;
+
+ return status;
+}
+
+uint64_t pt_section_offset(const struct pt_section *section)
+{
+ if (!section)
+ return 0ull;
+
+ return section->offset;
+}
+
+int pt_section_alloc_bcache(struct pt_section *section)
+{
+ struct pt_image_section_cache *iscache;
+ struct pt_block_cache *bcache;
+ uint64_t ssize, memsize;
+ uint32_t csize;
+ int errcode;
+
+ if (!section)
+ return -pte_internal;
+
+ if (!section->mcount)
+ return -pte_internal;
+
+ ssize = pt_section_size(section);
+ csize = (uint32_t) ssize;
+
+ if (csize != ssize)
+ return -pte_not_supported;
+
+ memsize = 0ull;
+
+ /* We need to take both the attach and the section lock in order to pair
+ * the block cache allocation and the resize notification.
+ *
+ * This allows map notifications in between but they only change the
+ * order of sections in the cache.
+ *
+ * The attach lock needs to be taken first.
+ */
+ errcode = pt_section_lock_attach(section);
+ if (errcode < 0)
+ return errcode;
+
+ errcode = pt_section_lock(section);
+ if (errcode < 0)
+ goto out_alock;
+
+ bcache = pt_section_bcache(section);
+ if (bcache) {
+ errcode = 0;
+ goto out_lock;
+ }
+
+ bcache = pt_bcache_alloc(csize);
+ if (!bcache) {
+ errcode = -pte_nomem;
+ goto out_lock;
+ }
+
+ /* Install the block cache. It will become visible and may be used
+ * immediately.
+ *
+ * If we fail later on, we leave the block cache and report the error to
+ * the allocating decoder thread.
+ */
+ section->bcache = bcache;
+
+ errcode = pt_section_memsize_locked(section, &memsize);
+ if (errcode < 0)
+ goto out_lock;
+
+ errcode = pt_section_unlock(section);
+ if (errcode < 0)
+ goto out_alock;
+
+ if (memsize) {
+ iscache = section->iscache;
+ if (iscache) {
+ errcode = pt_iscache_notify_resize(iscache, section,
+ memsize);
+ if (errcode < 0)
+ goto out_alock;
+ }
+ }
+
+ return pt_section_unlock_attach(section);
+
+
+out_lock:
+ (void) pt_section_unlock(section);
+
+out_alock:
+ (void) pt_section_unlock_attach(section);
+ return errcode;
+}
+
+int pt_section_on_map_lock(struct pt_section *section)
+{
+ struct pt_image_section_cache *iscache;
+ int errcode, status;
+
+ if (!section)
+ return -pte_internal;
+
+ errcode = pt_section_lock_attach(section);
+ if (errcode < 0)
+ return errcode;
+
+ iscache = section->iscache;
+ if (!iscache)
+ return pt_section_unlock_attach(section);
+
+ /* There is a potential deadlock when @section was unmapped again and
+ * @iscache tries to map it. This would cause this function to be
+ * re-entered while we're still holding the attach lock.
+ *
+ * This scenario is very unlikely, though, since our caller does not yet
+ * know whether pt_section_map() succeeded.
+ */
+ status = pt_iscache_notify_map(iscache, section);
+
+ errcode = pt_section_unlock_attach(section);
+ if (errcode < 0)
+ return errcode;
+
+ return status;
+}
+
+int pt_section_map_share(struct pt_section *section)
+{
+ uint16_t mcount;
+ int errcode;
+
+ if (!section)
+ return -pte_internal;
+
+ errcode = pt_section_lock(section);
+ if (errcode < 0)
+ return errcode;
+
+ mcount = section->mcount;
+ if (!mcount) {
+ (void) pt_section_unlock(section);
+ return -pte_internal;
+ }
+
+ mcount += 1;
+ if (!mcount) {
+ (void) pt_section_unlock(section);
+ return -pte_overflow;
+ }
+
+ section->mcount = mcount;
+
+ return pt_section_unlock(section);
+}
+
+int pt_section_unmap(struct pt_section *section)
+{
+ uint16_t mcount;
+ int errcode, status;
+
+ if (!section)
+ return -pte_internal;
+
+ errcode = pt_section_lock(section);
+ if (errcode < 0)
+ return errcode;
+
+ mcount = section->mcount;
+
+ errcode = -pte_nomap;
+ if (!mcount)
+ goto out_unlock;
+
+ section->mcount = mcount -= 1;
+ if (mcount)
+ return pt_section_unlock(section);
+
+ errcode = -pte_internal;
+ if (!section->unmap)
+ goto out_unlock;
+
+ status = section->unmap(section);
+
+ pt_bcache_free(section->bcache);
+ section->bcache = NULL;
+
+ errcode = pt_section_unlock(section);
+ if (errcode < 0)
+ return errcode;
+
+ return status;
+
+out_unlock:
+ (void) pt_section_unlock(section);
+ return errcode;
+}
+
+int pt_section_read(const struct pt_section *section, uint8_t *buffer,
+ uint16_t size, uint64_t offset)
+{
+ uint64_t limit, space;
+
+ if (!section)
+ return -pte_internal;
+
+ if (!section->read)
+ return -pte_nomap;
+
+ limit = section->size;
+ if (limit <= offset)
+ return -pte_nomap;
+
+ /* Truncate if we try to read past the end of the section. */
+ space = limit - offset;
+ if (space < size)
+ size = (uint16_t) space;
+
+ return section->read(section, buffer, size, offset);
+}