summaryrefslogtreecommitdiff
path: root/libipt/src/pt_sync.c
diff options
context:
space:
mode:
Diffstat (limited to 'libipt/src/pt_sync.c')
-rw-r--r--libipt/src/pt_sync.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/libipt/src/pt_sync.c b/libipt/src/pt_sync.c
new file mode 100644
index 0000000000000..53d36e74d5294
--- /dev/null
+++ b/libipt/src/pt_sync.c
@@ -0,0 +1,248 @@
+/*
+ * 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_sync.h"
+#include "pt_packet.h"
+#include "pt_opcodes.h"
+
+#include "intel-pt.h"
+
+
+/* A psb packet contains a unique 2-byte repeating pattern.
+ *
+ * There are only two ways to fill up a 64bit work with such a pattern.
+ */
+static const uint64_t psb_pattern[] = {
+ ((uint64_t) pt_psb_lohi | (uint64_t) pt_psb_lohi << 16 |
+ (uint64_t) pt_psb_lohi << 32 | (uint64_t) pt_psb_lohi << 48),
+ ((uint64_t) pt_psb_hilo | (uint64_t) pt_psb_hilo << 16 |
+ (uint64_t) pt_psb_hilo << 32 | (uint64_t) pt_psb_hilo << 48)
+};
+
+static const uint8_t *truncate(const uint8_t *pointer, size_t alignment)
+{
+ uintptr_t raw = (uintptr_t) pointer;
+
+ raw /= alignment;
+ raw *= alignment;
+
+ return (const uint8_t *) raw;
+}
+
+static const uint8_t *align(const uint8_t *pointer, size_t alignment)
+{
+ return truncate(pointer + alignment - 1, alignment);
+}
+
+/* Find a psb packet given a position somewhere in the payload.
+ *
+ * Return the position of the psb packet.
+ * Return NULL, if this is not a psb packet.
+ */
+static const uint8_t *pt_find_psb(const uint8_t *pos,
+ const struct pt_config *config)
+{
+ const uint8_t *begin, *end;
+ int errcode;
+
+ if (!pos || !config)
+ return NULL;
+
+ begin = config->begin;
+ end = config->end;
+
+ /* Navigate to the end of the psb payload pattern.
+ *
+ * Beware that PSB is an extended opcode. We must not confuse the extend
+ * opcode of the following packet as belonging to the PSB.
+ */
+ if (*pos != pt_psb_hi)
+ pos++;
+
+ for (; (pos + 1) < end; pos += 2) {
+ uint8_t hi, lo;
+
+ hi = pos[0];
+ lo = pos[1];
+
+ if (hi != pt_psb_hi)
+ break;
+
+ if (lo != pt_psb_lo)
+ break;
+ }
+ /*
+ * We're right after the psb payload and within the buffer.
+ * Navigate to the expected beginning of the psb packet.
+ */
+ pos -= ptps_psb;
+
+ /* Check if we're still inside the buffer. */
+ if (pos < begin)
+ return NULL;
+
+ /* Check that this is indeed a psb packet we're at. */
+ if (pos[0] != pt_opc_psb || pos[1] != pt_ext_psb)
+ return NULL;
+
+ errcode = pt_pkt_read_psb(pos, config);
+ if (errcode < 0)
+ return NULL;
+
+ return pos;
+}
+
+static int pt_sync_within_bounds(const uint8_t *pos, const uint8_t *begin,
+ const uint8_t *end)
+{
+ /* We allow @pos == @end representing the very end of the trace.
+ *
+ * This will result in -pte_eos when we actually try to read from @pos.
+ */
+ return (begin <= pos) && (pos <= end);
+}
+
+int pt_sync_set(const uint8_t **sync, const uint8_t *pos,
+ const struct pt_config *config)
+{
+ const uint8_t *begin, *end;
+ int errcode;
+
+ if (!sync || !pos || !config)
+ return -pte_internal;
+
+ begin = config->begin;
+ end = config->end;
+
+ if (!pt_sync_within_bounds(pos, begin, end))
+ return -pte_eos;
+
+ if (end < pos + 2)
+ return -pte_eos;
+
+ /* Check that this is indeed a psb packet we're at. */
+ if (pos[0] != pt_opc_psb || pos[1] != pt_ext_psb)
+ return -pte_nosync;
+
+ errcode = pt_pkt_read_psb(pos, config);
+ if (errcode < 0)
+ return errcode;
+
+ *sync = pos;
+
+ return 0;
+}
+
+int pt_sync_forward(const uint8_t **sync, const uint8_t *pos,
+ const struct pt_config *config)
+{
+ const uint8_t *begin, *end, *start;
+
+ if (!sync || !pos || !config)
+ return -pte_internal;
+
+ start = pos;
+ begin = config->begin;
+ end = config->end;
+
+ if (!pt_sync_within_bounds(pos, begin, end))
+ return -pte_internal;
+
+ /* We search for a full 64bit word. It's OK to skip the current one. */
+ pos = align(pos, sizeof(*psb_pattern));
+
+ /* Search for the psb payload pattern in the buffer. */
+ for (;;) {
+ const uint8_t *current = pos;
+ uint64_t val;
+
+ pos += sizeof(uint64_t);
+ if (end < pos)
+ return -pte_eos;
+
+ val = * (const uint64_t *) current;
+
+ if ((val != psb_pattern[0]) && (val != psb_pattern[1]))
+ continue;
+
+ /* We found a 64bit word's worth of psb payload pattern. */
+ current = pt_find_psb(pos, config);
+ if (!current)
+ continue;
+
+ /* If @start points inside a PSB, we may find that one. Ignore
+ * it unless @start points to its beginning.
+ */
+ if (current < start)
+ continue;
+
+ *sync = current;
+ return 0;
+ }
+}
+
+int pt_sync_backward(const uint8_t **sync, const uint8_t *pos,
+ const struct pt_config *config)
+{
+ const uint8_t *begin, *end;
+
+ if (!sync || !pos || !config)
+ return -pte_internal;
+
+ begin = config->begin;
+ end = config->end;
+
+ if (!pt_sync_within_bounds(pos, begin, end))
+ return -pte_internal;
+
+ /* We search for a full 64bit word. It's OK to skip the current one. */
+ pos = truncate(pos, sizeof(*psb_pattern));
+
+ /* Search for the psb payload pattern in the buffer. */
+ for (;;) {
+ const uint8_t *next = pos;
+ uint64_t val;
+
+ pos -= sizeof(uint64_t);
+ if (pos < begin)
+ return -pte_eos;
+
+ val = * (const uint64_t *) pos;
+
+ if ((val != psb_pattern[0]) && (val != psb_pattern[1]))
+ continue;
+
+ /* We found a 64bit word's worth of psb payload pattern. */
+ next = pt_find_psb(next, config);
+ if (!next)
+ continue;
+
+ *sync = next;
+ return 0;
+ }
+}