blob: f8b52e5d2a5e71f1ba96952ac57ba747abf5a5e0 [file] [log] [blame]
/*
* Copyright (c) 2016, 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_block_decoder.h"
#include "pt_insn.h"
#include "pt_ild.h"
#include "intel-pt.h"
#include <string.h>
static int pt_blk_proceed(struct pt_block_decoder *, struct pt_block *);
static int pt_blk_process_trailing_events(struct pt_block_decoder *,
struct pt_block *);
static void pt_blk_reset(struct pt_block_decoder *decoder)
{
if (!decoder)
return;
decoder->mode = ptem_unknown;
decoder->ip = 0ull;
decoder->status = 0;
decoder->enabled = 0;
decoder->process_event = 0;
decoder->speculative = 0;
pt_retstack_init(&decoder->retstack);
pt_asid_init(&decoder->asid);
}
int pt_blk_decoder_init(struct pt_block_decoder *decoder,
const struct pt_config *config)
{
int errcode;
if (!decoder)
return -pte_internal;
errcode = pt_qry_decoder_init(&decoder->query, config);
if (errcode < 0)
return errcode;
pt_image_init(&decoder->default_image, NULL);
decoder->image = &decoder->default_image;
pt_blk_reset(decoder);
return 0;
}
void pt_blk_decoder_fini(struct pt_block_decoder *decoder)
{
if (!decoder)
return;
pt_image_fini(&decoder->default_image);
pt_qry_decoder_fini(&decoder->query);
}
struct pt_block_decoder *
pt_blk_alloc_decoder(const struct pt_config *config)
{
struct pt_block_decoder *decoder;
int errcode;
decoder = malloc(sizeof(*decoder));
if (!decoder)
return NULL;
errcode = pt_blk_decoder_init(decoder, config);
if (errcode < 0) {
free(decoder);
return NULL;
}
return decoder;
}
void pt_blk_free_decoder(struct pt_block_decoder *decoder)
{
if (!decoder)
return;
pt_blk_decoder_fini(decoder);
free(decoder);
}
static int pt_blk_start(struct pt_block_decoder *decoder, int status)
{
if (!decoder)
return -pte_internal;
if (status < 0)
return status;
decoder->status = status;
if (!(status & pts_ip_suppressed))
decoder->enabled = 1;
return 0;
}
static int pt_blk_sync_reset(struct pt_block_decoder *decoder)
{
if (!decoder)
return -pte_internal;
pt_blk_reset(decoder);
return 0;
}
int pt_blk_sync_forward(struct pt_block_decoder *decoder)
{
int errcode, status;
if (!decoder)
return -pte_invalid;
errcode = pt_blk_sync_reset(decoder);
if (errcode < 0)
return errcode;
status = pt_qry_sync_forward(&decoder->query, &decoder->ip);
return pt_blk_start(decoder, status);
}
int pt_blk_sync_backward(struct pt_block_decoder *decoder)
{
int errcode, status;
if (!decoder)
return -pte_invalid;
errcode = pt_blk_sync_reset(decoder);
if (errcode < 0)
return errcode;
status = pt_qry_sync_backward(&decoder->query, &decoder->ip);
return pt_blk_start(decoder, status);
}
int pt_blk_sync_set(struct pt_block_decoder *decoder, uint64_t offset)
{
int errcode, status;
if (!decoder)
return -pte_invalid;
errcode = pt_blk_sync_reset(decoder);
if (errcode < 0)
return errcode;
status = pt_qry_sync_set(&decoder->query, &decoder->ip, offset);
return pt_blk_start(decoder, status);
}
int pt_blk_get_offset(struct pt_block_decoder *decoder, uint64_t *offset)
{
if (!decoder)
return -pte_invalid;
return pt_qry_get_offset(&decoder->query, offset);
}
int pt_blk_get_sync_offset(struct pt_block_decoder *decoder, uint64_t *offset)
{
if (!decoder)
return -pte_invalid;
return pt_qry_get_sync_offset(&decoder->query, offset);
}
struct pt_image *pt_blk_get_image(struct pt_block_decoder *decoder)
{
if (!decoder)
return NULL;
return decoder->image;
}
int pt_blk_set_image(struct pt_block_decoder *decoder, struct pt_image *image)
{
if (!decoder)
return -pte_invalid;
if (!image)
image = &decoder->default_image;
decoder->image = image;
return 0;
}
const struct pt_config *
pt_blk_get_config(const struct pt_block_decoder *decoder)
{
if (!decoder)
return NULL;
return pt_qry_get_config(&decoder->query);
}
int pt_blk_time(struct pt_block_decoder *decoder, uint64_t *time,
uint32_t *lost_mtc, uint32_t *lost_cyc)
{
if (!decoder || !time)
return -pte_invalid;
return pt_qry_time(&decoder->query, time, lost_mtc, lost_cyc);
}
int pt_blk_core_bus_ratio(struct pt_block_decoder *decoder, uint32_t *cbr)
{
if (!decoder || !cbr)
return -pte_invalid;
return pt_qry_core_bus_ratio(&decoder->query, cbr);
}
/* Fetch the next pending event.
*
* Checks for pending events. If an event is pending, fetches it (if not
* already in process).
*
* Returns zero if no event is pending.
* Returns a positive integer if an event is pending or in process.
* Returns a negative error code otherwise.
*/
static inline int pt_blk_fetch_event(struct pt_block_decoder *decoder)
{
int status;
if (!decoder)
return -pte_internal;
if (decoder->process_event)
return 1;
if (!(decoder->status & pts_event_pending))
return 0;
status = pt_qry_event(&decoder->query, &decoder->event,
sizeof(decoder->event));
if (status < 0)
return status;
decoder->process_event = 1;
decoder->status = status;
return 1;
}
static inline int pt_blk_block_is_empty(const struct pt_block *block)
{
if (!block)
return 1;
return !block->ninsn;
}
static inline int block_to_user(struct pt_block *ublock, size_t size,
const struct pt_block *block)
{
if (!ublock || !block)
return -pte_internal;
if (ublock == block)
return 0;
/* Zero out any unknown bytes. */
if (sizeof(*block) < size) {
memset(ublock + sizeof(*block), 0, size - sizeof(*block));
size = sizeof(*block);
}
memcpy(ublock, block, size);
return 0;
}
static int pt_insn_false(const struct pt_insn *insn,
const struct pt_insn_ext *iext)
{
(void) insn;
(void) iext;
return 0;
}
/* Determine the next IP using trace.
*
* Tries to determine the IP of the next instruction using trace and provides it
* in @pip.
*
* Not requiring trace to determine the IP is treated as an internal error.
*
* Does not update the return compression stack for indirect calls. This is
* expected to have been done, already, when trying to determine the next IP
* without using trace.
*
* Does not update @decoder->status. The caller is expected to do that.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
* Returns -pte_internal if @pip, @decoder, @insn, or @iext are NULL.
* Returns -pte_internal if no trace is required.
*/
static int pt_blk_next_ip(uint64_t *pip, struct pt_block_decoder *decoder,
const struct pt_insn *insn,
const struct pt_insn_ext *iext)
{
int status;
if (!pip || !decoder || !insn || !iext)
return -pte_internal;
/* We handle non-taken conditional branches, and compressed returns
* directly in the switch.
*
* All kinds of branches are handled below the switch.
*/
switch (insn->iclass) {
case ptic_cond_jump: {
uint64_t ip;
int taken;
status = pt_qry_cond_branch(&decoder->query, &taken);
if (status < 0)
return status;
ip = insn->ip + insn->size;
if (taken)
ip += iext->variant.branch.displacement;
*pip = ip;
return status;
}
case ptic_return: {
int taken, errcode;
/* Check for a compressed return. */
status = pt_qry_cond_branch(&decoder->query, &taken);
if (status < 0) {
if (status != -pte_bad_query)
return status;
break;
}
/* A compressed return is indicated by a taken conditional
* branch.
*/
if (!taken)
return -pte_bad_retcomp;
errcode = pt_retstack_pop(&decoder->retstack, pip);
if (errcode < 0)
return errcode;
return status;
}
case ptic_jump:
case ptic_call:
/* A direct jump or call wouldn't require trace. */
if (iext->variant.branch.is_direct)
return -pte_internal;
break;
case ptic_far_call:
case ptic_far_return:
case ptic_far_jump:
break;
case ptic_other:
return -pte_internal;
case ptic_error:
return -pte_bad_insn;
}
/* Process an indirect branch.
*
* This covers indirect jumps and calls, non-compressed returns, and all
* flavors of far transfers.
*/
return pt_qry_indirect_branch(&decoder->query, pip);
}
/* Process an enabled event.
*
* Determines whether the enabled event can be processed in this iteration or
* has to be postponed.
*
* If the event can be processed, do so and proceed.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_enabled(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
/* This event can't be a status update. */
if (ev->status_update)
return -pte_bad_context;
/* We must have an IP in order to start decoding. */
if (ev->ip_suppressed)
return -pte_noip;
/* We must currently be disabled. */
if (decoder->enabled)
return -pte_bad_context;
/* Delay processing of the event if the block is alredy in progress. */
if (!pt_blk_block_is_empty(block))
return 0;
/* Check if we resumed from a preceding disable or if we enabled at a
* different position.
*/
if (ev->variant.enabled.ip == decoder->ip && !block->enabled)
block->resumed = 1;
else {
block->enabled = 1;
block->resumed = 0;
}
/* Clear an indication of a preceding disable. */
block->disabled = 0;
block->ip = decoder->ip = ev->variant.enabled.ip;
decoder->enabled = 1;
decoder->process_event = 0;
return pt_blk_proceed(decoder, block);
}
/* Apply a disabled event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_disabled(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
/* This event can't be a status update. */
if (ev->status_update)
return -pte_bad_context;
/* We must currently be enabled. */
if (!decoder->enabled)
return -pte_bad_context;
/* We preserve @decoder->ip. This is where we expect tracing to resume
* and we'll indicate that on the subsequent enabled event if tracing
* actually does resume from there.
*/
decoder->enabled = 0;
decoder->process_event = 0;
block->disabled = 1;
return 0;
}
/* Process a disabled event.
*
* We reached the location of a disabled event. This ends a non-empty block.
*
* We may see disabled events for empty blocks when we have a series of enables
* and disabled on the same IP without any trace in between. We ignore the
* disabled event in this case and proceed.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_disabled(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
if (!block)
return -pte_internal;
errcode = pt_blk_apply_disabled(decoder, block, ev);
if (errcode < 0)
return errcode;
/* The event completes a non-empty block. */
if (!pt_blk_block_is_empty(block))
return 0;
/* Ignore the disable if the block is empty. */
block->disabled = 0;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing disabled event.
*
* We reached the location of a disabled event after completing a block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_disabled(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_disabled(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Apply an asynchronous branch event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_async_branch(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
/* This event can't be a status update. */
if (ev->status_update)
return -pte_bad_context;
/* We must currently be enabled. */
if (!decoder->enabled)
return -pte_bad_context;
/* Indicate the async branch as an interrupt. This ends the block. */
block->interrupted = 1;
/* Jump to the branch destination. We will continue from there in the
* next iteration.
*/
decoder->ip = ev->variant.async_branch.to;
decoder->process_event = 0;
return 0;
}
/* Process an asynchronous branch event.
*
* We reached the source location of an asynchronous branch. This ends a
* non-empty block.
*
* We may come across an asynchronous branch for an empty block, e.g. when
* tracing just started. We ignore the event in that case and proceed. It will
* look like tracing started at the asynchronous branch destination instead of
* at its source.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_async_branch(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
if (!block)
return -pte_internal;
errcode = pt_blk_apply_async_branch(decoder, block, ev);
if (errcode < 0)
return errcode;
if (!pt_blk_block_is_empty(block))
return 0;
/* We may still change the start IP for an empty block. Do not indicate
* the interrupt in this case.
*/
block->interrupted = 0;
block->ip = decoder->ip;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing asynchronous branch event.
*
* We reached the source location of an asynchronous branch after completing a
* block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int
pt_blk_process_trailing_async_branch(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_async_branch(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Apply a paging event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_paging(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
(void) block;
if (!decoder || !ev)
return -pte_internal;
decoder->asid.cr3 = ev->variant.paging.cr3;
decoder->process_event = 0;
return 0;
}
/* Process a paging event.
*
* We reached the location of a paging event. Update CR3 and proceed.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_paging(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_paging(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing paging event.
*
* We reached the location of a paging event after completing a block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_paging(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_paging(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Apply a vmcs event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_vmcs(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
(void) block;
if (!decoder || !ev)
return -pte_internal;
decoder->asid.vmcs = ev->variant.vmcs.base;
decoder->process_event = 0;
return 0;
}
/* Process a vmcs event.
*
* We reached the location of a vmcs event. Update VMCS base and proceed.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_vmcs(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_vmcs(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing vmcs event.
*
* We reached the location of a vmcs event after completing a block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_vmcs(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_vmcs(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Process an overflow event.
*
* An overflow ends a non-empty block. The overflow itself is indicated in the
* next block. Indicate the overflow and resume in this case.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_overflow(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
/* This event can't be a status update. */
if (ev->status_update)
return -pte_bad_context;
/* The overflow ends a non-empty block. We will process the event in
* the next iteration.
*/
if (!pt_blk_block_is_empty(block))
return 0;
/* If the IP is suppressed, the overflow resolved while tracing was
* disabled. Otherwise it resolved while tracing was enabled.
*/
if (ev->ip_suppressed) {
/* Tracing is disabled. It doesn't make sense to preserve the
* previous IP. This will just be misleading. Even if tracing
* had been disabled before, as well, we might have missed the
* re-enable in the overflow.
*/
decoder->enabled = 0;
decoder->ip = 0ull;
/* Indicate the overflow. Since tracing is disabled, the block
* will remain empty until tracing gets re-enabled again.
*
* When the block is eventually returned it will have the resync
* and the enabled bit set to indicate the the overflow resolved
* before tracing was enabled.
*/
block->resynced = 1;
} else {
/* Tracing is enabled and we're at the IP at which the overflow
* resolved.
*/
decoder->enabled = 1;
decoder->ip = ev->variant.overflow.ip;
/* Indicate the overflow and set the start IP. The block is
* empty so we may still change it.
*
* We do not indicate a tracing enable if tracing had been
* disabled before to distinguish this from the above case.
*/
block->resynced = 1;
block->ip = decoder->ip;
}
/* We don't know the TSX state. Let's assume we execute normally.
*
* We also don't know the execution mode. Let's keep what we have
* in case we don't get an update before we have to decode the next
* instruction.
*/
decoder->speculative = 0;
decoder->process_event = 0;
return pt_blk_proceed(decoder, block);
}
/* Apply an exec mode event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_exec_mode(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
enum pt_exec_mode mode;
if (!decoder || !block || !ev)
return -pte_internal;
/* Use status update events to diagnose inconsistencies. */
mode = ev->variant.exec_mode.mode;
if (ev->status_update && decoder->enabled &&
decoder->mode != ptem_unknown && decoder->mode != mode)
return -pte_bad_status_update;
decoder->mode = mode;
decoder->process_event = 0;
return 0;
}
/* Process an exec mode event.
*
* We reached the location of an exec mode event. Update the exec mode and
* proceed.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_exec_mode(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
if (!decoder || !block)
return -pte_internal;
errcode = pt_blk_apply_exec_mode(decoder, block, ev);
if (errcode < 0)
return errcode;
/* An execution mode change ends a non-empty block. */
if (!pt_blk_block_is_empty(block))
return 0;
/* We may still change the execution mode of an empty block. */
block->mode = decoder->mode;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing exec mode event.
*
* We reached the location of an exec mode event after completing a block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_exec_mode(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_exec_mode(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Apply a tsx event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_tsx(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
decoder->speculative = ev->variant.tsx.speculative;
decoder->process_event = 0;
if (decoder->enabled && !pt_blk_block_is_empty(block)) {
if (ev->variant.tsx.aborted)
block->aborted = 1;
else if (block->speculative && !ev->variant.tsx.speculative)
block->committed = 1;
}
return 0;
}
/* Process a tsx event.
*
* We reached the location of a tsx event. A speculation mode change ends a
* non-empty block. Indicate commit or abort in the ended block.
*
* We might see tsx event while tracing is disabled or for empty blocks, e.g. if
* tracing was just enabled. In this case we do not indicate the abort or
* commit.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_tsx(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
if (!decoder || !block)
return -pte_internal;
errcode = pt_blk_apply_tsx(decoder, block, ev);
if (errcode < 0)
return errcode;
/* A speculation mode change ends a non-empty block. */
if (!pt_blk_block_is_empty(block))
return 0;
/* We may still change the speculation mode of an empty block. */
block->speculative = decoder->speculative;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing tsx event.
*
* We reached the location of a tsx event after completing a block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_tsx(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_tsx(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Apply a stop event.
*
* This is used for proceed events and for trailing events.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_apply_stop(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
/* This event can't be a status update. */
if (ev->status_update)
return -pte_bad_context;
/* Tracing is always disabled before it is stopped. */
if (decoder->enabled)
return -pte_bad_context;
decoder->process_event = 0;
/* Indicate the stop. */
block->stopped = 1;
return 0;
}
/* Process a stop event.
*
* We got a stop event. This always succeeds a disabled event.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_process_stop(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_stop(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_proceed(decoder, block);
}
/* Process a trailing stop event.
*
* We got a stop event. This always succeeds a disabled event.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_stop(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_event *ev)
{
int errcode;
errcode = pt_blk_apply_stop(decoder, block, ev);
if (errcode < 0)
return errcode;
return pt_blk_process_trailing_events(decoder, block);
}
/* Check if we can reach a particular IP from the current location.
*
* Try to proceed to @ip without using trace. Do not update any internal state
* on our way and ignore errors.
*
* Returns non-zero if @ip was reached.
* Returns zero if @ip could not be reached.
*/
static int pt_blk_ip_is_reachable(struct pt_block_decoder *decoder, uint64_t ip,
size_t steps)
{
struct pt_insn_ext iext;
struct pt_insn insn;
if (!decoder)
return 0;
memset(&insn, 0, sizeof(insn));
memset(&iext, 0, sizeof(iext));
/* We do not expect execution mode changes. */
insn.mode = decoder->mode;
insn.ip = decoder->ip;
for (; steps && (insn.ip != ip); --steps) {
int size, errcode;
/* If we can't read the memory for the instruction, we can't
* reach it.
*/
size = pt_image_read(decoder->image, &insn.isid, insn.raw,
sizeof(insn.raw), &decoder->asid, insn.ip);
if (size < 0)
return 0;
/* We initialize @insn.size to the maximal possible size. It
* will be set to the actual size during instruction decode.
*/
insn.size = (uint8_t) size;
errcode = pt_ild_decode(&insn, &iext);
if (errcode < 0)
return 0;
errcode = pt_insn_next_ip(&insn.ip, &insn, &iext);
if (errcode < 0)
return 0;
}
return 1;
}
/* Proceed to the next IP using trace.
*
* We failed to proceed without trace. This ends the current block. Now use
* trace to do one final step to determine the start IP of the next block.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_proceed_with_trace(struct pt_block_decoder *decoder,
const struct pt_insn *insn,
const struct pt_insn_ext *iext)
{
int status;
if (!decoder)
return -pte_internal;
status = pt_blk_next_ip(&decoder->ip, decoder, insn, iext);
if (status < 0)
return status;
/* Preserve the query decoder's response which indicates upcoming
* events.
*/
decoder->status = status;
/* We do need an IP in order to proceed. */
if (status & pts_ip_suppressed)
return -pte_noip;
return 0;
}
/* Proceed by one instruction.
*
* Tries to decode the instruction at @decoder->ip and, on success, adds it to
* @block and provides it in @pinsn and @piext.
*
* The instruction will not be added if:
*
* - the memory could not be read: return error
* - it could not be decoded: return error
* - @block is already full: return zero
* - @block would switch sections: return zero
*
* Returns a positive integer if the instruction was added.
* Returns zero if the instruction didn't fit into @block.
* Returns a negative error code otherwise.
*/
static int pt_blk_proceed_one_insn(struct pt_block_decoder *decoder,
struct pt_block *block,
struct pt_insn *pinsn,
struct pt_insn_ext *piext)
{
struct pt_insn_ext iext;
struct pt_insn insn;
uint16_t ninsn;
int status;
if (!decoder || !block || !pinsn || !piext)
return -pte_internal;
/* There's nothing to do if there is no room in @block. */
ninsn = block->ninsn + 1;
if (!ninsn)
return 0;
insn.mode = decoder->mode;
insn.ip = decoder->ip;
status = pt_image_read(decoder->image, &insn.isid, insn.raw,
sizeof(insn.raw), &decoder->asid, insn.ip);
if (status < 0)
return status;
/* We do not switch sections inside a block. */
if (insn.isid != block->isid) {
if (!pt_blk_block_is_empty(block))
return 0;
block->isid = insn.isid;
}
insn.size = (uint8_t) status;
status = pt_ild_decode(&insn, &iext);
if (status < 0)
return status;
/* Log calls' return addresses for return compression.
*
* Unless this is a call to the next instruction as is used for position
* independent code.
*/
if ((insn.iclass == ptic_call) &&
(!iext.variant.branch.is_direct ||
iext.variant.branch.displacement)) {
status = pt_retstack_push(&decoder->retstack,
insn.ip + insn.size);
if (status < 0)
return status;
}
/* We have a new instruction. */
block->end_ip = insn.ip;
block->ninsn = ninsn;
*pinsn = insn;
*piext = iext;
return 1;
}
/* Proceed to a particular type of instruction without using trace.
*
* Proceed until we reach an instruction for which @predicate returns a positive
* integer or until:
*
* - @predicate returns an error: return error
* - @block is full: return zero
* - @block would switch sections: return zero
* - we would need trace: return -pte_bad_query
*
* Provide the last instruction that was reached in @insn and @iext.
*
* Update @decoder->ip to point to the last IP that was reached. If we fail due
* to lack of trace or if we reach a desired instruction, this is @insn->ip;
* otherwise this is the next instruction's IP.
*
* Returns a positive integer if a suitable instruction was reached.
* Returns zero if no such instruction was reached.
* Returns a negative error code otherwise.
*/
static int pt_blk_proceed_to_insn(struct pt_block_decoder *decoder,
struct pt_block *block,
struct pt_insn *insn,
struct pt_insn_ext *iext,
int (*predicate)(const struct pt_insn *,
const struct pt_insn_ext *))
{
int status;
if (!decoder || !predicate)
return -pte_internal;
for (;;) {
status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
if (status <= 0)
return status;
/* We're done if this instruction matches the spec (positive
* status) or we run into an error (negative status).
*/
status = predicate(insn, iext);
if (status != 0)
return status;
/* Let's see if we can proceed to the next IP without trace. */
status = pt_insn_next_ip(&decoder->ip, insn, iext);
if (status < 0)
return status;
}
}
/* Proceed to a particular IP without using trace.
*
* Proceed until we reach @ip or until:
*
* - @block is full: return zero
* - @block would switch sections: return zero
* - we would need trace: return -pte_bad_query
*
* Provide the last instruction that was reached in @insn and @iext. If we
* reached @ip, this is the instruction preceding it.
*
* Update @decoder->ip to point to the last IP that was reached. If we fail due
* to lack of trace, this is @insn->ip; otherwise this is the next instruction's
* IP.
*
* Returns a positive integer if @ip was reached.
* Returns zero if no such instruction was reached.
* Returns a negative error code otherwise.
*/
static int pt_blk_proceed_to_ip(struct pt_block_decoder *decoder,
struct pt_block *block, struct pt_insn *insn,
struct pt_insn_ext *iext, uint64_t ip)
{
int status;
if (!decoder)
return -pte_internal;
for (;;) {
/* We're done when we reach @ip. We may not even have to decode
* a single instruction in some cases.
*/
if (decoder->ip == ip)
return 1;
status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
if (status <= 0)
return status;
/* Let's see if we can proceed to the next IP without trace. */
status = pt_insn_next_ip(&decoder->ip, insn, iext);
if (status < 0)
return status;
}
}
/* Proceed to the event location for a disabled event.
*
* We have a (synchronous) disabled event pending. Proceed to the event
* location and indicate whether we were able to reach it.
*
* The last instruction that was reached is stored in @insn/@iext.
*
* Returns a positive integer if the event location was reached.
* Returns zero if the event location was not reached.
* Returns a negative error code otherwise.
*/
static int pt_blk_proceed_to_disabled(struct pt_block_decoder *decoder,
struct pt_block *block,
struct pt_insn *insn,
struct pt_insn_ext *iext,
const struct pt_event *ev)
{
if (!decoder || !block || !ev)
return -pte_internal;
if (ev->ip_suppressed) {
/* A synchronous disabled event also binds to far branches and
* CPL-changing instructions. Both would require trace,
* however, and are thus implicitly handled by erroring out.
*
* The would-require-trace error is handled by our caller.
*/
return pt_blk_proceed_to_insn(decoder, block, insn, iext,
pt_insn_changes_cr3);
} else
return pt_blk_proceed_to_ip(decoder, block, insn, iext,
ev->variant.disabled.ip);
}
/* Proceed from an instruction at which we stopped previously.
*
* We proceeded to @insn/@iext and stopped after decoding and accounting for the
* instruction but before determining the next IP.
*
* Determine the next IP then proceed normally.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_proceed_from_insn(struct pt_block_decoder *decoder,
struct pt_block *block,
const struct pt_insn *insn,
const struct pt_insn_ext *iext)
{
int status;
if (!decoder)
return -pte_internal;
/* Let's see if we can proceed to the next IP without trace. */
status = pt_insn_next_ip(&decoder->ip, insn, iext);
if (status < 0) {
if (status != -pte_bad_query)
return status;
return pt_blk_proceed_with_trace(decoder, insn, iext);
}
return pt_blk_proceed(decoder, block);
}
/* Try to work around erratum SKD022.
*
* If we get an asynchronous disable on VMLAUNCH or VMRESUME, the FUP that
* caused the disable to be asynchronous might have been bogous.
*
* Returns a positive integer if the erratum has been handled.
* Returns zero if the erratum does not apply.
* Returns a negative error code otherwise.
*/
static int pt_blk_handle_erratum_skd022(struct pt_block_decoder *decoder,
struct pt_event *ev)
{
struct pt_insn_ext iext;
struct pt_insn insn;
int size, errcode;
if (!decoder || !ev)
return -pte_internal;
insn.mode = decoder->mode;
insn.ip = ev->variant.async_disabled.at;
size = pt_image_read(decoder->image, &insn.isid, insn.raw,
sizeof(insn.raw), &decoder->asid, insn.ip);
if (size < 0)
return 0;
/* We initialize @insn.size to the maximal possible size. It will be
* set to the actual size during instruction decode.
*/
insn.size = (uint8_t) size;
errcode = pt_ild_decode(&insn, &iext);
if (errcode < 0)
return 0;
switch (iext.iclass) {
default:
/* The erratum does not apply. */
return 0;
case PTI_INST_VMLAUNCH:
case PTI_INST_VMRESUME:
/* The erratum may apply. We can't be sure without a lot more
* analysis. Let's assume it does.
*
* We turn the async disable into a sync disable. Our caller
* will restart event processing.
*/
ev->type = ptev_disabled;
ev->variant.disabled.ip = ev->variant.async_disabled.ip;
return 1;
}
}
/* Proceed to the next event.
*
* We have an event pending. Proceed to the event location and either process
* the event and continue or postpone the event to the next block.
*
* On our way to the event location we may also be forced to postpone the event
* to the next block, e.g. if we overflow the number of instructions in the
* block or if we need trace in order to reach the event location.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_proceed_event(struct pt_block_decoder *decoder,
struct pt_block *block)
{
struct pt_insn_ext iext;
struct pt_insn insn;
struct pt_event *ev;
uint64_t ip;
int status;
if (!decoder || !block)
return -pte_internal;
if (!decoder->process_event)
return -pte_internal;
status = 0;
ev = &decoder->event;
switch (ev->type) {
case ptev_enabled:
return pt_blk_process_enabled(decoder, block, ev);
case ptev_disabled:
status = pt_blk_proceed_to_disabled(decoder, block, &insn,
&iext, ev);
if (status <= 0) {
/* A synchronous disable event also binds to the next
* indirect or conditional branch, i.e. to any branch
* that would have required trace.
*/
if (status != -pte_bad_query)
break;
/* The @decoder->ip still points to the indirect or
* conditional branch instruction that caused us to
* error out. That's not where we expect tracing to
* resume since the instruction already retired.
*
* For calls, a fair assumption is that tracing resumes
* after returning from the called function. For other
* types of instructions, we simply don't know.
*/
switch (insn.iclass) {
case ptic_call:
case ptic_far_call:
decoder->ip = insn.ip + insn.size;
break;
default:
decoder->ip = 0ull;
break;
}
}
return pt_blk_process_disabled(decoder, block, ev);
case ptev_async_disabled:
status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
ev->variant.async_disabled.at);
if (status <= 0)
break;
if (decoder->query.config.errata.skd022) {
status = pt_blk_handle_erratum_skd022(decoder, ev);
if (status != 0) {
if (status < 0)
break;
return pt_blk_proceed_event(decoder, block);
}
}
return pt_blk_process_disabled(decoder, block, ev);
case ptev_async_branch:
status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
ev->variant.async_branch.from);
if (status <= 0)
break;
return pt_blk_process_async_branch(decoder, block, ev);
case ptev_paging:
if (!decoder->enabled)
return pt_blk_process_paging(decoder, block, ev);
status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
pt_insn_binds_to_pip);
if (status <= 0)
break;
status = pt_blk_apply_paging(decoder, block, ev);
if (status < 0)
break;
return pt_blk_proceed_from_insn(decoder, block, &insn, &iext);
case ptev_async_paging:
if (!ev->ip_suppressed) {
ip = ev->variant.async_paging.ip;
status = pt_blk_proceed_to_ip(decoder, block, &insn,
&iext, ip);
if (status <= 0)
break;
}
return pt_blk_process_paging(decoder, block, ev);
case ptev_vmcs:
if (!decoder->enabled)
return pt_blk_process_vmcs(decoder, block, ev);
status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
pt_insn_binds_to_vmcs);
if (status <= 0)
break;
status = pt_blk_apply_vmcs(decoder, block, ev);
if (status < 0)
break;
return pt_blk_proceed_from_insn(decoder, block, &insn, &iext);
case ptev_async_vmcs:
if (!ev->ip_suppressed) {
ip = ev->variant.async_vmcs.ip;
status = pt_blk_proceed_to_ip(decoder, block, &insn,
&iext, ip);
if (status <= 0)
break;
}
return pt_blk_process_vmcs(decoder, block, ev);
case ptev_overflow:
return pt_blk_process_overflow(decoder, block, ev);
case ptev_exec_mode:
if (!ev->ip_suppressed) {
ip = ev->variant.exec_mode.ip;
status = pt_blk_proceed_to_ip(decoder, block, &insn,
&iext, ip);
if (status <= 0)
break;
}
return pt_blk_process_exec_mode(decoder, block, ev);
case ptev_tsx:
if (!ev->ip_suppressed) {
ip = ev->variant.tsx.ip;
status = pt_blk_proceed_to_ip(decoder, block, &insn,
&iext, ip);
if (status <= 0)
break;
}
return pt_blk_process_tsx(decoder, block, ev);
case ptev_stop:
return pt_blk_process_stop(decoder, block, ev);
}
return status;
}
/* Proceed to the next decision point.
*
* Tracing is enabled and we don't have an event pending. Proceed as far as
* we get without trace. Stop when we either:
*
* - need trace in order to continue
* - overflow the max number of instructions in a block
*
* We actually proceed one instruction further to get the start IP for the next
* block. This only updates @decoder's internal state, though.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_proceed_no_event(struct pt_block_decoder *decoder,
struct pt_block *block)
{
struct pt_insn_ext iext;
struct pt_insn insn;
int status;
if (!decoder || !block)
return -pte_internal;
/* This is overly conservative, really. We shouldn't get a bad-query
* status unless we decoded at least one instruction successfully.
*/
memset(&insn, 0, sizeof(insn));
memset(&iext, 0, sizeof(iext));
/* Proceed as far as we get without trace. */
status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
pt_insn_false);
if (status < 0) {
if (status != -pte_bad_query)
return status;
return pt_blk_proceed_with_trace(decoder, &insn, &iext);
}
return 0;
}
/* Proceed to the next event or decision point.
*
* Returns zero on success, a negative error code otherwise.
*/
static int pt_blk_proceed(struct pt_block_decoder *decoder,
struct pt_block *block)
{
int event_pending;
event_pending = pt_blk_fetch_event(decoder);
if (event_pending != 0) {
if (event_pending < 0)
return event_pending;
return pt_blk_proceed_event(decoder, block);
}
/* The end of the trace ends a non-empty block.
*
* If we're called again, we will proceed until we really need trace.
* For example, if tracing is currently disabled.
*/
if (decoder->status & pts_eos) {
if (!pt_blk_block_is_empty(block))
return 0;
if (!decoder->enabled)
return -pte_eos;
}
/* If tracing is disabled and we have still trace left but no event,
* something is wrong.
*/
if (!decoder->enabled)
return -pte_no_enable;
return pt_blk_proceed_no_event(decoder, block);
}
static int pt_blk_status(const struct pt_block_decoder *decoder)
{
int status, flags;
if (!decoder)
return -pte_internal;
status = decoder->status;
flags = 0;
/* Forward end-of-trace indications.
*
* Postpone it as long as we're still processing events, though.
*/
if ((status & pts_eos) && !decoder->process_event)
flags |= pts_eos;
return flags;
}
/* Try to work around erratum BDM64.
*
* If we got a transaction abort immediately following a branch that produced
* trace, the trace for that branch might have been corrupted.
*
* Returns a positive integer if the erratum was handled.
* Returns zero if the erratum does not seem to apply.
* Returns a negative error code otherwise.
*/
static int pt_blk_handle_erratum_bdm64(struct pt_block_decoder *decoder,
const struct pt_event *ev)
{
if (!decoder || !ev)
return -pte_internal;
/* This only affects aborts. */
if (!ev->variant.tsx.aborted)
return 0;
/* Let's check if we can reach the event location from here.
*
* If we can, let's assume the erratum did not hit. We might still be
* wrong but we're not able to tell.
*/
if (pt_blk_ip_is_reachable(decoder, ev->variant.tsx.ip, 0x1000))
return 0;
/* We can't reach the event location. This could either mean that we
* stopped too early (and status is zero) or that the erratum hit.
*
* We assume the latter and pretend that the previous branch brought us
* to the event location, instead.
*/
decoder->ip = ev->variant.tsx.ip;
return 1;
}
/* Process events that bind to the current decoder IP.
*
* We filled a block and proceeded to the next IP, which will become the start
* IP of the next block. Process any pending events that bind to that IP so we
* can indicate their effect in the current block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_process_trailing_events(struct pt_block_decoder *decoder,
struct pt_block *block)
{
struct pt_event *ev;
int event_pending, status;
if (!decoder)
return -pte_internal;
event_pending = pt_blk_fetch_event(decoder);
if (event_pending <= 0) {
if (event_pending < 0)
return event_pending;
return pt_blk_status(decoder);
}
ev = &decoder->event;
switch (ev->type) {
case ptev_enabled:
case ptev_disabled:
case ptev_paging:
case ptev_vmcs:
case ptev_overflow:
break;
case ptev_async_disabled:
if (decoder->ip != ev->variant.async_disabled.at)
break;
if (decoder->query.config.errata.skd022) {
status = pt_blk_handle_erratum_skd022(decoder, ev);
if (status != 0) {
if (status < 0)
break;
return pt_blk_process_trailing_events(decoder,
block);
}
}
return pt_blk_process_trailing_disabled(decoder, block, ev);
case ptev_async_branch:
if (decoder->ip != ev->variant.async_branch.from)
break;
return pt_blk_process_trailing_async_branch(decoder, block, ev);
case ptev_async_paging:
if (!ev->ip_suppressed &&
decoder->ip != ev->variant.async_paging.ip)
break;
return pt_blk_process_trailing_paging(decoder, block, ev);
case ptev_async_vmcs:
if (!ev->ip_suppressed &&
decoder->ip != ev->variant.async_vmcs.ip)
break;
return pt_blk_process_trailing_vmcs(decoder, block, ev);
case ptev_exec_mode:
if (!ev->ip_suppressed &&
decoder->ip != ev->variant.exec_mode.ip)
break;
return pt_blk_process_trailing_exec_mode(decoder, block, ev);
case ptev_tsx:
if (!ev->ip_suppressed) {
if (decoder->query.config.errata.bdm64) {
status = pt_blk_handle_erratum_bdm64(decoder,
ev);
if (status < 0)
break;
}
if (decoder->ip != ev->variant.tsx.ip)
break;
}
return pt_blk_process_trailing_tsx(decoder, block, ev);
case ptev_stop:
return pt_blk_process_trailing_stop(decoder, block, ev);
}
return pt_blk_status(decoder);
}
/* Collect one block.
*
* Fill a new, empty block.
*
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
* code otherwise.
*/
static int pt_blk_collect(struct pt_block_decoder *decoder,
struct pt_block *block)
{
int errcode;
if (!decoder || !block)
return -pte_internal;
/* Zero-initialize the block in case of error returns. */
memset(block, 0, sizeof(*block));
/* Fill in a few things from the current decode state.
*
* This reflects the state of the last pt_blk_next() or pt_blk_start()
* call. Note that, unless we stop with tracing disabled, we proceed
* already to the start IP of the next block.
*
* Some of the state may later be overwritten as we process events.
*/
block->ip = decoder->ip;
block->mode = decoder->mode;
if (decoder->speculative)
block->speculative = 1;
/* Proceed one block. */
errcode = pt_blk_proceed(decoder, block);
if (errcode < 0)
return errcode;
/* We may still have events left that trigger on the current IP.
*
* This IP lies outside of @block but events typically bind to the IP of
* the last instruction that did not retire.
*/
return pt_blk_process_trailing_events(decoder, block);
}
int pt_blk_next(struct pt_block_decoder *decoder, struct pt_block *ublock,
size_t size)
{
struct pt_block block, *pblock;
int errcode, status;
if (!decoder || !ublock)
return -pte_invalid;
pblock = size == sizeof(block) ? ublock : &block;
status = pt_blk_collect(decoder, pblock);
errcode = block_to_user(ublock, size, pblock);
if (errcode < 0)
return errcode;
return status;
}