diff options
Diffstat (limited to 'libipt/src/pt_packet.c')
-rw-r--r-- | libipt/src/pt_packet.c | 573 |
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; +} |