/* * 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 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; }