| /* |
| * Copyright (c) 2013-2016, 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 "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; |
| } |