diff options
Diffstat (limited to 'libipt/src/pt_sync.c')
-rw-r--r-- | libipt/src/pt_sync.c | 248 |
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; + } +} |