summaryrefslogtreecommitdiff
path: root/libipt/src/pt_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'libipt/src/pt_packet.c')
-rw-r--r--libipt/src/pt_packet.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/libipt/src/pt_packet.c b/libipt/src/pt_packet.c
new file mode 100644
index 0000000000000..8ec46f7db450c
--- /dev/null
+++ b/libipt/src/pt_packet.c
@@ -0,0 +1,573 @@
+/*
+ * 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;
+}