/* * This is modified version of Simple PT dumper by Andi Kleen * https://github.com/andikleen/simple-pt */ /* * Copyright (c) 2015, Intel Corporation * Author: Andi Kleen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. 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. * * 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 HOLDER 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. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include "defs.h" #include "map.h" #define BIT(x) (1U << (x)) typedef unsigned long long u64; static FILE *out_fp; static char psb[16] = { 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82 }; #define LEFT(x) ((end - p) >= (x)) /* Caller must have checked length */ static u64 get_ip_val(unsigned char **pp, unsigned char *end, int len, uint64_t *last_ip) { unsigned char *p = *pp; u64 v = *last_ip; int i; unsigned shift = 0; if (len == 0) { *last_ip = 0; return 0; /* out of context */ } if (len < 4) { if (!LEFT(len)) { *last_ip = 0; return 0; /* XXX error */ } for (i = 0; i < len; i++, shift += 16, p += 2) { uint64_t b = *(uint16_t *)p; v = (v & ~(0xffffULL << shift)) | (b << shift); } v = ((int64_t)(v << (64 - 48))) >> (64 - 48); /* sign extension */ } else { return 0; /* XXX error */ } *pp = p; *last_ip = v; return v; } static char *get_ip_symbol(u64 ip, char *buf, int bufsize) { struct syment *sm; ulong offset; sm = value_search(ip, &offset); if (sm) { if (!offset) sprintf(buf, "%s", sm->name); else sprintf(buf, "%s+%lu", sm->name, offset); return buf; } return NULL; } /* Caller must have checked length */ static u64 get_val(unsigned char **pp, int len) { unsigned char *p = *pp; u64 v = 0; int i; unsigned shift = 0; for (i = 0; i < len; i++, shift += 8) v |= ((uint64_t)(*p++)) << shift; *pp = p; return v; } static void print_unknown(unsigned char *p, unsigned char *end, unsigned char *map) { fprintf(out_fp, "unknown packet: "); unsigned len = end - p; int i; if (len > 16) len = 16; for (i = 0; i < len; i++) fprintf(out_fp, "%02x ", p[i]); fprintf(out_fp, "\n"); } static void print_tnt_byte(unsigned char v, int max) { int i; for (i = max - 1; i >= 0; i--) if (v & BIT(i)) fprintf(out_fp, "T"); else fprintf(out_fp, "N"); } static void print_tnt_stop(unsigned char v) { int j; for (j = 7; j >= 0; j--) { if (v & BIT(j)) break; } print_tnt_byte(v, j); } static void print_multi_tnt(unsigned char *p, int len) { int i; for (i = len - 1; i >= 0 && p[i] == 0; i--) ; if (i >= 0) { print_tnt_stop(p[i]); i--; } else { fprintf(out_fp, "??? no stop bit"); return; } for (; i >= 0; i--) print_tnt_byte(p[i], 8); } void decode_buffer(unsigned char *map, size_t len) { unsigned char *end = map + len; unsigned char *p; size_t skipped = 0; size_t overflow = 0; uint64_t last_ip = 0; u64 ip; char buf[64]; for (p = map; p < end; ) { unsigned char *prev = p; /* look for PSB */ p = memmem(p, end - p, psb, 16); if (!p) { p = end; break; } skipped += p - prev; while (p < end) { fprintf(out_fp, "%lx\t", p - map); if (*p == 2 && LEFT(2)) { if (p[1] == 0xa3 && LEFT(8)) { /* long TNT */ fprintf(out_fp, "tnt64 "); print_multi_tnt(p + 2, 6); fprintf(out_fp, "\n"); p += 8; continue; } if (p[1] == 0x43 && LEFT(8)) { /* PIP */ p += 2; fprintf(out_fp, "pip\t%llx\n", (get_val(&p, 6) >> 1) << 5); continue; } if (p[1] == 3 && LEFT(4) && p[3] == 0) { /* CBR */ fprintf(out_fp, "cbr\t%u\n", p[2]); p += 4; continue; } if (p[1] == 0b10000011) { fprintf(out_fp, "tracestop\n"); p += 2; continue; } if (p[1] == 0b11110011 && LEFT(8)) { /* OVF */ fprintf(out_fp, "ovf\n"); p += 8; overflow++; continue; } if (p[1] == 0x82 && LEFT(16) && !memcmp(p, psb, 16)) { /* PSB */ fprintf(out_fp, "psb\n"); p += 16; continue; } if (p[1] == 0b100011) { /* PSBEND */ fprintf(out_fp, "psbend\n"); p += 2; continue; } /* MNT */ if (p[1] == 0b11000011 && LEFT(11) && p[2] == 0b10001000) { fprintf(out_fp, "mnt\t%llx\n", p[3] | ((u64)p[4] << 8) | ((u64)p[5] << 16) | ((u64)p[6] << 24) | ((u64)p[7] << 32) | ((u64)p[8] << 40) | ((u64)p[9] << 48) | ((u64)p[10] << 56)); p += 10; continue; } /* TMA */ if (p[1] == 0b01110011 && LEFT(7)) { fprintf(out_fp, "tma\tctc=%u fc=%u\n", p[2] | (p[3] << 8), p[5] | ((p[6] & 1) << 8)); p += 7; continue; } /* VMCS */ if (p[1] == 0b11001000 && LEFT(7)) { fprintf(out_fp, "vmcs\t%llx\n", ((u64)p[2] << 12) | ((u64)p[3] << 20) | ((u64)p[4] << 28) | ((u64)p[5] << 36) | ((u64)p[6] << 44)); continue; } } if ((*p & BIT(0)) == 0) { if (*p == 0) { /* PAD */ fprintf(out_fp, "pad\n"); p++; continue; } fprintf(out_fp, "tnt8 "); print_tnt_stop(*p >> 1); fprintf(out_fp, "\n"); p++; continue; } char *name = NULL; switch (*p & 0x1f) { case 0xd: name = "tip"; break; case 0x11: name = "tip.pge"; break; case 0x1: name = "tip.pgd"; break; case 0x1d: name = "fup"; break; } if (name) { int ipl = *p >> 5; p++; ip = get_ip_val(&p, end, ipl, &last_ip); fprintf(out_fp, "%s\t%d: %llx", name, ipl, ip); if (get_ip_symbol(ip, buf, sizeof(buf))) fprintf(out_fp, " <%s>\n", buf); else fprintf(out_fp, "\n"); continue; } if (*p == 0x99 && LEFT(2)) { /* MODE */ if ((p[1] >> 5) == 1) { fprintf(out_fp, "mode.tsx"); if (p[1] & BIT(0)) fprintf(out_fp, " intx"); if (p[1] & BIT(1)) fprintf(out_fp, " txabort"); fprintf(out_fp, "\n"); p += 2; continue; } else if ((p[1] >> 5) == 0) { fprintf(out_fp, "mode.exec"); fprintf(out_fp, " lma=%d", (p[1] & BIT(0))); fprintf(out_fp, " cs.d=%d", !!(p[1] & BIT(1))); fprintf(out_fp, "\n"); p += 2; continue; } } if (*p == 0x19 && LEFT(8)) { /* TSC */ p++; fprintf(out_fp, "tsc\t%llu\n", get_val(&p, 7)); continue; } if (*p == 0b01011001 && LEFT(2)) { /* MTC */ fprintf(out_fp, "mtc\t%u\n", p[1]); p += 2; continue; } if ((*p & 3) == 3) { /* CYC */ u64 cyc = *p >> 2; unsigned shift = 4; if ((*p & 4) && LEFT(1)) { do { p++; cyc |= (*p >> 1) << shift; shift += 7; } while ((*p & 1) && LEFT(1)); } fprintf(out_fp, "cyc\t%llu\n", cyc); p++; continue; } print_unknown(p, end, map); break; } } if (p < end || skipped) fprintf(out_fp, "%lu bytes undecoded\n", (end - p) + skipped); if (overflow) fprintf(out_fp, "%lu overflows\n", overflow); } int fastdecode(char *in, char *out) { size_t len; unsigned char *map = mapfile(in, &len); if (!map) { fprintf(fp, "Cannot map file: %s\n", in); return FALSE; } if ((out_fp = fopen(out, "w")) == NULL) { fprintf(fp, "Cannot open file: %s\n", out); return FALSE; } decode_buffer(map, len); unmapfile(map, len); fclose(out_fp); return TRUE; }