/*
* 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;
}
}