|
Packit |
b1f7ae |
/*
|
|
Packit |
b1f7ae |
* Copyright (c) 2016-2017, Intel Corporation
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Redistribution and use in source and binary forms, with or without
|
|
Packit |
b1f7ae |
* modification, are permitted provided that the following conditions are met:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* * Redistributions of source code must retain the above copyright notice,
|
|
Packit |
b1f7ae |
* this list of conditions and the following disclaimer.
|
|
Packit |
b1f7ae |
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
Packit |
b1f7ae |
* this list of conditions and the following disclaimer in the documentation
|
|
Packit |
b1f7ae |
* and/or other materials provided with the distribution.
|
|
Packit |
b1f7ae |
* * Neither the name of Intel Corporation nor the names of its contributors
|
|
Packit |
b1f7ae |
* may be used to endorse or promote products derived from this software
|
|
Packit |
b1f7ae |
* without specific prior written permission.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
Packit |
b1f7ae |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
Packit |
b1f7ae |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
b1f7ae |
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
Packit |
b1f7ae |
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
Packit |
b1f7ae |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
Packit |
b1f7ae |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
b1f7ae |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
Packit |
b1f7ae |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
b1f7ae |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
Packit |
b1f7ae |
* POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
#include "pt_block_decoder.h"
|
|
Packit |
b1f7ae |
#include "pt_block_cache.h"
|
|
Packit |
b1f7ae |
#include "pt_section.h"
|
|
Packit |
b1f7ae |
#include "pt_image.h"
|
|
Packit |
b1f7ae |
#include "pt_insn.h"
|
|
Packit |
b1f7ae |
#include "pt_ild.h"
|
|
Packit |
b1f7ae |
#include "pt_config.h"
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
#include "intel-pt.h"
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
#include <string.h>
|
|
Packit |
b1f7ae |
#include <stdlib.h>
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_no_event(struct pt_block_decoder *,
|
|
Packit |
b1f7ae |
struct pt_block *);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Release a cached section.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If @scache does not contain a section, this does noting.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
* Returns -pte_internal, if @scache is NULL.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_scache_invalidate(struct pt_cached_section *scache)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_section *section;
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!scache)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
section = scache->section;
|
|
Packit |
b1f7ae |
if (!section)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_section_unmap(section);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
scache->section = NULL;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_section_put(section);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Cache @section loaded at @laddr identified by @isid in @scache.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* The caller transfers its use- and map-count to @scache.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
* Returns -pte_internal if @scache or @section is NULL.
|
|
Packit |
b1f7ae |
* Returns -pte_internal if another section is already cached.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_cache_section(struct pt_cached_section *scache,
|
|
Packit |
b1f7ae |
struct pt_section *section, uint64_t laddr,
|
|
Packit |
b1f7ae |
int isid)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!scache || !section)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (scache->section)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
scache->section = section;
|
|
Packit |
b1f7ae |
scache->laddr = laddr;
|
|
Packit |
b1f7ae |
scache->isid = isid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Get @scache's cached section.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Check whether @scache contains a section that an image lookup of @ip in @asid
|
|
Packit |
b1f7ae |
* would return. On success, provides the cached section in @psection and its
|
|
Packit |
b1f7ae |
* load address in @pladdr.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns the section's identifier on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
* Returns -pte_internal if @scache, @psection, or @pladdr is NULL.
|
|
Packit |
b1f7ae |
* Returns -pte_nomap if @scache does not have a section cached.
|
|
Packit |
b1f7ae |
* Returns -pte_nomap if @scache's cached section does not contain @ip.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_cached_section(struct pt_cached_section *scache,
|
|
Packit |
b1f7ae |
struct pt_section **psection, uint64_t *pladdr,
|
|
Packit |
b1f7ae |
struct pt_image *image, struct pt_asid *asid,
|
|
Packit |
b1f7ae |
uint64_t ip)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_section *section;
|
|
Packit |
b1f7ae |
uint64_t laddr;
|
|
Packit |
b1f7ae |
int isid, errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!scache || !psection || !pladdr)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
section = scache->section;
|
|
Packit |
b1f7ae |
laddr = scache->laddr;
|
|
Packit |
b1f7ae |
isid = scache->isid;
|
|
Packit |
b1f7ae |
if (!section)
|
|
Packit |
b1f7ae |
return -pte_nomap;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_image_validate(image, asid, ip, section, laddr, isid);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
*psection = section;
|
|
Packit |
b1f7ae |
*pladdr = laddr;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return isid;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static void pt_blk_reset(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->mode = ptem_unknown;
|
|
Packit |
b1f7ae |
decoder->ip = 0ull;
|
|
Packit |
b1f7ae |
decoder->status = 0;
|
|
Packit |
b1f7ae |
decoder->enabled = 0;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
decoder->speculative = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pt_retstack_init(&decoder->retstack);
|
|
Packit |
b1f7ae |
pt_asid_init(&decoder->asid);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Initialize the query decoder flags based on our flags. */
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static int pt_blk_init_qry_flags(struct pt_conf_flags *qflags,
|
|
Packit |
b1f7ae |
const struct pt_conf_flags *flags)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!qflags || !flags)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(qflags, 0, sizeof(*qflags));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_decoder_init(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
const struct pt_config *uconfig)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_config config;
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_config_from_user(&config, uconfig);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The user supplied decoder flags. */
|
|
Packit |
b1f7ae |
decoder->flags = config.flags;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Set the flags we need for the query decoder we use. */
|
|
Packit |
b1f7ae |
errcode = pt_blk_init_qry_flags(&config.flags, &decoder->flags);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_qry_decoder_init(&decoder->query, &config);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pt_image_init(&decoder->default_image, NULL);
|
|
Packit |
b1f7ae |
decoder->image = &decoder->default_image;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&decoder->scache, 0, sizeof(decoder->scache));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pt_blk_reset(decoder);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
void pt_blk_decoder_fini(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Release the cached section so we don't leak it. */
|
|
Packit |
b1f7ae |
(void) pt_blk_scache_invalidate(&decoder->scache);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pt_image_fini(&decoder->default_image);
|
|
Packit |
b1f7ae |
pt_qry_decoder_fini(&decoder->query);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
struct pt_block_decoder *
|
|
Packit |
b1f7ae |
pt_blk_alloc_decoder(const struct pt_config *config)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_block_decoder *decoder;
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder = malloc(sizeof(*decoder));
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return NULL;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_decoder_init(decoder, config);
|
|
Packit |
b1f7ae |
if (errcode < 0) {
|
|
Packit |
b1f7ae |
free(decoder);
|
|
Packit |
b1f7ae |
return NULL;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return decoder;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
void pt_blk_free_decoder(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pt_blk_decoder_fini(decoder);
|
|
Packit |
b1f7ae |
free(decoder);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static int pt_blk_start(struct pt_block_decoder *decoder, int status)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
if (!(status & pts_ip_suppressed))
|
|
Packit |
b1f7ae |
decoder->enabled = 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static int pt_blk_sync_reset(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pt_blk_reset(decoder);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_sync_forward(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode, status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_sync_reset(decoder);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_qry_sync_forward(&decoder->query, &decoder->ip);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_start(decoder, status);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_sync_backward(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode, status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_sync_reset(decoder);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_qry_sync_backward(&decoder->query, &decoder->ip);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_start(decoder, status);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_sync_set(struct pt_block_decoder *decoder, uint64_t offset)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode, status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_sync_reset(decoder);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_qry_sync_set(&decoder->query, &decoder->ip, offset);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_start(decoder, status);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_get_offset(struct pt_block_decoder *decoder, uint64_t *offset)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_qry_get_offset(&decoder->query, offset);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_get_sync_offset(struct pt_block_decoder *decoder, uint64_t *offset)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_qry_get_sync_offset(&decoder->query, offset);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
struct pt_image *pt_blk_get_image(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return NULL;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return decoder->image;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_set_image(struct pt_block_decoder *decoder, struct pt_image *image)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!image)
|
|
Packit |
b1f7ae |
image = &decoder->default_image;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->image = image;
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
const struct pt_config *
|
|
Packit |
b1f7ae |
pt_blk_get_config(const struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return NULL;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_qry_get_config(&decoder->query);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_time(struct pt_block_decoder *decoder, uint64_t *time,
|
|
Packit |
b1f7ae |
uint32_t *lost_mtc, uint32_t *lost_cyc)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !time)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_qry_time(&decoder->query, time, lost_mtc, lost_cyc);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_core_bus_ratio(struct pt_block_decoder *decoder, uint32_t *cbr)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !cbr)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_qry_core_bus_ratio(&decoder->query, cbr);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Fetch the next pending event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Checks for pending events. If an event is pending, fetches it (if not
|
|
Packit |
b1f7ae |
* already in process).
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero if no event is pending.
|
|
Packit |
b1f7ae |
* Returns a positive integer if an event is pending or in process.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static inline int pt_blk_fetch_event(struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (decoder->process_event)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!(decoder->status & pts_event_pending))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_qry_event(&decoder->query, &decoder->event,
|
|
Packit |
b1f7ae |
sizeof(decoder->event));
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->process_event = 1;
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static inline int pt_blk_block_is_empty(const struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!block)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return !block->ninsn;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static inline int block_to_user(struct pt_block *ublock, size_t size,
|
|
Packit |
b1f7ae |
const struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!ublock || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (ublock == block)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Zero out any unknown bytes. */
|
|
Packit |
b1f7ae |
if (sizeof(*block) < size) {
|
|
Packit |
b1f7ae |
memset(ublock + sizeof(*block), 0, size - sizeof(*block));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
size = sizeof(*block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memcpy(ublock, block, size);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static int pt_insn_false(const struct pt_insn *insn,
|
|
Packit |
b1f7ae |
const struct pt_insn_ext *iext)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
(void) insn;
|
|
Packit |
b1f7ae |
(void) iext;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Determine the next IP using trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Tries to determine the IP of the next instruction using trace and provides it
|
|
Packit |
b1f7ae |
* in @pip.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Not requiring trace to determine the IP is treated as an internal error.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Does not update the return compression stack for indirect calls. This is
|
|
Packit |
b1f7ae |
* expected to have been done, already, when trying to determine the next IP
|
|
Packit |
b1f7ae |
* without using trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Does not update @decoder->status. The caller is expected to do that.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
|
|
Packit |
b1f7ae |
* code otherwise.
|
|
Packit |
b1f7ae |
* Returns -pte_internal if @pip, @decoder, @insn, or @iext are NULL.
|
|
Packit |
b1f7ae |
* Returns -pte_internal if no trace is required.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_next_ip(uint64_t *pip, struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
const struct pt_insn *insn,
|
|
Packit |
b1f7ae |
const struct pt_insn_ext *iext)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!pip || !decoder || !insn || !iext)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We handle non-taken conditional branches, and compressed returns
|
|
Packit |
b1f7ae |
* directly in the switch.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* All kinds of branches are handled below the switch.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
switch (insn->iclass) {
|
|
Packit |
b1f7ae |
case ptic_cond_jump: {
|
|
Packit |
b1f7ae |
uint64_t ip;
|
|
Packit |
b1f7ae |
int taken;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_qry_cond_branch(&decoder->query, &taken);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
ip = insn->ip + insn->size;
|
|
Packit |
b1f7ae |
if (taken)
|
|
Packit |
b1f7ae |
ip += iext->variant.branch.displacement;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
*pip = ip;
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_return: {
|
|
Packit |
b1f7ae |
int taken, errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Check for a compressed return. */
|
|
Packit |
b1f7ae |
status = pt_qry_cond_branch(&decoder->query, &taken);
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* A compressed return is indicated by a taken conditional
|
|
Packit |
b1f7ae |
* branch.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!taken)
|
|
Packit |
b1f7ae |
return -pte_bad_retcomp;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_retstack_pop(&decoder->retstack, pip);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_jump:
|
|
Packit |
b1f7ae |
case ptic_call:
|
|
Packit |
b1f7ae |
/* A direct jump or call wouldn't require trace. */
|
|
Packit |
b1f7ae |
if (iext->variant.branch.is_direct)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_far_call:
|
|
Packit |
b1f7ae |
case ptic_far_return:
|
|
Packit |
b1f7ae |
case ptic_far_jump:
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_other:
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_error:
|
|
Packit |
b1f7ae |
return -pte_bad_insn;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process an indirect branch.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This covers indirect jumps and calls, non-compressed returns, and all
|
|
Packit |
b1f7ae |
* flavors of far transfers.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_qry_indirect_branch(&decoder->query, pip);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process an enabled event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Determines whether the enabled event can be processed in this iteration or
|
|
Packit |
b1f7ae |
* has to be postponed.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event has been processed.
|
|
Packit |
b1f7ae |
* Returns zero if the event shall be postponed.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_enabled(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This event can't be a status update. */
|
|
Packit |
b1f7ae |
if (ev->status_update)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We must have an IP in order to start decoding. */
|
|
Packit |
b1f7ae |
if (ev->ip_suppressed)
|
|
Packit |
b1f7ae |
return -pte_noip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We must currently be disabled. */
|
|
Packit |
b1f7ae |
if (decoder->enabled)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Delay processing of the event if the block is alredy in progress. */
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Check if we resumed from a preceding disable or if we enabled at a
|
|
Packit |
b1f7ae |
* different position.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (ev->variant.enabled.ip == decoder->ip && !block->enabled)
|
|
Packit |
b1f7ae |
block->resumed = 1;
|
|
Packit |
b1f7ae |
else {
|
|
Packit |
b1f7ae |
block->enabled = 1;
|
|
Packit |
b1f7ae |
block->resumed = 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Clear an indication of a preceding disable. */
|
|
Packit |
b1f7ae |
block->disabled = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
block->ip = decoder->ip = ev->variant.enabled.ip;
|
|
Packit |
b1f7ae |
decoder->enabled = 1;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply a disabled event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_disabled(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This event can't be a status update. */
|
|
Packit |
b1f7ae |
if (ev->status_update)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We must currently be enabled. */
|
|
Packit |
b1f7ae |
if (!decoder->enabled)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We preserve @decoder->ip. This is where we expect tracing to resume
|
|
Packit |
b1f7ae |
* and we'll indicate that on the subsequent enabled event if tracing
|
|
Packit |
b1f7ae |
* actually does resume from there.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->enabled = 0;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
block->disabled = 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process a disabled event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We reached the location of a disabled event. This ends a non-empty block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We may see disabled events for empty blocks when we have a series of enables
|
|
Packit |
b1f7ae |
* and disabled on the same IP without any trace in between. We ignore the
|
|
Packit |
b1f7ae |
* disabled event in this case and proceed.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event has been processed.
|
|
Packit |
b1f7ae |
* Returns zero if the event shall be postponed.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_disabled(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_apply_disabled(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The event completes a non-empty block. */
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Ignore the disable if the block is empty. */
|
|
Packit |
b1f7ae |
block->disabled = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply an asynchronous branch event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_async_branch(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This event can't be a status update. */
|
|
Packit |
b1f7ae |
if (ev->status_update)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We must currently be enabled. */
|
|
Packit |
b1f7ae |
if (!decoder->enabled)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Indicate the async branch as an interrupt. This ends the block. */
|
|
Packit |
b1f7ae |
block->interrupted = 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Jump to the branch destination. We will continue from there in the
|
|
Packit |
b1f7ae |
* next iteration.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->ip = ev->variant.async_branch.to;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process an asynchronous branch event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We reached the source location of an asynchronous branch. This ends a
|
|
Packit |
b1f7ae |
* non-empty block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We may come across an asynchronous branch for an empty block, e.g. when
|
|
Packit |
b1f7ae |
* tracing just started. We ignore the event in that case and proceed. It will
|
|
Packit |
b1f7ae |
* look like tracing started at the asynchronous branch destination instead of
|
|
Packit |
b1f7ae |
* at its source.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event has been processed.
|
|
Packit |
b1f7ae |
* Returns zero if the event shall be postponed.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_async_branch(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_apply_async_branch(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We may still change the start IP for an empty block. Do not indicate
|
|
Packit |
b1f7ae |
* the interrupt in this case.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->interrupted = 0;
|
|
Packit |
b1f7ae |
block->ip = decoder->ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply a paging event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_paging(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
(void) block;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->asid.cr3 = ev->variant.paging.cr3;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply a vmcs event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_vmcs(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
(void) block;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->asid.vmcs = ev->variant.vmcs.base;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process an overflow event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* An overflow ends a non-empty block. The overflow itself is indicated in the
|
|
Packit |
b1f7ae |
* next block. Indicate the overflow and resume in this case.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event has been processed.
|
|
Packit |
b1f7ae |
* Returns zero if the event shall be postponed.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_overflow(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This event can't be a status update. */
|
|
Packit |
b1f7ae |
if (ev->status_update)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The overflow ends a non-empty block. We will process the event in
|
|
Packit |
b1f7ae |
* the next iteration.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If the IP is suppressed, the overflow resolved while tracing was
|
|
Packit |
b1f7ae |
* disabled. Otherwise it resolved while tracing was enabled.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (ev->ip_suppressed) {
|
|
Packit |
b1f7ae |
/* Tracing is disabled. It doesn't make sense to preserve the
|
|
Packit |
b1f7ae |
* previous IP. This will just be misleading. Even if tracing
|
|
Packit |
b1f7ae |
* had been disabled before, as well, we might have missed the
|
|
Packit |
b1f7ae |
* re-enable in the overflow.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->enabled = 0;
|
|
Packit |
b1f7ae |
decoder->ip = 0ull;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Indicate the overflow. Since tracing is disabled, the block
|
|
Packit |
b1f7ae |
* will remain empty until tracing gets re-enabled again.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* When the block is eventually returned it will have the resync
|
|
Packit |
b1f7ae |
* and the enabled bit set to indicate the the overflow resolved
|
|
Packit |
b1f7ae |
* before tracing was enabled.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->resynced = 1;
|
|
Packit |
b1f7ae |
} else {
|
|
Packit |
b1f7ae |
/* Tracing is enabled and we're at the IP at which the overflow
|
|
Packit |
b1f7ae |
* resolved.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->enabled = 1;
|
|
Packit |
b1f7ae |
decoder->ip = ev->variant.overflow.ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Indicate the overflow and set the start IP. The block is
|
|
Packit |
b1f7ae |
* empty so we may still change it.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We do not indicate a tracing enable if tracing had been
|
|
Packit |
b1f7ae |
* disabled before to distinguish this from the above case.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->resynced = 1;
|
|
Packit |
b1f7ae |
block->ip = decoder->ip;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We don't know the TSX state. Let's assume we execute normally.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We also don't know the execution mode. Let's keep what we have
|
|
Packit |
b1f7ae |
* in case we don't get an update before we have to decode the next
|
|
Packit |
b1f7ae |
* instruction.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->speculative = 0;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply an exec mode event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_exec_mode(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
enum pt_exec_mode mode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Use status update events to diagnose inconsistencies. */
|
|
Packit |
b1f7ae |
mode = ev->variant.exec_mode.mode;
|
|
Packit |
b1f7ae |
if (ev->status_update && decoder->enabled &&
|
|
Packit |
b1f7ae |
decoder->mode != ptem_unknown && decoder->mode != mode)
|
|
Packit |
b1f7ae |
return -pte_bad_status_update;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->mode = mode;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process an exec mode event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We reached the location of an exec mode event. Update the exec mode and
|
|
Packit |
b1f7ae |
* proceed.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event has been processed.
|
|
Packit |
b1f7ae |
* Returns zero if the event shall be postponed.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_exec_mode(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_apply_exec_mode(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* An execution mode change ends a non-empty block. */
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We may still change the execution mode of an empty block. */
|
|
Packit |
b1f7ae |
block->mode = decoder->mode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply a tsx event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_tsx(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->speculative = ev->variant.tsx.speculative;
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (decoder->enabled && !pt_blk_block_is_empty(block)) {
|
|
Packit |
b1f7ae |
if (ev->variant.tsx.aborted)
|
|
Packit |
b1f7ae |
block->aborted = 1;
|
|
Packit |
b1f7ae |
else if (block->speculative && !ev->variant.tsx.speculative)
|
|
Packit |
b1f7ae |
block->committed = 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process a tsx event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We reached the location of a tsx event. A speculation mode change ends a
|
|
Packit |
b1f7ae |
* non-empty block. Indicate commit or abort in the ended block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We might see tsx event while tracing is disabled or for empty blocks, e.g. if
|
|
Packit |
b1f7ae |
* tracing was just enabled. In this case we do not indicate the abort or
|
|
Packit |
b1f7ae |
* commit.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event has been processed.
|
|
Packit |
b1f7ae |
* Returns zero if the event shall be postponed.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_tsx(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_apply_tsx(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* A speculation mode change ends a non-empty block. */
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We may still change the speculation mode of an empty block. */
|
|
Packit |
b1f7ae |
block->speculative = decoder->speculative;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply a stop event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This is used for proceed events and for trailing events.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_apply_stop(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This event can't be a status update. */
|
|
Packit |
b1f7ae |
if (ev->status_update)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Tracing is always disabled before it is stopped. */
|
|
Packit |
b1f7ae |
if (decoder->enabled)
|
|
Packit |
b1f7ae |
return -pte_bad_context;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->process_event = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Indicate the stop. */
|
|
Packit |
b1f7ae |
block->stopped = 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next IP using trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We failed to proceed without trace. This ends the current block. Now use
|
|
Packit |
b1f7ae |
* trace to do one final step to determine the start IP of the next block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_with_trace(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
const struct pt_insn *insn,
|
|
Packit |
b1f7ae |
const struct pt_insn_ext *iext)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_next_ip(&decoder->ip, decoder, insn, iext);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Preserve the query decoder's response which indicates upcoming
|
|
Packit |
b1f7ae |
* events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We do need an IP in order to proceed. */
|
|
Packit |
b1f7ae |
if (status & pts_ip_suppressed)
|
|
Packit |
b1f7ae |
return -pte_noip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Decode one instruction in a known section.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Decode the instruction at @insn->ip in @section loaded at @laddr assuming
|
|
Packit |
b1f7ae |
* execution mode @insn->mode.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_decode_in_section(struct pt_insn *insn,
|
|
Packit |
b1f7ae |
struct pt_insn_ext *iext,
|
|
Packit |
b1f7ae |
const struct pt_section *section,
|
|
Packit |
b1f7ae |
uint64_t laddr)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!insn || !iext)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We know that @ip is contained in @section.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Note that we need to translate @ip into a section offset.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_section_read(section, insn->raw, sizeof(insn->raw),
|
|
Packit |
b1f7ae |
insn->ip - laddr);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We initialize @insn->size to the maximal possible size. It will be
|
|
Packit |
b1f7ae |
* set to the actual size during instruction decode.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
insn->size = (uint8_t) status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_ild_decode(insn, iext);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Update the return-address stack if @insn is a near call.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static inline int pt_blk_log_call(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
const struct pt_insn *insn,
|
|
Packit |
b1f7ae |
const struct pt_insn_ext *iext)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !insn || !iext)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (insn->iclass != ptic_call)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Ignore direct calls to the next instruction that are used for
|
|
Packit |
b1f7ae |
* position independent code.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (iext->variant.branch.is_direct &&
|
|
Packit |
b1f7ae |
!iext->variant.branch.displacement)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_retstack_push(&decoder->retstack, insn->ip + insn->size);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed by one instruction.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Tries to decode the instruction at @decoder->ip and, on success, adds it to
|
|
Packit |
b1f7ae |
* @block and provides it in @pinsn and @piext.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* The instruction will not be added if:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - the memory could not be read: return error
|
|
Packit |
b1f7ae |
* - it could not be decoded: return error
|
|
Packit |
b1f7ae |
* - @block is already full: return zero
|
|
Packit |
b1f7ae |
* - @block would switch sections: return zero
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the instruction was added.
|
|
Packit |
b1f7ae |
* Returns zero if the instruction didn't fit into @block.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_one_insn(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
struct pt_insn *pinsn,
|
|
Packit |
b1f7ae |
struct pt_insn_ext *piext)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
uint16_t ninsn;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block || !pinsn || !piext)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* There's nothing to do if there is no room in @block. */
|
|
Packit |
b1f7ae |
ninsn = block->ninsn + 1;
|
|
Packit |
b1f7ae |
if (!ninsn)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The truncated instruction must be last. */
|
|
Packit |
b1f7ae |
if (block->truncated)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&insn, 0, sizeof(insn));
|
|
Packit |
b1f7ae |
memset(&iext, 0, sizeof(iext));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = decoder->mode;
|
|
Packit |
b1f7ae |
insn.ip = decoder->ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We do not switch sections inside a block. */
|
|
Packit |
b1f7ae |
if (insn.isid != block->isid) {
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
block->isid = insn.isid;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we couldn't read @insn's memory in one chunk from @insn.isid, we
|
|
Packit |
b1f7ae |
* provide the memory in @block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (insn.truncated) {
|
|
Packit |
b1f7ae |
memcpy(block->raw, insn.raw, insn.size);
|
|
Packit |
b1f7ae |
block->size = insn.size;
|
|
Packit |
b1f7ae |
block->truncated = 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Log calls' return addresses for return compression. */
|
|
Packit |
b1f7ae |
status = pt_blk_log_call(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We have a new instruction. */
|
|
Packit |
b1f7ae |
block->iclass = insn.iclass;
|
|
Packit |
b1f7ae |
block->end_ip = insn.ip;
|
|
Packit |
b1f7ae |
block->ninsn = ninsn;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
*pinsn = insn;
|
|
Packit |
b1f7ae |
*piext = iext;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to a particular type of instruction without using trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Proceed until we reach an instruction for which @predicate returns a positive
|
|
Packit |
b1f7ae |
* integer or until:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - @predicate returns an error: return error
|
|
Packit |
b1f7ae |
* - @block is full: return zero
|
|
Packit |
b1f7ae |
* - @block would switch sections: return zero
|
|
Packit |
b1f7ae |
* - we would need trace: return -pte_bad_query
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Provide the last instruction that was reached in @insn and @iext.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Update @decoder->ip to point to the last IP that was reached. If we fail due
|
|
Packit |
b1f7ae |
* to lack of trace or if we reach a desired instruction, this is @insn->ip;
|
|
Packit |
b1f7ae |
* otherwise this is the next instruction's IP.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if a suitable instruction was reached.
|
|
Packit |
b1f7ae |
* Returns zero if no such instruction was reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_insn(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
struct pt_insn *insn,
|
|
Packit |
b1f7ae |
struct pt_insn_ext *iext,
|
|
Packit |
b1f7ae |
int (*predicate)(const struct pt_insn *,
|
|
Packit |
b1f7ae |
const struct pt_insn_ext *))
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !insn || !predicate)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
for (;;) {
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We're done if this instruction matches the spec (positive
|
|
Packit |
b1f7ae |
* status) or we run into an error (negative status).
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = predicate(insn, iext);
|
|
Packit |
b1f7ae |
if (status != 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's see if we can proceed to the next IP without trace. */
|
|
Packit |
b1f7ae |
status = pt_insn_next_ip(&decoder->ip, insn, iext);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* End the block if the user asked us to.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We only need to take care about direct near calls. Indirect
|
|
Packit |
b1f7ae |
* and far calls require trace and will naturally end a block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (decoder->flags.variant.block.end_on_call &&
|
|
Packit |
b1f7ae |
(insn->iclass == ptic_call))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to a particular IP without using trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Proceed until we reach @ip or until:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - @block is full: return zero
|
|
Packit |
b1f7ae |
* - @block would switch sections: return zero
|
|
Packit |
b1f7ae |
* - we would need trace: return -pte_bad_query
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Provide the last instruction that was reached in @insn and @iext. If we
|
|
Packit |
b1f7ae |
* reached @ip, this is the instruction preceding it.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Update @decoder->ip to point to the last IP that was reached. If we fail due
|
|
Packit |
b1f7ae |
* to lack of trace, this is @insn->ip; otherwise this is the next instruction's
|
|
Packit |
b1f7ae |
* IP.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if @ip was reached.
|
|
Packit |
b1f7ae |
* Returns zero if no such instruction was reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_ip(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block, struct pt_insn *insn,
|
|
Packit |
b1f7ae |
struct pt_insn_ext *iext, uint64_t ip)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !insn)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
for (;;) {
|
|
Packit |
b1f7ae |
/* We're done when we reach @ip. We may not even have to decode
|
|
Packit |
b1f7ae |
* a single instruction in some cases.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (decoder->ip == ip)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's see if we can proceed to the next IP without trace. */
|
|
Packit |
b1f7ae |
status = pt_insn_next_ip(&decoder->ip, insn, iext);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* End the block if the user asked us to.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We only need to take care about direct near calls. Indirect
|
|
Packit |
b1f7ae |
* and far calls require trace and will naturally end a block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (decoder->flags.variant.block.end_on_call &&
|
|
Packit |
b1f7ae |
(insn->iclass == ptic_call))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to a particular IP with trace, if necessary.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Proceed until we reach @ip or until:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - @block is full: return zero
|
|
Packit |
b1f7ae |
* - @block would switch sections: return zero
|
|
Packit |
b1f7ae |
* - we need trace: return zero
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Update @decoder->ip to point to the last IP that was reached.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* A return of zero ends @block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if @ip was reached.
|
|
Packit |
b1f7ae |
* Returns zero if no such instruction was reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_ip_with_trace(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
uint64_t ip)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Try to reach @ip without trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We're also OK if @block overflowed or we switched sections and we
|
|
Packit |
b1f7ae |
* have to try again in the next iteration.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext, ip);
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Needing trace is not an error. We use trace to determine the next
|
|
Packit |
b1f7ae |
* start IP and end the block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the event location for a disabled event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We have a (synchronous) disabled event pending. Proceed to the event
|
|
Packit |
b1f7ae |
* location and indicate whether we were able to reach it.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* The last instruction that was reached is stored in @insn/@iext.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event location was reached.
|
|
Packit |
b1f7ae |
* Returns zero if the event location was not reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_disabled(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
struct pt_insn *insn,
|
|
Packit |
b1f7ae |
struct pt_insn_ext *iext,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (ev->ip_suppressed) {
|
|
Packit |
b1f7ae |
/* A synchronous disabled event also binds to far branches and
|
|
Packit |
b1f7ae |
* CPL-changing instructions. Both would require trace,
|
|
Packit |
b1f7ae |
* however, and are thus implicitly handled by erroring out.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* The would-require-trace error is handled by our caller.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_blk_proceed_to_insn(decoder, block, insn, iext,
|
|
Packit |
b1f7ae |
pt_insn_changes_cr3);
|
|
Packit |
b1f7ae |
} else
|
|
Packit |
b1f7ae |
return pt_blk_proceed_to_ip(decoder, block, insn, iext,
|
|
Packit |
b1f7ae |
ev->variant.disabled.ip);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the event location for an async paging event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We have an async paging event pending. Proceed to the event location and
|
|
Packit |
b1f7ae |
* indicate whether we were able to reach it. Needing trace in order to proceed
|
|
Packit |
b1f7ae |
* is not an error in this case but ends the block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event location was reached.
|
|
Packit |
b1f7ae |
* Returns zero if the event location was not reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_async_paging(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply the event immediately if we don't have an IP. */
|
|
Packit |
b1f7ae |
if (ev->ip_suppressed)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_to_ip_with_trace(decoder, block,
|
|
Packit |
b1f7ae |
ev->variant.async_paging.ip);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the event location for an async vmcs event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We have an async vmcs event pending. Proceed to the event location and
|
|
Packit |
b1f7ae |
* indicate whether we were able to reach it. Needing trace in order to proceed
|
|
Packit |
b1f7ae |
* is not an error in this case but ends the block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event location was reached.
|
|
Packit |
b1f7ae |
* Returns zero if the event location was not reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_async_vmcs(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply the event immediately if we don't have an IP. */
|
|
Packit |
b1f7ae |
if (ev->ip_suppressed)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_to_ip_with_trace(decoder, block,
|
|
Packit |
b1f7ae |
ev->variant.async_vmcs.ip);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the event location for an exec mode event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We have an exec mode event pending. Proceed to the event location and
|
|
Packit |
b1f7ae |
* indicate whether we were able to reach it. Needing trace in order to proceed
|
|
Packit |
b1f7ae |
* is not an error in this case but ends the block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event location was reached.
|
|
Packit |
b1f7ae |
* Returns zero if the event location was not reached.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_to_exec_mode(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Apply the event immediately if we don't have an IP. */
|
|
Packit |
b1f7ae |
if (ev->ip_suppressed)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_to_ip_with_trace(decoder, block,
|
|
Packit |
b1f7ae |
ev->variant.exec_mode.ip);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Try to work around erratum SKD022.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we get an asynchronous disable on VMLAUNCH or VMRESUME, the FUP that
|
|
Packit |
b1f7ae |
* caused the disable to be asynchronous might have been bogous.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the erratum has been handled.
|
|
Packit |
b1f7ae |
* Returns zero if the erratum does not apply.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_handle_erratum_skd022(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = decoder->mode;
|
|
Packit |
b1f7ae |
insn.ip = ev->variant.async_disabled.at;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
switch (iext.iclass) {
|
|
Packit |
b1f7ae |
default:
|
|
Packit |
b1f7ae |
/* The erratum does not apply. */
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case PTI_INST_VMLAUNCH:
|
|
Packit |
b1f7ae |
case PTI_INST_VMRESUME:
|
|
Packit |
b1f7ae |
/* The erratum may apply. We can't be sure without a lot more
|
|
Packit |
b1f7ae |
* analysis. Let's assume it does.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We turn the async disable into a sync disable. Our caller
|
|
Packit |
b1f7ae |
* will restart event processing.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
ev->type = ptev_disabled;
|
|
Packit |
b1f7ae |
ev->variant.disabled.ip = ev->variant.async_disabled.ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We have an event pending. Proceed to the event location and either process
|
|
Packit |
b1f7ae |
* the event and continue or postpone the event to the next block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* On our way to the event location we may also be forced to postpone the event
|
|
Packit |
b1f7ae |
* to the next block, e.g. if we overflow the number of instructions in the
|
|
Packit |
b1f7ae |
* block or if we need trace in order to reach the event location.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_event(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
for (;;) {
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
struct pt_event *ev;
|
|
Packit |
b1f7ae |
uint64_t ip;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder->process_event)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
ev = &decoder->event;
|
|
Packit |
b1f7ae |
switch (ev->type) {
|
|
Packit |
b1f7ae |
case ptev_enabled:
|
|
Packit |
b1f7ae |
status = pt_blk_process_enabled(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_disabled:
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_disabled(decoder, block,
|
|
Packit |
b1f7ae |
&insn, &iext, ev);
|
|
Packit |
b1f7ae |
if (status <= 0) {
|
|
Packit |
b1f7ae |
/* A synchronous disable event also binds to the
|
|
Packit |
b1f7ae |
* next indirect or conditional branch, i.e. to
|
|
Packit |
b1f7ae |
* any branch that would have required trace.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The @decoder->ip still points to the indirect
|
|
Packit |
b1f7ae |
* or conditional branch instruction that caused
|
|
Packit |
b1f7ae |
* us to error out. That's not where we expect
|
|
Packit |
b1f7ae |
* tracing to resume since the instruction
|
|
Packit |
b1f7ae |
* already retired.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* For calls, a fair assumption is that tracing
|
|
Packit |
b1f7ae |
* resumes after returning from the called
|
|
Packit |
b1f7ae |
* function. For other types of instructions,
|
|
Packit |
b1f7ae |
* we simply don't know.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
switch (insn.iclass) {
|
|
Packit |
b1f7ae |
case ptic_call:
|
|
Packit |
b1f7ae |
case ptic_far_call:
|
|
Packit |
b1f7ae |
decoder->ip = insn.ip + insn.size;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
default:
|
|
Packit |
b1f7ae |
decoder->ip = 0ull;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_process_disabled(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_disabled:
|
|
Packit |
b1f7ae |
ip = ev->variant.async_disabled.at;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_ip(decoder, block, &insn,
|
|
Packit |
b1f7ae |
&iext, ip);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (decoder->query.config.errata.skd022) {
|
|
Packit |
b1f7ae |
status = pt_blk_handle_erratum_skd022(decoder,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status != 0) {
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If the erratum hits, we modify the
|
|
Packit |
b1f7ae |
* event. Try again.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_process_disabled(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_branch:
|
|
Packit |
b1f7ae |
ip = ev->variant.async_branch.from;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_ip(decoder, block, &insn,
|
|
Packit |
b1f7ae |
&iext, ip);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_process_async_branch(decoder, block,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_paging:
|
|
Packit |
b1f7ae |
if (!decoder->enabled) {
|
|
Packit |
b1f7ae |
status = pt_blk_apply_paging(decoder, block,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_insn(decoder, block, &insn,
|
|
Packit |
b1f7ae |
&iext,
|
|
Packit |
b1f7ae |
pt_insn_binds_to_pip);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_paging(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We accounted for @insn in @block but we have not
|
|
Packit |
b1f7ae |
* updated @decoder->ip, yet. Let's do so now.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we can't, we have to proceed with trace. This
|
|
Packit |
b1f7ae |
* ends event processing.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_insn_next_ip(&decoder->ip, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn,
|
|
Packit |
b1f7ae |
&iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_paging:
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_async_paging(decoder, block,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_paging(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_vmcs:
|
|
Packit |
b1f7ae |
if (!decoder->enabled) {
|
|
Packit |
b1f7ae |
status = pt_blk_apply_vmcs(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_insn(decoder, block, &insn,
|
|
Packit |
b1f7ae |
&iext,
|
|
Packit |
b1f7ae |
pt_insn_binds_to_vmcs);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_vmcs(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We accounted for @insn in @block but we have not
|
|
Packit |
b1f7ae |
* updated @decoder->ip, yet. Let's do so now.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we can't, we have to proceed with trace. This
|
|
Packit |
b1f7ae |
* ends event processing.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_insn_next_ip(&decoder->ip, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn,
|
|
Packit |
b1f7ae |
&iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_vmcs:
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_async_vmcs(decoder, block,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_vmcs(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_overflow:
|
|
Packit |
b1f7ae |
status = pt_blk_process_overflow(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_exec_mode:
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_exec_mode(decoder, block,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_process_exec_mode(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_tsx:
|
|
Packit |
b1f7ae |
if (!ev->ip_suppressed) {
|
|
Packit |
b1f7ae |
ip = ev->variant.tsx.ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_ip(decoder, block,
|
|
Packit |
b1f7ae |
&insn, &iext, ip);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_process_tsx(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_stop:
|
|
Packit |
b1f7ae |
status = pt_blk_apply_stop(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We should have processed the event. If we have not, we might
|
|
Packit |
b1f7ae |
* spin here forever.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (decoder->process_event)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Check if we have more events pending. */
|
|
Packit |
b1f7ae |
status = pt_blk_fetch_event(decoder);
|
|
Packit |
b1f7ae |
if (status <= 0) {
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next decision point without using the block cache.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Tracing is enabled and we don't have an event pending. Proceed as far as
|
|
Packit |
b1f7ae |
* we get without trace. Stop when we either:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - need trace in order to continue
|
|
Packit |
b1f7ae |
* - overflow the max number of instructions in a block
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We actually proceed one instruction further to get the start IP for the next
|
|
Packit |
b1f7ae |
* block. This only updates @decoder's internal state, though.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_no_event_uncached(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This is overly conservative, really. We shouldn't get a bad-query
|
|
Packit |
b1f7ae |
* status unless we decoded at least one instruction successfully.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
memset(&insn, 0, sizeof(insn));
|
|
Packit |
b1f7ae |
memset(&iext, 0, sizeof(iext));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed as far as we get without trace. */
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
|
|
Packit |
b1f7ae |
pt_insn_false);
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Check if @ip is contained in @section loaded at @laddr.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns non-zero if it is.
|
|
Packit |
b1f7ae |
* Returns zero if it isn't or of @section is NULL.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static inline int pt_blk_is_in_section(uint64_t ip,
|
|
Packit |
b1f7ae |
const struct pt_section *section,
|
|
Packit |
b1f7ae |
uint64_t laddr)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
uint64_t begin, end;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
begin = laddr;
|
|
Packit |
b1f7ae |
end = begin + pt_section_size(section);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return (begin <= ip && ip < end);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Insert a trampoline block cache entry.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Add a trampoline block cache entry at @ip to continue at @nip, where @nip
|
|
Packit |
b1f7ae |
* must be the next instruction after @ip.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Both @ip and @nip must be section-relative
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static inline int pt_blk_add_trampoline(struct pt_block_cache *bcache,
|
|
Packit |
b1f7ae |
uint64_t ip, uint64_t nip,
|
|
Packit |
b1f7ae |
enum pt_exec_mode mode)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_bcache_entry bce;
|
|
Packit |
b1f7ae |
int64_t disp;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The displacement from @ip to @nip for the trampoline. */
|
|
Packit |
b1f7ae |
disp = (int64_t) (nip - ip);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&bce, 0, sizeof(bce));
|
|
Packit |
b1f7ae |
bce.displacement = (int32_t) disp;
|
|
Packit |
b1f7ae |
bce.ninsn = 1;
|
|
Packit |
b1f7ae |
bce.mode = mode;
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_again;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we can't reach @nip without overflowing the displacement field, we
|
|
Packit |
b1f7ae |
* have to stop and re-decode the instruction at @ip.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if ((int64_t) bce.displacement != disp) {
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&bce, 0, sizeof(bce));
|
|
Packit |
b1f7ae |
bce.ninsn = 1;
|
|
Packit |
b1f7ae |
bce.mode = mode;
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_decode;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_bcache_add(bcache, ip, bce);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
enum {
|
|
Packit |
b1f7ae |
/* The maximum number of steps when filling the block cache. */
|
|
Packit |
b1f7ae |
bcache_fill_steps = 0x400
|
|
Packit |
b1f7ae |
};
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next instruction and fill the block cache for @decoder->ip.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Tracing is enabled and we don't have an event pending. The current IP is not
|
|
Packit |
b1f7ae |
* yet cached.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Proceed one instruction without using the block cache, then try to proceed
|
|
Packit |
b1f7ae |
* further using the block cache.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* On our way back, add a block cache entry for the IP before proceeding. Note
|
|
Packit |
b1f7ae |
* that the recursion is bounded by @steps and ultimately by the maximum number
|
|
Packit |
b1f7ae |
* of instructions in a block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_no_event_fill_cache(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
struct pt_block_cache *bcache,
|
|
Packit |
b1f7ae |
struct pt_section *section,
|
|
Packit |
b1f7ae |
uint64_t laddr, size_t steps)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_bcache_entry bce;
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
uint64_t nip, dip;
|
|
Packit |
b1f7ae |
int64_t disp;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !steps)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed one instruction by decoding and examining it.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Note that we also return on a status of zero that indicates that the
|
|
Packit |
b1f7ae |
* instruction didn't fit into @block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_one_insn(decoder, block, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status <= 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's see if we can proceed to the next IP without trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we can't, this is certainly a decision point.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_insn_next_ip(&decoder->ip, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&bce, 0, sizeof(bce));
|
|
Packit |
b1f7ae |
bce.ninsn = 1;
|
|
Packit |
b1f7ae |
bce.mode = insn.mode;
|
|
Packit |
b1f7ae |
bce.isize = insn.size;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Clear the instruction size in case of overflows. */
|
|
Packit |
b1f7ae |
if ((uint8_t) bce.isize != insn.size)
|
|
Packit |
b1f7ae |
bce.isize = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
switch (insn.iclass) {
|
|
Packit |
b1f7ae |
case ptic_error:
|
|
Packit |
b1f7ae |
case ptic_other:
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_jump:
|
|
Packit |
b1f7ae |
/* A direct jump doesn't require trace. */
|
|
Packit |
b1f7ae |
if (iext.variant.branch.is_direct)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_indirect;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_call:
|
|
Packit |
b1f7ae |
/* A direct call doesn't require trace. */
|
|
Packit |
b1f7ae |
if (iext.variant.branch.is_direct)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_ind_call;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_return:
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_return;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_cond_jump:
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_cond;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptic_far_call:
|
|
Packit |
b1f7ae |
case ptic_far_return:
|
|
Packit |
b1f7ae |
case ptic_far_jump:
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_indirect;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If the block was truncated, we have to decode its last
|
|
Packit |
b1f7ae |
* instruction each time.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We could have skipped the above switch and size assignment in
|
|
Packit |
b1f7ae |
* this case but this is already a slow and hopefully infrequent
|
|
Packit |
b1f7ae |
* path.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (block->truncated)
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_decode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_bcache_add(bcache, insn.ip - laddr, bce);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The next instruction's IP. */
|
|
Packit |
b1f7ae |
nip = decoder->ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Even if we were able to proceed without trace, we might have to stop
|
|
Packit |
b1f7ae |
* here for various reasons:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - at near direct calls to update the return-address stack
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We are forced to re-decode @insn to get the branch displacement.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Even though it is constant, we don't cache it to avoid increasing
|
|
Packit |
b1f7ae |
* the size of a cache entry. Note that the displacement field is
|
|
Packit |
b1f7ae |
* zero for this entry and we might be tempted to use it - but other
|
|
Packit |
b1f7ae |
* entries that point to this decision point will have non-zero
|
|
Packit |
b1f7ae |
* displacement.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We could proceed after a near direct call but we migh as well
|
|
Packit |
b1f7ae |
* postpone it to the next iteration. Make sure to end the block if
|
|
Packit |
b1f7ae |
* @decoder->flags.variant.block.end_on_call is set, though.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - if we switched sections
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This ends a block just like a branch that requires trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We need to re-decode @insn in order to determine the start IP of
|
|
Packit |
b1f7ae |
* the next block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - if the block is truncated
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We need to read the last instruction's memory from multiple
|
|
Packit |
b1f7ae |
* sections and provide it to the user.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We could still use the block cache but then we'd have to handle
|
|
Packit |
b1f7ae |
* this case for each qualifier. Truncation is hopefully rare and
|
|
Packit |
b1f7ae |
* having to read the memory for the instruction from multiple
|
|
Packit |
b1f7ae |
* sections is already slow. Let's rather keep things simple and
|
|
Packit |
b1f7ae |
* route it through the decode flow, where we already have
|
|
Packit |
b1f7ae |
* everything in place.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (insn.iclass == ptic_call ||
|
|
Packit |
b1f7ae |
!pt_blk_is_in_section(nip, section, laddr) || block->truncated) {
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&bce, 0, sizeof(bce));
|
|
Packit |
b1f7ae |
bce.ninsn = 1;
|
|
Packit |
b1f7ae |
bce.mode = insn.mode;
|
|
Packit |
b1f7ae |
bce.qualifier = ptbq_decode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_bcache_add(bcache, insn.ip - laddr, bce);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We proceeded one instruction. Let's see if we have a cache entry for
|
|
Packit |
b1f7ae |
* the next instruction.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_bcache_lookup(&bce, bcache, nip - laddr);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we don't have a valid cache entry, yet, fill the cache some more.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* On our way back, we add a cache entry for this instruction based on
|
|
Packit |
b1f7ae |
* the cache entry of the succeeding instruction.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!pt_bce_is_valid(bce)) {
|
|
Packit |
b1f7ae |
/* If we exceeded the maximum number of allowed steps, we insert
|
|
Packit |
b1f7ae |
* a trampoline to the next instruction.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* The next time we encounter the same code, we will use the
|
|
Packit |
b1f7ae |
* trampoline to jump directly to where we left off this time
|
|
Packit |
b1f7ae |
* and continue from there.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
steps -= 1;
|
|
Packit |
b1f7ae |
if (!steps)
|
|
Packit |
b1f7ae |
return pt_blk_add_trampoline(bcache, insn.ip - laddr,
|
|
Packit |
b1f7ae |
nip - laddr, insn.mode);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_proceed_no_event_fill_cache(decoder, block,
|
|
Packit |
b1f7ae |
bcache, section,
|
|
Packit |
b1f7ae |
laddr, steps);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's see if we have more luck this time. */
|
|
Packit |
b1f7ae |
status = pt_bcache_lookup(&bce, bcache, nip - laddr);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we still don't have a valid cache entry, we're done. Most
|
|
Packit |
b1f7ae |
* likely, @block overflowed and we couldn't proceed past the
|
|
Packit |
b1f7ae |
* next instruction.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!pt_bce_is_valid(bce))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We must not have switched execution modes.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This would require an event and we're on the no-event flow.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (pt_bce_exec_mode(bce) != insn.mode)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The decision point IP and the displacement from @insn.ip. */
|
|
Packit |
b1f7ae |
dip = nip + bce.displacement;
|
|
Packit |
b1f7ae |
disp = (int64_t) (dip - insn.ip);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We must not have switched sections between @nip and @dip since the
|
|
Packit |
b1f7ae |
* cache entry at @nip brought us to @dip.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!pt_blk_is_in_section(dip, section, laddr))
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's try to reach @nip's decision point from @insn.ip.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* There are two fields that may overflow: @bce.ninsn and
|
|
Packit |
b1f7ae |
* @bce.displacement.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
bce.ninsn += 1;
|
|
Packit |
b1f7ae |
bce.displacement = (int32_t) disp;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If none of them overflowed, we're done.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If one or both overflowed, let's try to insert a trampoline, i.e. we
|
|
Packit |
b1f7ae |
* try to reach @dip via a ptbq_again entry to @nip.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!bce.ninsn || ((int64_t) bce.displacement != disp))
|
|
Packit |
b1f7ae |
return pt_blk_add_trampoline(bcache, insn.ip - laddr,
|
|
Packit |
b1f7ae |
nip - laddr, insn.mode);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We're done. Add the cache entry.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* There's a chance that other decoders updated the cache entry in the
|
|
Packit |
b1f7ae |
* meantime. They should have come to the same conclusion as we,
|
|
Packit |
b1f7ae |
* though, and the cache entries should be identical.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Cache updates are atomic so even if the two versions were not
|
|
Packit |
b1f7ae |
* identical, we wouldn't care because they are both correct.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_bcache_add(bcache, insn.ip - laddr, bce);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed at a potentially truncated instruction.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We were not able to decode the instruction at @decoder->ip in @decoder's
|
|
Packit |
b1f7ae |
* cached section. This is typically caused by not having enough bytes.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Try to decode the instruction again using the entire image. If this succeeds
|
|
Packit |
b1f7ae |
* we expect to end up with an instruction that was truncated in the section it
|
|
Packit |
b1f7ae |
* started. We provide the full instruction in this case and end the block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_truncated(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&iext, 0, sizeof(iext));
|
|
Packit |
b1f7ae |
memset(&insn, 0, sizeof(insn));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = decoder->mode;
|
|
Packit |
b1f7ae |
insn.ip = decoder->ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We shouldn't use this function if the instruction isn't truncated. */
|
|
Packit |
b1f7ae |
if (!insn.truncated)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Provide the instruction in the block. This ends the block. */
|
|
Packit |
b1f7ae |
memcpy(block->raw, insn.raw, insn.size);
|
|
Packit |
b1f7ae |
block->iclass = insn.iclass;
|
|
Packit |
b1f7ae |
block->size = insn.size;
|
|
Packit |
b1f7ae |
block->truncated = 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Log calls' return addresses for return compression. */
|
|
Packit |
b1f7ae |
errcode = pt_blk_log_call(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's see if we can proceed to the next IP without trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* The truncated instruction ends the block but we still need to get the
|
|
Packit |
b1f7ae |
* next block's start IP.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
errcode = pt_insn_next_ip(&decoder->ip, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (errcode < 0) {
|
|
Packit |
b1f7ae |
if (errcode != -pte_bad_query)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next decision point using the block cache.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Tracing is enabled and we don't have an event pending. We already set
|
|
Packit |
b1f7ae |
* @block's isid. All reads are done within @section as we're not switching
|
|
Packit |
b1f7ae |
* sections between blocks.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Proceed as far as we get without trace. Stop when we either:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - need trace in order to continue
|
|
Packit |
b1f7ae |
* - overflow the max number of instructions in a block
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We actually proceed one instruction further to get the start IP for the next
|
|
Packit |
b1f7ae |
* block. This only updates @decoder's internal state, though.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_no_event_cached(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
struct pt_block_cache *bcache,
|
|
Packit |
b1f7ae |
struct pt_section *section,
|
|
Packit |
b1f7ae |
uint64_t laddr)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_bcache_entry bce;
|
|
Packit |
b1f7ae |
uint16_t binsn, ninsn;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_bcache_lookup(&bce, bcache, decoder->ip - laddr);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we don't find a valid cache entry, fill the cache. */
|
|
Packit |
b1f7ae |
if (!pt_bce_is_valid(bce))
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_fill_cache(decoder, block,
|
|
Packit |
b1f7ae |
bcache, section,
|
|
Packit |
b1f7ae |
laddr,
|
|
Packit |
b1f7ae |
bcache_fill_steps);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We have a valid cache entry. Let's first check if the way to the
|
|
Packit |
b1f7ae |
* decision point still fits into @block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If it doesn't, we end the block without filling it as much as we
|
|
Packit |
b1f7ae |
* could since this would require us to switch to the slow path.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* On the next iteration, we will start with an empty block, which is
|
|
Packit |
b1f7ae |
* guaranteed to have enough room for at least one block cache entry.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
binsn = block->ninsn;
|
|
Packit |
b1f7ae |
ninsn = binsn + (uint16_t) bce.ninsn;
|
|
Packit |
b1f7ae |
if (ninsn < binsn)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Jump ahead to the decision point and proceed from there.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We're not switching execution modes so even if @block already has an
|
|
Packit |
b1f7ae |
* execution mode, it will be the one we're going to set.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->ip += bce.displacement;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We don't know the instruction class so we should be setting it to
|
|
Packit |
b1f7ae |
* ptic_error. Since we will be able to fill it back in later in most
|
|
Packit |
b1f7ae |
* cases, we move the clearing to the switch cases that don't.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->end_ip = decoder->ip;
|
|
Packit |
b1f7ae |
block->ninsn = ninsn;
|
|
Packit |
b1f7ae |
block->mode = pt_bce_exec_mode(bce);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
switch (pt_bce_qualifier(bce)) {
|
|
Packit |
b1f7ae |
case ptbq_again:
|
|
Packit |
b1f7ae |
/* We're not able to reach the actual decision point due to
|
|
Packit |
b1f7ae |
* overflows so we inserted a trampoline.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We don't know the instruction and it is not guaranteed that
|
|
Packit |
b1f7ae |
* we will proceed further (e.g. if @block overflowed). Let's
|
|
Packit |
b1f7ae |
* clear any previously stored instruction class which has
|
|
Packit |
b1f7ae |
* become invalid when we updated @block->ninsn.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->iclass = ptic_error;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_cached(decoder, block, bcache,
|
|
Packit |
b1f7ae |
section, laddr);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptbq_cond:
|
|
Packit |
b1f7ae |
/* We're at a conditional branch. */
|
|
Packit |
b1f7ae |
block->iclass = ptic_cond_jump;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's first check whether we know the size of the
|
|
Packit |
b1f7ae |
* instruction. If we do, we might get away without decoding
|
|
Packit |
b1f7ae |
* the instruction.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we don't know the size we might as well do the full decode
|
|
Packit |
b1f7ae |
* and proceed-with-trace flow we do for ptbq_decode.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (bce.isize) {
|
|
Packit |
b1f7ae |
uint64_t ip;
|
|
Packit |
b1f7ae |
int taken;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If the branch is not taken, we don't need to decode
|
|
Packit |
b1f7ae |
* the instruction at @decoder->ip.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If it is taken, we have to implement everything here.
|
|
Packit |
b1f7ae |
* We can't use the normal decode and proceed-with-trace
|
|
Packit |
b1f7ae |
* flow since we already consumed the TNT bit.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_qry_cond_branch(&decoder->query, &taken);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Preserve the query decoder's response which indicates
|
|
Packit |
b1f7ae |
* upcoming events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
ip = decoder->ip;
|
|
Packit |
b1f7ae |
if (taken) {
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&iext, 0, sizeof(iext));
|
|
Packit |
b1f7ae |
memset(&insn, 0, sizeof(insn));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = pt_bce_exec_mode(bce);
|
|
Packit |
b1f7ae |
insn.ip = ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_decode_in_section(&insn, &iext,
|
|
Packit |
b1f7ae |
section,
|
|
Packit |
b1f7ae |
laddr);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
ip += iext.variant.branch.displacement;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
decoder->ip = ip + bce.isize;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Fall through to ptbq_decode. */
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptbq_decode: {
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We need to decode the instruction at @decoder->ip and decide
|
|
Packit |
b1f7ae |
* what to do based on that.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We already accounted for the instruction so we can't just
|
|
Packit |
b1f7ae |
* call pt_blk_proceed_one_insn().
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&iext, 0, sizeof(iext));
|
|
Packit |
b1f7ae |
memset(&insn, 0, sizeof(insn));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = pt_bce_exec_mode(bce);
|
|
Packit |
b1f7ae |
insn.ip = decoder->ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_decode_in_section(&insn, &iext, section, laddr);
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_insn)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_truncated(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We just decoded @insn so we know the instruction class. */
|
|
Packit |
b1f7ae |
block->iclass = insn.iclass;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Log calls' return addresses for return compression. */
|
|
Packit |
b1f7ae |
status = pt_blk_log_call(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's see if we can proceed to the next IP without trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Note that we also stop due to displacement overflows or to
|
|
Packit |
b1f7ae |
* maintain the return-address stack for near direct calls.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_insn_next_ip(&decoder->ip, &insn, &iext;;
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We can't, so let's proceed with trace, which
|
|
Packit |
b1f7ae |
* completes the block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_blk_proceed_with_trace(decoder, &insn, &iext;;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* End the block if the user asked us to.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We only need to take care about direct near calls. Indirect
|
|
Packit |
b1f7ae |
* and far calls require trace and will naturally end a block.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (decoder->flags.variant.block.end_on_call &&
|
|
Packit |
b1f7ae |
(insn.iclass == ptic_call))
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we can proceed without trace and we stay in @section we
|
|
Packit |
b1f7ae |
* may proceed further.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We're done if we switch sections, though.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!pt_blk_is_in_section(decoder->ip, section, laddr))
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_cached(decoder, block, bcache,
|
|
Packit |
b1f7ae |
section, laddr);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptbq_ind_call: {
|
|
Packit |
b1f7ae |
uint64_t ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We're at a near indirect call. */
|
|
Packit |
b1f7ae |
block->iclass = ptic_call;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We need to update the return-address stack and query the
|
|
Packit |
b1f7ae |
* destination IP.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
ip = decoder->ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we already know the size of the instruction, we don't need
|
|
Packit |
b1f7ae |
* to re-decode it.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (bce.isize)
|
|
Packit |
b1f7ae |
ip += bce.isize;
|
|
Packit |
b1f7ae |
else {
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
memset(&iext, 0, sizeof(iext));
|
|
Packit |
b1f7ae |
memset(&insn, 0, sizeof(insn));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = pt_bce_exec_mode(bce);
|
|
Packit |
b1f7ae |
insn.ip = ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_decode_in_section(&insn, &iext, section,
|
|
Packit |
b1f7ae |
laddr);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
ip += insn.size;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_retstack_push(&decoder->retstack, ip);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_qry_indirect_branch(&decoder->query, &decoder->ip);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Preserve the query decoder's response which indicates
|
|
Packit |
b1f7ae |
* upcoming events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptbq_return: {
|
|
Packit |
b1f7ae |
int taken;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We're at a near return. */
|
|
Packit |
b1f7ae |
block->iclass = ptic_return;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Check for a compressed return. */
|
|
Packit |
b1f7ae |
status = pt_qry_cond_branch(&decoder->query, &taken);
|
|
Packit |
b1f7ae |
if (status < 0) {
|
|
Packit |
b1f7ae |
if (status != -pte_bad_query)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* The return is not compressed. We need another query
|
|
Packit |
b1f7ae |
* to determine the destination IP.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_qry_indirect_branch(&decoder->query,
|
|
Packit |
b1f7ae |
&decoder->ip);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Preserve the query decoder's response which indicates
|
|
Packit |
b1f7ae |
* upcoming events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Preserve the query decoder's response which indicates
|
|
Packit |
b1f7ae |
* upcoming events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* A compressed return is indicated by a taken conditional
|
|
Packit |
b1f7ae |
* branch.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!taken)
|
|
Packit |
b1f7ae |
return -pte_bad_retcomp;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_retstack_pop(&decoder->retstack, &decoder->ip);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptbq_indirect:
|
|
Packit |
b1f7ae |
/* We're at an indirect jump or far transfer.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We don't know the exact instruction class and there's no
|
|
Packit |
b1f7ae |
* reason to decode the instruction for any other purpose.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Indicate that we don't know the instruction class and leave
|
|
Packit |
b1f7ae |
* it to our caller to decode the instruction if needed.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->iclass = ptic_error;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This is neither a near call nor return so we don't need to
|
|
Packit |
b1f7ae |
* touch the return-address stack.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Just query the destination IP.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_qry_indirect_branch(&decoder->query, &decoder->ip);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Preserve the query decoder's response which indicates
|
|
Packit |
b1f7ae |
* upcoming events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->status = status;
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next decision point - try using the cache.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Tracing is enabled and we don't have an event pending. Proceed as far as
|
|
Packit |
b1f7ae |
* we get without trace. Stop when we either:
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* - need trace in order to continue
|
|
Packit |
b1f7ae |
* - overflow the max number of instructions in a block
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We actually proceed one instruction further to get the start IP for the next
|
|
Packit |
b1f7ae |
* block. This only updates @decoder's internal state, though.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_no_event_trycache(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_block_cache *bcache;
|
|
Packit |
b1f7ae |
struct pt_section *section;
|
|
Packit |
b1f7ae |
uint64_t laddr;
|
|
Packit |
b1f7ae |
int isid, errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
isid = pt_blk_cached_section(&decoder->scache, §ion, &laddr,
|
|
Packit |
b1f7ae |
decoder->image, &decoder->asid,
|
|
Packit |
b1f7ae |
decoder->ip);
|
|
Packit |
b1f7ae |
if (isid < 0) {
|
|
Packit |
b1f7ae |
if (isid != -pte_nomap)
|
|
Packit |
b1f7ae |
return isid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_scache_invalidate(&decoder->scache);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
isid = pt_image_find(decoder->image, §ion, &laddr,
|
|
Packit |
b1f7ae |
&decoder->asid, decoder->ip);
|
|
Packit |
b1f7ae |
if (isid < 0) {
|
|
Packit |
b1f7ae |
if (isid != -pte_nomap)
|
|
Packit |
b1f7ae |
return isid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Even if there is no such section in the image, we may
|
|
Packit |
b1f7ae |
* still read the memory via the callback function.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_uncached(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_section_map(section);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
goto out_put;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = pt_blk_cache_section(&decoder->scache, section, laddr,
|
|
Packit |
b1f7ae |
isid);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
goto out_unmap;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We do not switch sections inside a block. */
|
|
Packit |
b1f7ae |
if (isid != block->isid) {
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
block->isid = isid;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
bcache = pt_section_bcache(section);
|
|
Packit |
b1f7ae |
if (!bcache)
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_uncached(decoder, block);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_cached(decoder, block, bcache, section,
|
|
Packit |
b1f7ae |
laddr);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
out_unmap:
|
|
Packit |
b1f7ae |
(void) pt_section_unmap(section);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
out_put:
|
|
Packit |
b1f7ae |
(void) pt_section_put(section);
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next decision point.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We don't have an event pending. Ensure that tracing is enabled and proceed
|
|
Packit |
b1f7ae |
* as far as we get. Try using the cache, if possible.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed_no_event(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
/* The end of the trace ends a non-empty block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we're called again, we will proceed until we really need trace.
|
|
Packit |
b1f7ae |
* For example, if tracing is currently disabled.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (decoder->status & pts_eos) {
|
|
Packit |
b1f7ae |
if (!pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder->enabled)
|
|
Packit |
b1f7ae |
return -pte_eos;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If tracing is disabled and we have still trace left but no event,
|
|
Packit |
b1f7ae |
* something is wrong.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (!decoder->enabled)
|
|
Packit |
b1f7ae |
return -pte_no_enable;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event_trycache(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed to the next event or decision point.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns zero on success, a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_proceed(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int event_pending;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
event_pending = pt_blk_fetch_event(decoder);
|
|
Packit |
b1f7ae |
if (event_pending != 0) {
|
|
Packit |
b1f7ae |
if (event_pending < 0)
|
|
Packit |
b1f7ae |
return event_pending;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_event(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_proceed_no_event(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
static int pt_blk_status(const struct pt_block_decoder *decoder)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int status, flags;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = decoder->status;
|
|
Packit |
b1f7ae |
flags = 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Forward end-of-trace indications.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Postpone it as long as we're still processing events, though.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if ((status & pts_eos) && !decoder->process_event)
|
|
Packit |
b1f7ae |
flags |= pts_eos;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return flags;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
enum {
|
|
Packit |
b1f7ae |
/* The maximum number of steps to take when determining whether the
|
|
Packit |
b1f7ae |
* event location can be reached.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
bdm64_max_steps = 0x100
|
|
Packit |
b1f7ae |
};
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Try to work around erratum BDM64.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we got a transaction abort immediately following a branch that produced
|
|
Packit |
b1f7ae |
* trace, the trace for that branch might have been corrupted.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the erratum was handled.
|
|
Packit |
b1f7ae |
* Returns zero if the erratum does not seem to apply.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_handle_erratum_bdm64(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
const struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_insn_ext iext;
|
|
Packit |
b1f7ae |
struct pt_insn insn;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This only affects aborts. */
|
|
Packit |
b1f7ae |
if (!ev->variant.tsx.aborted)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* This only affects branches that require trace.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If the erratum hits, that branch ended the current block and brought
|
|
Packit |
b1f7ae |
* us to the trailing event flow.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
if (pt_blk_block_is_empty(block))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
insn.mode = block->mode;
|
|
Packit |
b1f7ae |
insn.ip = block->end_ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!pt_insn_is_branch(&insn, &iext))
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Let's check if we can reach the event location from here.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* If we can, let's assume the erratum did not hit. We might still be
|
|
Packit |
b1f7ae |
* wrong but we're not able to tell.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
status = pt_insn_range_is_contiguous(decoder->ip, ev->variant.tsx.ip,
|
|
Packit |
b1f7ae |
decoder->mode, decoder->image,
|
|
Packit |
b1f7ae |
&decoder->asid, bdm64_max_steps);
|
|
Packit |
b1f7ae |
if (status > 0)
|
|
Packit |
b1f7ae |
return 0;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We can't reach the event location. This could either mean that we
|
|
Packit |
b1f7ae |
* stopped too early (and status is zero) or that the erratum hit.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We assume the latter and pretend that the previous branch brought us
|
|
Packit |
b1f7ae |
* to the event location, instead.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
decoder->ip = ev->variant.tsx.ip;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Handle a trailing TSX event.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This involves handling erratum BDM64.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a positive integer if the event is to be postponed.
|
|
Packit |
b1f7ae |
* Returns zero if the event was handled successfully.
|
|
Packit |
b1f7ae |
* Returns a negative error code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static inline int pt_blk_handle_trailing_tsx(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block,
|
|
Packit |
b1f7ae |
const struct pt_event *ev)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder || !ev)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!ev->ip_suppressed) {
|
|
Packit |
b1f7ae |
if (decoder->query.config.errata.bdm64) {
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_handle_erratum_bdm64(decoder, block,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (decoder->ip != ev->variant.tsx.ip)
|
|
Packit |
b1f7ae |
return 1;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_apply_tsx(decoder, block, ev);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Process events that bind to the current decoder IP.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* We filled a block and proceeded to the next IP, which will become the start
|
|
Packit |
b1f7ae |
* IP of the next block. Process any pending events that bind to that IP so we
|
|
Packit |
b1f7ae |
* can indicate their effect in the current block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
|
|
Packit |
b1f7ae |
* code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_process_trailing_events(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
if (!decoder)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
for (;;) {
|
|
Packit |
b1f7ae |
struct pt_event *ev;
|
|
Packit |
b1f7ae |
int status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_fetch_event(decoder);
|
|
Packit |
b1f7ae |
if (status <= 0) {
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
ev = &decoder->event;
|
|
Packit |
b1f7ae |
switch (ev->type) {
|
|
Packit |
b1f7ae |
case ptev_enabled:
|
|
Packit |
b1f7ae |
case ptev_disabled:
|
|
Packit |
b1f7ae |
case ptev_paging:
|
|
Packit |
b1f7ae |
case ptev_vmcs:
|
|
Packit |
b1f7ae |
case ptev_overflow:
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_disabled:
|
|
Packit |
b1f7ae |
if (decoder->ip != ev->variant.async_disabled.at)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (decoder->query.config.errata.skd022) {
|
|
Packit |
b1f7ae |
status = pt_blk_handle_erratum_skd022(decoder,
|
|
Packit |
b1f7ae |
ev);
|
|
Packit |
b1f7ae |
if (status != 0) {
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_disabled(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_branch:
|
|
Packit |
b1f7ae |
if (decoder->ip != ev->variant.async_branch.from)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_async_branch(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_paging:
|
|
Packit |
b1f7ae |
if (!ev->ip_suppressed &&
|
|
Packit |
b1f7ae |
decoder->ip != ev->variant.async_paging.ip)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_paging(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_async_vmcs:
|
|
Packit |
b1f7ae |
if (!ev->ip_suppressed &&
|
|
Packit |
b1f7ae |
decoder->ip != ev->variant.async_vmcs.ip)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_vmcs(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_exec_mode:
|
|
Packit |
b1f7ae |
if (!ev->ip_suppressed &&
|
|
Packit |
b1f7ae |
decoder->ip != ev->variant.exec_mode.ip)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_apply_exec_mode(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_tsx:
|
|
Packit |
b1f7ae |
status = pt_blk_handle_trailing_tsx(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (status > 0)
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
case ptev_stop:
|
|
Packit |
b1f7ae |
status = pt_blk_apply_stop(decoder, block, ev);
|
|
Packit |
b1f7ae |
if (status < 0)
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
continue;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* If we fall out of the switch, we're done. */
|
|
Packit |
b1f7ae |
break;
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return pt_blk_status(decoder);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Collect one block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Fill a new, empty block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Returns a non-negative pt_status_flag bit-vector on success, a negative error
|
|
Packit |
b1f7ae |
* code otherwise.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
static int pt_blk_collect(struct pt_block_decoder *decoder,
|
|
Packit |
b1f7ae |
struct pt_block *block)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
int errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !block)
|
|
Packit |
b1f7ae |
return -pte_internal;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Zero-initialize the block in case of error returns. */
|
|
Packit |
b1f7ae |
memset(block, 0, sizeof(*block));
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Fill in a few things from the current decode state.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This reflects the state of the last pt_blk_next() or pt_blk_start()
|
|
Packit |
b1f7ae |
* call. Note that, unless we stop with tracing disabled, we proceed
|
|
Packit |
b1f7ae |
* already to the start IP of the next block.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* Some of the state may later be overwritten as we process events.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
block->ip = decoder->ip;
|
|
Packit |
b1f7ae |
block->mode = decoder->mode;
|
|
Packit |
b1f7ae |
if (decoder->speculative)
|
|
Packit |
b1f7ae |
block->speculative = 1;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* Proceed one block. */
|
|
Packit |
b1f7ae |
errcode = pt_blk_proceed(decoder, block);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
/* We may still have events left that trigger on the current IP.
|
|
Packit |
b1f7ae |
*
|
|
Packit |
b1f7ae |
* This IP lies outside of @block but events typically bind to the IP of
|
|
Packit |
b1f7ae |
* the last instruction that did not retire.
|
|
Packit |
b1f7ae |
*/
|
|
Packit |
b1f7ae |
return pt_blk_process_trailing_events(decoder, block);
|
|
Packit |
b1f7ae |
}
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
int pt_blk_next(struct pt_block_decoder *decoder, struct pt_block *ublock,
|
|
Packit |
b1f7ae |
size_t size)
|
|
Packit |
b1f7ae |
{
|
|
Packit |
b1f7ae |
struct pt_block block, *pblock;
|
|
Packit |
b1f7ae |
int errcode, status;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
if (!decoder || !ublock)
|
|
Packit |
b1f7ae |
return -pte_invalid;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
pblock = size == sizeof(block) ? ublock : █
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
status = pt_blk_collect(decoder, pblock);
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
errcode = block_to_user(ublock, size, pblock);
|
|
Packit |
b1f7ae |
if (errcode < 0)
|
|
Packit |
b1f7ae |
return errcode;
|
|
Packit |
b1f7ae |
|
|
Packit |
b1f7ae |
return status;
|
|
Packit |
b1f7ae |
}
|