mirror of
https://github.com/opnsense/src.git
synced 2026-06-08 00:02:14 -04:00
Its latest version merged from: ^/vendor/processor-trace/892e12c5a27bda5806d1e63269986bb4171b5a8b Sponsored by: DARPA, AFRL
573 lines
12 KiB
C
573 lines
12 KiB
C
/*
|
|
* Copyright (c) 2013-2019, Intel Corporation
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "pt_packet.h"
|
|
#include "pt_opcodes.h"
|
|
|
|
#include "intel-pt.h"
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
static uint64_t pt_pkt_read_value(const uint8_t *pos, int size)
|
|
{
|
|
uint64_t val;
|
|
int idx;
|
|
|
|
for (val = 0, idx = 0; idx < size; ++idx) {
|
|
uint64_t byte = *pos++;
|
|
|
|
byte <<= (idx * 8);
|
|
val |= byte;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
int pt_pkt_read_unknown(struct pt_packet *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
int (*decode)(struct pt_packet_unknown *, const struct pt_config *,
|
|
const uint8_t *, void *);
|
|
int size;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
decode = config->decode.callback;
|
|
if (!decode)
|
|
return -pte_bad_opc;
|
|
|
|
/* Fill in some default values. */
|
|
packet->payload.unknown.packet = pos;
|
|
packet->payload.unknown.priv = NULL;
|
|
|
|
/* We accept a size of zero to allow the callback to modify the
|
|
* trace buffer and resume normal decoding.
|
|
*/
|
|
size = (*decode)(&packet->payload.unknown, config, pos,
|
|
config->decode.context);
|
|
if (size < 0)
|
|
return size;
|
|
|
|
if (size > UCHAR_MAX)
|
|
return -pte_invalid;
|
|
|
|
packet->type = ppt_unknown;
|
|
packet->size = (uint8_t) size;
|
|
|
|
if (config->end < pos + size)
|
|
return -pte_eos;
|
|
|
|
return size;
|
|
}
|
|
|
|
int pt_pkt_read_psb(const uint8_t *pos, const struct pt_config *config)
|
|
{
|
|
int count;
|
|
|
|
if (!pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_psb)
|
|
return -pte_eos;
|
|
|
|
pos += pt_opcs_psb;
|
|
|
|
for (count = 0; count < pt_psb_repeat_count; ++count) {
|
|
if (*pos++ != pt_psb_hi)
|
|
return -pte_bad_packet;
|
|
if (*pos++ != pt_psb_lo)
|
|
return -pte_bad_packet;
|
|
}
|
|
|
|
return ptps_psb;
|
|
}
|
|
|
|
static int pt_pkt_ip_size(enum pt_ip_compression ipc)
|
|
{
|
|
switch (ipc) {
|
|
case pt_ipc_suppressed:
|
|
return 0;
|
|
|
|
case pt_ipc_update_16:
|
|
return 2;
|
|
|
|
case pt_ipc_update_32:
|
|
return 4;
|
|
|
|
case pt_ipc_update_48:
|
|
case pt_ipc_sext_48:
|
|
return 6;
|
|
|
|
case pt_ipc_full:
|
|
return 8;
|
|
}
|
|
|
|
return -pte_bad_packet;
|
|
}
|
|
|
|
int pt_pkt_read_ip(struct pt_packet_ip *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint64_t ip;
|
|
uint8_t ipc;
|
|
int ipsize;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
ipc = (*pos++ >> pt_opm_ipc_shr) & pt_opm_ipc_shr_mask;
|
|
|
|
ip = 0ull;
|
|
ipsize = pt_pkt_ip_size((enum pt_ip_compression) ipc);
|
|
if (ipsize < 0)
|
|
return ipsize;
|
|
|
|
if (config->end < pos + ipsize)
|
|
return -pte_eos;
|
|
|
|
if (ipsize)
|
|
ip = pt_pkt_read_value(pos, ipsize);
|
|
|
|
packet->ipc = (enum pt_ip_compression) ipc;
|
|
packet->ip = ip;
|
|
|
|
return ipsize + 1;
|
|
}
|
|
|
|
static uint8_t pt_pkt_tnt_bit_size(uint64_t payload)
|
|
{
|
|
uint8_t size;
|
|
|
|
/* The payload bit-size is the bit-index of the payload's stop-bit,
|
|
* which itself is not part of the payload proper.
|
|
*/
|
|
for (size = 0; ; size += 1) {
|
|
payload >>= 1;
|
|
if (!payload)
|
|
break;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int pt_pkt_read_tnt(struct pt_packet_tnt *packet, uint64_t payload)
|
|
{
|
|
uint8_t bit_size;
|
|
|
|
if (!packet)
|
|
return -pte_internal;
|
|
|
|
bit_size = pt_pkt_tnt_bit_size(payload);
|
|
if (!bit_size)
|
|
return -pte_bad_packet;
|
|
|
|
/* Remove the stop bit from the payload. */
|
|
payload &= ~(1ull << bit_size);
|
|
|
|
packet->payload = payload;
|
|
packet->bit_size = bit_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pt_pkt_read_tnt_8(struct pt_packet_tnt *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
int errcode;
|
|
|
|
(void) config;
|
|
|
|
if (!pos)
|
|
return -pte_internal;
|
|
|
|
errcode = pt_pkt_read_tnt(packet, pos[0] >> pt_opm_tnt_8_shr);
|
|
if (errcode < 0)
|
|
return errcode;
|
|
|
|
return ptps_tnt_8;
|
|
}
|
|
|
|
int pt_pkt_read_tnt_64(struct pt_packet_tnt *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint64_t payload;
|
|
int errcode;
|
|
|
|
if (!pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_tnt_64)
|
|
return -pte_eos;
|
|
|
|
payload = pt_pkt_read_value(pos + pt_opcs_tnt_64, pt_pl_tnt_64_size);
|
|
|
|
errcode = pt_pkt_read_tnt(packet, payload);
|
|
if (errcode < 0)
|
|
return errcode;
|
|
|
|
return ptps_tnt_64;
|
|
}
|
|
|
|
int pt_pkt_read_pip(struct pt_packet_pip *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint64_t payload;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_pip)
|
|
return -pte_eos;
|
|
|
|
/* Read the payload. */
|
|
payload = pt_pkt_read_value(pos + pt_opcs_pip, pt_pl_pip_size);
|
|
|
|
/* Extract the non-root information from the payload. */
|
|
packet->nr = payload & pt_pl_pip_nr;
|
|
|
|
/* Create the cr3 value. */
|
|
payload >>= pt_pl_pip_shr;
|
|
payload <<= pt_pl_pip_shl;
|
|
packet->cr3 = payload;
|
|
|
|
return ptps_pip;
|
|
}
|
|
|
|
static int pt_pkt_read_mode_exec(struct pt_packet_mode_exec *packet,
|
|
uint8_t mode)
|
|
{
|
|
if (!packet)
|
|
return -pte_internal;
|
|
|
|
packet->csl = (mode & pt_mob_exec_csl) != 0;
|
|
packet->csd = (mode & pt_mob_exec_csd) != 0;
|
|
|
|
return ptps_mode;
|
|
}
|
|
|
|
static int pt_pkt_read_mode_tsx(struct pt_packet_mode_tsx *packet,
|
|
uint8_t mode)
|
|
{
|
|
if (!packet)
|
|
return -pte_internal;
|
|
|
|
packet->intx = (mode & pt_mob_tsx_intx) != 0;
|
|
packet->abrt = (mode & pt_mob_tsx_abrt) != 0;
|
|
|
|
return ptps_mode;
|
|
}
|
|
|
|
int pt_pkt_read_mode(struct pt_packet_mode *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint8_t payload, mode, leaf;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_mode)
|
|
return -pte_eos;
|
|
|
|
payload = pos[pt_opcs_mode];
|
|
leaf = payload & pt_mom_leaf;
|
|
mode = payload & pt_mom_bits;
|
|
|
|
packet->leaf = (enum pt_mode_leaf) leaf;
|
|
switch (leaf) {
|
|
default:
|
|
return -pte_bad_packet;
|
|
|
|
case pt_mol_exec:
|
|
return pt_pkt_read_mode_exec(&packet->bits.exec, mode);
|
|
|
|
case pt_mol_tsx:
|
|
return pt_pkt_read_mode_tsx(&packet->bits.tsx, mode);
|
|
}
|
|
}
|
|
|
|
int pt_pkt_read_tsc(struct pt_packet_tsc *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_tsc)
|
|
return -pte_eos;
|
|
|
|
packet->tsc = pt_pkt_read_value(pos + pt_opcs_tsc, pt_pl_tsc_size);
|
|
|
|
return ptps_tsc;
|
|
}
|
|
|
|
int pt_pkt_read_cbr(struct pt_packet_cbr *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_cbr)
|
|
return -pte_eos;
|
|
|
|
packet->ratio = pos[2];
|
|
|
|
return ptps_cbr;
|
|
}
|
|
|
|
int pt_pkt_read_tma(struct pt_packet_tma *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint16_t ctc, fc;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_tma)
|
|
return -pte_eos;
|
|
|
|
ctc = pos[pt_pl_tma_ctc_0];
|
|
ctc |= pos[pt_pl_tma_ctc_1] << 8;
|
|
|
|
fc = pos[pt_pl_tma_fc_0];
|
|
fc |= pos[pt_pl_tma_fc_1] << 8;
|
|
|
|
if (fc & ~pt_pl_tma_fc_mask)
|
|
return -pte_bad_packet;
|
|
|
|
packet->ctc = ctc;
|
|
packet->fc = fc;
|
|
|
|
return ptps_tma;
|
|
}
|
|
|
|
int pt_pkt_read_mtc(struct pt_packet_mtc *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_mtc)
|
|
return -pte_eos;
|
|
|
|
packet->ctc = pos[pt_opcs_mtc];
|
|
|
|
return ptps_mtc;
|
|
}
|
|
|
|
int pt_pkt_read_cyc(struct pt_packet_cyc *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
const uint8_t *begin, *end;
|
|
uint64_t value;
|
|
uint8_t cyc, ext, shl;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
begin = pos;
|
|
end = config->end;
|
|
|
|
/* The first byte contains the opcode and part of the payload.
|
|
* We already checked that this first byte is within bounds.
|
|
*/
|
|
cyc = *pos++;
|
|
|
|
ext = cyc & pt_opm_cyc_ext;
|
|
cyc >>= pt_opm_cyc_shr;
|
|
|
|
value = cyc;
|
|
shl = (8 - pt_opm_cyc_shr);
|
|
|
|
while (ext) {
|
|
uint64_t bits;
|
|
|
|
if (end <= pos)
|
|
return -pte_eos;
|
|
|
|
bits = *pos++;
|
|
ext = bits & pt_opm_cycx_ext;
|
|
|
|
bits >>= pt_opm_cycx_shr;
|
|
bits <<= shl;
|
|
|
|
shl += (8 - pt_opm_cycx_shr);
|
|
if (sizeof(value) * 8 < shl)
|
|
return -pte_bad_packet;
|
|
|
|
value |= bits;
|
|
}
|
|
|
|
packet->value = value;
|
|
|
|
return (int) (pos - begin);
|
|
}
|
|
|
|
int pt_pkt_read_vmcs(struct pt_packet_vmcs *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint64_t payload;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_vmcs)
|
|
return -pte_eos;
|
|
|
|
payload = pt_pkt_read_value(pos + pt_opcs_vmcs, pt_pl_vmcs_size);
|
|
|
|
packet->base = payload << pt_pl_vmcs_shl;
|
|
|
|
return ptps_vmcs;
|
|
}
|
|
|
|
int pt_pkt_read_mnt(struct pt_packet_mnt *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_mnt)
|
|
return -pte_eos;
|
|
|
|
packet->payload = pt_pkt_read_value(pos + pt_opcs_mnt, pt_pl_mnt_size);
|
|
|
|
return ptps_mnt;
|
|
}
|
|
|
|
int pt_pkt_read_exstop(struct pt_packet_exstop *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_exstop)
|
|
return -pte_eos;
|
|
|
|
packet->ip = pos[1] & pt_pl_exstop_ip_mask ? 1 : 0;
|
|
|
|
return ptps_exstop;
|
|
}
|
|
|
|
int pt_pkt_read_mwait(struct pt_packet_mwait *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_mwait)
|
|
return -pte_eos;
|
|
|
|
packet->hints = (uint32_t) pt_pkt_read_value(pos + pt_opcs_mwait,
|
|
pt_pl_mwait_hints_size);
|
|
packet->ext = (uint32_t) pt_pkt_read_value(pos + pt_opcs_mwait +
|
|
pt_pl_mwait_hints_size,
|
|
pt_pl_mwait_ext_size);
|
|
return ptps_mwait;
|
|
}
|
|
|
|
int pt_pkt_read_pwre(struct pt_packet_pwre *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint64_t payload;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_pwre)
|
|
return -pte_eos;
|
|
|
|
payload = pt_pkt_read_value(pos + pt_opcs_pwre, pt_pl_pwre_size);
|
|
|
|
memset(packet, 0, sizeof(*packet));
|
|
packet->state = (uint8_t) ((payload & pt_pl_pwre_state_mask) >>
|
|
pt_pl_pwre_state_shr);
|
|
packet->sub_state = (uint8_t) ((payload & pt_pl_pwre_sub_state_mask) >>
|
|
pt_pl_pwre_sub_state_shr);
|
|
if (payload & pt_pl_pwre_hw_mask)
|
|
packet->hw = 1;
|
|
|
|
return ptps_pwre;
|
|
}
|
|
|
|
int pt_pkt_read_pwrx(struct pt_packet_pwrx *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint64_t payload;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
if (config->end < pos + ptps_pwrx)
|
|
return -pte_eos;
|
|
|
|
payload = pt_pkt_read_value(pos + pt_opcs_pwrx, pt_pl_pwrx_size);
|
|
|
|
memset(packet, 0, sizeof(*packet));
|
|
packet->last = (uint8_t) ((payload & pt_pl_pwrx_last_mask) >>
|
|
pt_pl_pwrx_last_shr);
|
|
packet->deepest = (uint8_t) ((payload & pt_pl_pwrx_deepest_mask) >>
|
|
pt_pl_pwrx_deepest_shr);
|
|
if (payload & pt_pl_pwrx_wr_int)
|
|
packet->interrupt = 1;
|
|
if (payload & pt_pl_pwrx_wr_store)
|
|
packet->store = 1;
|
|
if (payload & pt_pl_pwrx_wr_hw)
|
|
packet->autonomous = 1;
|
|
|
|
return ptps_pwrx;
|
|
}
|
|
|
|
int pt_pkt_read_ptw(struct pt_packet_ptw *packet, const uint8_t *pos,
|
|
const struct pt_config *config)
|
|
{
|
|
uint8_t opc, plc;
|
|
int size;
|
|
|
|
if (!packet || !pos || !config)
|
|
return -pte_internal;
|
|
|
|
/* Skip the ext opcode. */
|
|
pos++;
|
|
|
|
opc = *pos++;
|
|
plc = (opc >> pt_opm_ptw_pb_shr) & pt_opm_ptw_pb_shr_mask;
|
|
|
|
size = pt_ptw_size(plc);
|
|
if (size < 0)
|
|
return size;
|
|
|
|
if (config->end < pos + size)
|
|
return -pte_eos;
|
|
|
|
packet->payload = pt_pkt_read_value(pos, size);
|
|
packet->plc = plc;
|
|
packet->ip = opc & pt_opm_ptw_ip ? 1 : 0;
|
|
|
|
return pt_opcs_ptw + size;
|
|
}
|