libipt: skd010/skd014
SKD010: Intel(R) PT FUP May be Dropped After OVF.
Some Intel PT (Intel Processor Trace) OVF (Overflow) packets may not
be followed by a FUP (Flow Update Packet) or TIP.PGE (Target IP
Packet, Packet Generation Enable).
Same as SKD014.
Change-Id: I2ce3d7c705b20697898d444e96d430fe61f5ad3f
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
diff --git a/libipt/include/intel-pt.h b/libipt/include/intel-pt.h
index 78a8a53..ffb8e5b 100644
--- a/libipt/include/intel-pt.h
+++ b/libipt/include/intel-pt.h
@@ -549,6 +549,16 @@
*/
uint32_t skd022:1;
+ /** SKD010: Intel(R) PT FUP May be Dropped After OVF.
+ *
+ * Same as: SKD014.
+ *
+ * Some Intel PT (Intel Processor Trace) OVF (Overflow) packets may not
+ * be followed by a FUP (Flow Update Packet) or TIP.PGE (Target IP
+ * Packet, Packet Generation Enable).
+ */
+ uint32_t skd010:1;
+
/* Reserve a few bytes for the future. */
uint32_t reserved[15];
};
diff --git a/libipt/src/pt_config.c b/libipt/src/pt_config.c
index 736f331..3a00766 100644
--- a/libipt/src/pt_config.c
+++ b/libipt/src/pt_config.c
@@ -59,6 +59,7 @@
errata->bdm70 = 1;
errata->skd007 = 1;
errata->skd022 = 1;
+ errata->skd010 = 1;
break;
}
break;
diff --git a/libipt/src/pt_query_decoder.c b/libipt/src/pt_query_decoder.c
index 879b8e5..c715e15 100644
--- a/libipt/src/pt_query_decoder.c
+++ b/libipt/src/pt_query_decoder.c
@@ -39,6 +39,48 @@
#include <stddef.h>
+/* Find a FUP in a PSB+ header.
+ *
+ * The packet @decoder must be synchronized onto the trace stream at the
+ * beginning or somewhere inside a PSB+ header.
+ *
+ * It uses @packet to hold trace packets during its search. If the search is
+ * successful, @packet will contain the first (and hopefully only) FUP packet in
+ * this PSB+. Otherwise, @packet may contain anything.
+ *
+ * Returns one if a FUP packet is found (@packet will contain it).
+ * Returns zero if no FUP packet is found (@packet is undefined).
+ * Returns a negative error code otherwise.
+ */
+static int pt_qry_find_header_fup(struct pt_packet *packet,
+ struct pt_packet_decoder *decoder)
+{
+ if (!packet || !decoder)
+ return -pte_internal;
+
+ for (;;) {
+ int errcode;
+
+ errcode = pt_pkt_next(decoder, packet, sizeof(*packet));
+ if (errcode < 0)
+ return errcode;
+
+ switch (packet->type) {
+ default:
+ /* Ignore the packet. */
+ break;
+
+ case ppt_psbend:
+ /* There's no FUP in here. */
+ return 0;
+
+ case ppt_fup:
+ /* Found it. */
+ return 1;
+ }
+ }
+}
+
int pt_qry_decoder_init(struct pt_query_decoder *decoder,
const struct pt_config *config)
{
@@ -1533,6 +1575,392 @@
}
}
+/* Recover from SKD010.
+ *
+ * Creates and publishes an overflow event at @packet's IP payload.
+ *
+ * Further updates @decoder as follows:
+ *
+ * - set time tracking to @time and @tcal
+ * - set the position to @offset
+ * - set ip to @packet's IP payload
+ * - set tracing to be enabled
+ *
+ * Returns 1 on success, a negative error code otherwise.
+ */
+static int skd010_recover(struct pt_query_decoder *decoder,
+ const struct pt_packet_ip *packet,
+ const struct pt_time_cal *tcal,
+ const struct pt_time *time, uint64_t offset)
+{
+ struct pt_last_ip ip;
+ struct pt_event *ev;
+ int errcode;
+
+ if (!decoder || !packet || !tcal || !time)
+ return -pte_internal;
+
+ /* We use the decoder's IP. It should be newly initialized. */
+ ip = decoder->ip;
+
+ /* Extract the IP payload from the packet. */
+ errcode = pt_last_ip_update_ip(&ip, packet, &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ /* Synthesize the overflow event. */
+ ev = pt_evq_standalone(&decoder->evq);
+ if (!ev)
+ return -pte_internal;
+
+ ev->type = ptev_overflow;
+
+ /* We do need a full IP. */
+ errcode = pt_last_ip_query(&ev->variant.overflow.ip, &ip);
+ if (errcode < 0)
+ return -pte_bad_context;
+
+ /* We continue decoding at the given offset. */
+ decoder->pos = decoder->config.begin + offset;
+
+ /* Tracing is enabled. */
+ decoder->enabled = 1;
+ decoder->ip = ip;
+
+ decoder->time = *time;
+ decoder->tcal = *tcal;
+
+ /* After updating the decoder's time, we can fill in the event
+ * timestamp.
+ */
+ pt_qry_add_event_time(ev, decoder);
+
+ /* Publish the event. */
+ decoder->event = ev;
+ return 1;
+}
+
+/* Scan ahead for a packet at which to resume after an overflow.
+ *
+ * This function is called after an OVF without a corresponding FUP. This
+ * normally means that the overflow resolved while tracing was disabled.
+ *
+ * With erratum SKD010 it might also mean that the FUP (or TIP.PGE) was dropped.
+ * The overflow thus resolved while tracing was enabled (or tracing was enabled
+ * after the overflow resolved). Search for an indication whether tracing is
+ * enabled or disabled by scanning upcoming packets.
+ *
+ * If we can confirm that tracing is disabled, the erratum does not apply and we
+ * can continue normally.
+ *
+ * If we can confirm that tracing is enabled, the erratum applies and we try to
+ * recover by synchronizing at a later packet and a different IP. If we can't
+ * recover, pretend the erratum didn't apply so we run into the error later.
+ * Since this assumes that tracing is disabled, no harm should be done, i.e. no
+ * bad trace should be generated.
+ *
+ * Returns a positive value if the overflow is handled.
+ * Returns zero if the overflow is not yet handled.
+ * Returns a negative error code otherwise.
+ */
+static int skd010_scan_for_ovf_resume(struct pt_packet_decoder *pkt,
+ struct pt_query_decoder *decoder)
+{
+ struct pt_time_cal tcal;
+ struct pt_time time;
+ struct {
+ struct pt_time_cal tcal;
+ struct pt_time time;
+ uint64_t offset;
+ } mode_tsx;
+ int errcode;
+
+ /* Keep track of time as we skip packets. */
+ time = decoder->time;
+ tcal = decoder->tcal;
+
+ /* Keep track of a potential recovery point at MODE.TSX. */
+ memset(&mode_tsx, 0, sizeof(mode_tsx));
+
+ for (;;) {
+ struct pt_packet packet;
+ uint64_t offset;
+
+ errcode = pt_pkt_get_offset(pkt, &offset);
+ if (errcode < 0)
+ return errcode;
+
+ errcode = pt_pkt_next(pkt, &packet, sizeof(packet));
+ if (errcode < 0) {
+ /* Let's assume the trace is correct if we run out
+ * of packets.
+ */
+ if (errcode == -pte_eos)
+ errcode = 0;
+
+ return errcode;
+ }
+
+ switch (packet.type) {
+ case ppt_tip_pge:
+ /* Everything is fine. There is nothing to do. */
+ return 0;
+
+ case ppt_tip_pgd:
+ /* This is a clear indication that the erratum
+ * apllies.
+ *
+ * We synchronize after the disable.
+ */
+
+ decoder->time = time;
+ decoder->tcal = tcal;
+ decoder->pos = decoder->config.begin + offset
+ + packet.size;
+
+ /* Even though the erratum applies, tracing is disabled
+ * at the time we're able to resync. We can use the
+ * normal code path.
+ */
+ return 0;
+
+ case ppt_tnt_8:
+ case ppt_tnt_64:
+ /* This is a clear indication that the erratum
+ * apllies.
+ *
+ * Yet, we can't recover from it as we wouldn't know how
+ * many TNT bits will have been used when we eventually
+ * find an IP packet at which to resume tracing.
+ */
+ return 0;
+
+ case ppt_pip:
+ case ppt_vmcs:
+ /* We could track those changes and synthesize extra
+ * events after the overflow event when recovering from
+ * the erratum. This requires infrastructure that we
+ * don't currently have, though, so we're not going to
+ * do it.
+ *
+ * Instead, we ignore those changes. We already don't
+ * know how many other changes were lost in the
+ * overflow.
+ */
+ break;
+
+ case ppt_mode:
+ switch (packet.payload.mode.leaf) {
+ case pt_mol_exec:
+ /* A MODE.EXEC packet binds to TIP, i.e.
+ *
+ * TIP.PGE: everything is fine
+ * TIP: the erratum applies
+ *
+ * In the TIP.PGE case, we may just follow the
+ * normal code flow.
+ *
+ * In the TIP case, we'd be able to re-sync at
+ * the TIP IP but have to skip packets up to and
+ * including the TIP.
+ *
+ * We'd need to synthesize the MODE.EXEC event
+ * after the overflow event when recovering at
+ * the TIP. We lack the infrastructure for this
+ * - it's getting too complicated.
+ *
+ * Instead, we ignore the execution mode change;
+ * we already don't know how many more such
+ * changes were lost in the overflow.
+ */
+ break;
+
+ case pt_mol_tsx:
+ /* A MODE.TSX packet may be standalone or bind
+ * to FUP.
+ *
+ * If this is the second MODE.TSX, we're sure
+ * that tracing is disabled and everything is
+ * fine.
+ */
+ if (mode_tsx.offset)
+ return 0;
+
+ /* If we find the FUP this packet binds to, we
+ * may recover at the FUP IP and restart
+ * processing packets from here. Remember the
+ * current state.
+ */
+ mode_tsx.offset = offset;
+ mode_tsx.time = time;
+ mode_tsx.tcal = tcal;
+
+ break;
+ }
+
+ break;
+
+ case ppt_fup:
+ /* This is a pretty good indication that tracing
+ * is indeed enabled and the erratum applies.
+ */
+
+ /* If we got a MODE.TSX packet before, we synchronize at
+ * the FUP IP but continue decoding packets starting
+ * from the MODE.TSX.
+ */
+ if (mode_tsx.offset)
+ return skd010_recover(decoder,
+ &packet.payload.ip,
+ &mode_tsx.tcal,
+ &mode_tsx.time,
+ mode_tsx.offset);
+
+ /* Without a preceding MODE.TSX, this FUP is the start
+ * of an async branch or disable. We synchronize at the
+ * FUP IP and continue decoding packets from here.
+ */
+ return skd010_recover(decoder, &packet.payload.ip,
+ &tcal, &time, offset);
+
+ case ppt_tip:
+ /* We syhchronize at the TIP IP and continue decoding
+ * packets after the TIP packet.
+ */
+ return skd010_recover(decoder, &packet.payload.ip,
+ &tcal, &time,
+ offset + packet.size);
+
+ case ppt_psb:
+ /* We reached a synchronization point. Tracing is
+ * enabled if and only if the PSB+ contains a FUP.
+ */
+ errcode = pt_qry_find_header_fup(&packet, pkt);
+ if (errcode < 0) {
+ /* If we ran out of packets, we can't tell.
+ * Let's assume the trace is correct.
+ */
+ if (errcode == -pte_eos)
+ errcode = 0;
+
+ return errcode;
+ }
+
+ /* If there is no FUP, tracing is disabled and
+ * everything is fine.
+ */
+ if (!errcode)
+ return 0;
+
+ /* We should have a FUP. */
+ if (packet.type != ppt_fup)
+ return -pte_internal;
+
+ /* Otherwise, we may synchronize at the FUP IP and
+ * continue decoding packets at the PSB.
+ */
+ return skd010_recover(decoder, &packet.payload.ip,
+ &tcal, &time, offset);
+
+ case ppt_psbend:
+ /* We shouldn't see this. */
+ return -pte_bad_context;
+
+ case ppt_ovf:
+ case ppt_stop:
+ /* It doesn't matter if it had been enabled or disabled
+ * before. We may resume normally.
+ */
+ return 0;
+
+ case ppt_unknown:
+ case ppt_invalid:
+ /* We can't skip this packet. */
+ return 0;
+
+ case ppt_pad:
+ case ppt_mnt:
+ /* Ignore this packet. */
+ break;
+
+ case ppt_tsc:
+ /* Keep track of time. */
+ errcode = pt_qry_apply_tsc(&time, &tcal,
+ &packet.payload.tsc,
+ &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ break;
+
+ case ppt_cbr:
+ /* Keep track of time. */
+ errcode = pt_qry_apply_cbr(&time, &tcal,
+ &packet.payload.cbr,
+ &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ break;
+
+ case ppt_tma:
+ /* Keep track of time. */
+ errcode = pt_qry_apply_tma(&time, &tcal,
+ &packet.payload.tma,
+ &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ break;
+
+ case ppt_mtc:
+ /* Keep track of time. */
+ errcode = pt_qry_apply_mtc(&time, &tcal,
+ &packet.payload.mtc,
+ &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ break;
+
+ case ppt_cyc:
+ /* Keep track of time. */
+ errcode = pt_qry_apply_cyc(&time, &tcal,
+ &packet.payload.cyc,
+ &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ break;
+ }
+ }
+}
+
+static int pt_qry_handle_skd010(struct pt_query_decoder *decoder)
+{
+ struct pt_packet_decoder pkt;
+ uint64_t offset;
+ int errcode;
+
+ if (!decoder)
+ return -pte_internal;
+
+ errcode = pt_qry_get_offset(decoder, &offset);
+ if (errcode < 0)
+ return errcode;
+
+ errcode = pt_pkt_decoder_init(&pkt, &decoder->config);
+ if (errcode < 0)
+ return errcode;
+
+ errcode = pt_pkt_sync_set(&pkt, offset);
+ if (errcode >= 0)
+ errcode = skd010_scan_for_ovf_resume(&pkt, decoder);
+
+ pt_pkt_decoder_fini(&pkt);
+ return errcode;
+}
+
int pt_qry_decode_ovf(struct pt_query_decoder *decoder)
{
const struct pt_decoder_function *dfun;
@@ -1591,6 +2019,23 @@
/* We set tracing to disabled in pt_qry_reset(); fix it. */
decoder->enabled = 1;
} else {
+ /* Check for erratum SKD010.
+ *
+ * The FUP may have been dropped. If we can figure out that
+ * tracing is enabled and hence the FUP is missing, we resume
+ * at a later packet and a different IP.
+ */
+ if (decoder->config.errata.skd010) {
+ int errcode;
+
+ errcode = pt_qry_handle_skd010(decoder);
+ if (errcode < 0)
+ return errcode;
+
+ if (errcode)
+ return 0;
+ }
+
ev = pt_evq_standalone(&decoder->evq);
if (!ev)
return -pte_internal;