xref: /freebsd-src/contrib/processor-trace/libipt/src/pt_block_decoder.c (revision 85f87cf491bec6f90948a85b10f5523ea24db9e3)
174fe6c29SRuslan Bukin /*
2*85f87cf4SRuslan Bukin  * Copyright (c) 2016-2019, Intel Corporation
374fe6c29SRuslan Bukin  *
474fe6c29SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
574fe6c29SRuslan Bukin  * modification, are permitted provided that the following conditions are met:
674fe6c29SRuslan Bukin  *
774fe6c29SRuslan Bukin  *  * Redistributions of source code must retain the above copyright notice,
874fe6c29SRuslan Bukin  *    this list of conditions and the following disclaimer.
974fe6c29SRuslan Bukin  *  * Redistributions in binary form must reproduce the above copyright notice,
1074fe6c29SRuslan Bukin  *    this list of conditions and the following disclaimer in the documentation
1174fe6c29SRuslan Bukin  *    and/or other materials provided with the distribution.
1274fe6c29SRuslan Bukin  *  * Neither the name of Intel Corporation nor the names of its contributors
1374fe6c29SRuslan Bukin  *    may be used to endorse or promote products derived from this software
1474fe6c29SRuslan Bukin  *    without specific prior written permission.
1574fe6c29SRuslan Bukin  *
1674fe6c29SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1774fe6c29SRuslan Bukin  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1874fe6c29SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1974fe6c29SRuslan Bukin  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2074fe6c29SRuslan Bukin  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2174fe6c29SRuslan Bukin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2274fe6c29SRuslan Bukin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2374fe6c29SRuslan Bukin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2474fe6c29SRuslan Bukin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2574fe6c29SRuslan Bukin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2674fe6c29SRuslan Bukin  * POSSIBILITY OF SUCH DAMAGE.
2774fe6c29SRuslan Bukin  */
2874fe6c29SRuslan Bukin 
2974fe6c29SRuslan Bukin #include "pt_block_decoder.h"
3074fe6c29SRuslan Bukin #include "pt_block_cache.h"
3174fe6c29SRuslan Bukin #include "pt_section.h"
3274fe6c29SRuslan Bukin #include "pt_image.h"
3374fe6c29SRuslan Bukin #include "pt_insn.h"
3474fe6c29SRuslan Bukin #include "pt_config.h"
3574fe6c29SRuslan Bukin #include "pt_asid.h"
3674fe6c29SRuslan Bukin #include "pt_compiler.h"
3774fe6c29SRuslan Bukin 
3874fe6c29SRuslan Bukin #include "intel-pt.h"
3974fe6c29SRuslan Bukin 
4074fe6c29SRuslan Bukin #include <string.h>
4174fe6c29SRuslan Bukin #include <stdlib.h>
4274fe6c29SRuslan Bukin 
4374fe6c29SRuslan Bukin 
4474fe6c29SRuslan Bukin static int pt_blk_proceed_trailing_event(struct pt_block_decoder *,
4574fe6c29SRuslan Bukin 					 struct pt_block *);
4674fe6c29SRuslan Bukin 
4774fe6c29SRuslan Bukin 
pt_blk_status(const struct pt_block_decoder * decoder,int flags)4874fe6c29SRuslan Bukin static int pt_blk_status(const struct pt_block_decoder *decoder, int flags)
4974fe6c29SRuslan Bukin {
5074fe6c29SRuslan Bukin 	int status;
5174fe6c29SRuslan Bukin 
5274fe6c29SRuslan Bukin 	if (!decoder)
5374fe6c29SRuslan Bukin 		return -pte_internal;
5474fe6c29SRuslan Bukin 
5574fe6c29SRuslan Bukin 	status = decoder->status;
5674fe6c29SRuslan Bukin 
5774fe6c29SRuslan Bukin 	/* Indicate whether tracing is disabled or enabled.
5874fe6c29SRuslan Bukin 	 *
5974fe6c29SRuslan Bukin 	 * This duplicates the indication in struct pt_insn and covers the case
6074fe6c29SRuslan Bukin 	 * where we indicate the status after synchronizing.
6174fe6c29SRuslan Bukin 	 */
6274fe6c29SRuslan Bukin 	if (!decoder->enabled)
6374fe6c29SRuslan Bukin 		flags |= pts_ip_suppressed;
6474fe6c29SRuslan Bukin 
6574fe6c29SRuslan Bukin 	/* Forward end-of-trace indications.
6674fe6c29SRuslan Bukin 	 *
6774fe6c29SRuslan Bukin 	 * Postpone it as long as we're still processing events, though.
6874fe6c29SRuslan Bukin 	 */
6974fe6c29SRuslan Bukin 	if ((status & pts_eos) && !decoder->process_event)
7074fe6c29SRuslan Bukin 		flags |= pts_eos;
7174fe6c29SRuslan Bukin 
7274fe6c29SRuslan Bukin 	return flags;
7374fe6c29SRuslan Bukin }
7474fe6c29SRuslan Bukin 
pt_blk_reset(struct pt_block_decoder * decoder)7574fe6c29SRuslan Bukin static void pt_blk_reset(struct pt_block_decoder *decoder)
7674fe6c29SRuslan Bukin {
7774fe6c29SRuslan Bukin 	if (!decoder)
7874fe6c29SRuslan Bukin 		return;
7974fe6c29SRuslan Bukin 
8074fe6c29SRuslan Bukin 	decoder->mode = ptem_unknown;
8174fe6c29SRuslan Bukin 	decoder->ip = 0ull;
8274fe6c29SRuslan Bukin 	decoder->status = 0;
8374fe6c29SRuslan Bukin 	decoder->enabled = 0;
8474fe6c29SRuslan Bukin 	decoder->process_event = 0;
8574fe6c29SRuslan Bukin 	decoder->speculative = 0;
8674fe6c29SRuslan Bukin 	decoder->process_insn = 0;
8774fe6c29SRuslan Bukin 	decoder->bound_paging = 0;
8874fe6c29SRuslan Bukin 	decoder->bound_vmcs = 0;
8974fe6c29SRuslan Bukin 	decoder->bound_ptwrite = 0;
9074fe6c29SRuslan Bukin 
9174fe6c29SRuslan Bukin 	memset(&decoder->event, 0, sizeof(decoder->event));
9274fe6c29SRuslan Bukin 	pt_retstack_init(&decoder->retstack);
9374fe6c29SRuslan Bukin 	pt_asid_init(&decoder->asid);
9474fe6c29SRuslan Bukin }
9574fe6c29SRuslan Bukin 
9674fe6c29SRuslan Bukin /* Initialize the query decoder flags based on our flags. */
9774fe6c29SRuslan Bukin 
pt_blk_init_qry_flags(struct pt_conf_flags * qflags,const struct pt_conf_flags * flags)9874fe6c29SRuslan Bukin static int pt_blk_init_qry_flags(struct pt_conf_flags *qflags,
9974fe6c29SRuslan Bukin 				 const struct pt_conf_flags *flags)
10074fe6c29SRuslan Bukin {
10174fe6c29SRuslan Bukin 	if (!qflags || !flags)
10274fe6c29SRuslan Bukin 		return -pte_internal;
10374fe6c29SRuslan Bukin 
10474fe6c29SRuslan Bukin 	memset(qflags, 0, sizeof(*qflags));
105*85f87cf4SRuslan Bukin 	qflags->variant.query.keep_tcal_on_ovf =
106*85f87cf4SRuslan Bukin 		flags->variant.block.keep_tcal_on_ovf;
10774fe6c29SRuslan Bukin 
10874fe6c29SRuslan Bukin 	return 0;
10974fe6c29SRuslan Bukin }
11074fe6c29SRuslan Bukin 
pt_blk_decoder_init(struct pt_block_decoder * decoder,const struct pt_config * uconfig)11174fe6c29SRuslan Bukin int pt_blk_decoder_init(struct pt_block_decoder *decoder,
11274fe6c29SRuslan Bukin 			const struct pt_config *uconfig)
11374fe6c29SRuslan Bukin {
11474fe6c29SRuslan Bukin 	struct pt_config config;
11574fe6c29SRuslan Bukin 	int errcode;
11674fe6c29SRuslan Bukin 
11774fe6c29SRuslan Bukin 	if (!decoder)
11874fe6c29SRuslan Bukin 		return -pte_internal;
11974fe6c29SRuslan Bukin 
12074fe6c29SRuslan Bukin 	errcode = pt_config_from_user(&config, uconfig);
12174fe6c29SRuslan Bukin 	if (errcode < 0)
12274fe6c29SRuslan Bukin 		return errcode;
12374fe6c29SRuslan Bukin 
12474fe6c29SRuslan Bukin 	/* The user supplied decoder flags. */
12574fe6c29SRuslan Bukin 	decoder->flags = config.flags;
12674fe6c29SRuslan Bukin 
12774fe6c29SRuslan Bukin 	/* Set the flags we need for the query decoder we use. */
12874fe6c29SRuslan Bukin 	errcode = pt_blk_init_qry_flags(&config.flags, &decoder->flags);
12974fe6c29SRuslan Bukin 	if (errcode < 0)
13074fe6c29SRuslan Bukin 		return errcode;
13174fe6c29SRuslan Bukin 
13274fe6c29SRuslan Bukin 	errcode = pt_qry_decoder_init(&decoder->query, &config);
13374fe6c29SRuslan Bukin 	if (errcode < 0)
13474fe6c29SRuslan Bukin 		return errcode;
13574fe6c29SRuslan Bukin 
13674fe6c29SRuslan Bukin 	pt_image_init(&decoder->default_image, NULL);
13774fe6c29SRuslan Bukin 	decoder->image = &decoder->default_image;
13874fe6c29SRuslan Bukin 
13974fe6c29SRuslan Bukin 	errcode = pt_msec_cache_init(&decoder->scache);
14074fe6c29SRuslan Bukin 	if (errcode < 0)
14174fe6c29SRuslan Bukin 		return errcode;
14274fe6c29SRuslan Bukin 
14374fe6c29SRuslan Bukin 	pt_blk_reset(decoder);
14474fe6c29SRuslan Bukin 
14574fe6c29SRuslan Bukin 	return 0;
14674fe6c29SRuslan Bukin }
14774fe6c29SRuslan Bukin 
pt_blk_decoder_fini(struct pt_block_decoder * decoder)14874fe6c29SRuslan Bukin void pt_blk_decoder_fini(struct pt_block_decoder *decoder)
14974fe6c29SRuslan Bukin {
15074fe6c29SRuslan Bukin 	if (!decoder)
15174fe6c29SRuslan Bukin 		return;
15274fe6c29SRuslan Bukin 
15374fe6c29SRuslan Bukin 	pt_msec_cache_fini(&decoder->scache);
15474fe6c29SRuslan Bukin 	pt_image_fini(&decoder->default_image);
15574fe6c29SRuslan Bukin 	pt_qry_decoder_fini(&decoder->query);
15674fe6c29SRuslan Bukin }
15774fe6c29SRuslan Bukin 
15874fe6c29SRuslan Bukin struct pt_block_decoder *
pt_blk_alloc_decoder(const struct pt_config * config)15974fe6c29SRuslan Bukin pt_blk_alloc_decoder(const struct pt_config *config)
16074fe6c29SRuslan Bukin {
16174fe6c29SRuslan Bukin 	struct pt_block_decoder *decoder;
16274fe6c29SRuslan Bukin 	int errcode;
16374fe6c29SRuslan Bukin 
16474fe6c29SRuslan Bukin 	decoder = malloc(sizeof(*decoder));
16574fe6c29SRuslan Bukin 	if (!decoder)
16674fe6c29SRuslan Bukin 		return NULL;
16774fe6c29SRuslan Bukin 
16874fe6c29SRuslan Bukin 	errcode = pt_blk_decoder_init(decoder, config);
16974fe6c29SRuslan Bukin 	if (errcode < 0) {
17074fe6c29SRuslan Bukin 		free(decoder);
17174fe6c29SRuslan Bukin 		return NULL;
17274fe6c29SRuslan Bukin 	}
17374fe6c29SRuslan Bukin 
17474fe6c29SRuslan Bukin 	return decoder;
17574fe6c29SRuslan Bukin }
17674fe6c29SRuslan Bukin 
pt_blk_free_decoder(struct pt_block_decoder * decoder)17774fe6c29SRuslan Bukin void pt_blk_free_decoder(struct pt_block_decoder *decoder)
17874fe6c29SRuslan Bukin {
17974fe6c29SRuslan Bukin 	if (!decoder)
18074fe6c29SRuslan Bukin 		return;
18174fe6c29SRuslan Bukin 
18274fe6c29SRuslan Bukin 	pt_blk_decoder_fini(decoder);
18374fe6c29SRuslan Bukin 	free(decoder);
18474fe6c29SRuslan Bukin }
18574fe6c29SRuslan Bukin 
18674fe6c29SRuslan Bukin /* Maybe synthesize a tick event.
18774fe6c29SRuslan Bukin  *
18874fe6c29SRuslan Bukin  * If we're not already processing events, check the current time against the
18974fe6c29SRuslan Bukin  * last event's time.  If it changed, synthesize a tick event with the new time.
19074fe6c29SRuslan Bukin  *
19174fe6c29SRuslan Bukin  * Returns zero if no tick event has been created.
19274fe6c29SRuslan Bukin  * Returns a positive integer if a tick event has been created.
19374fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
19474fe6c29SRuslan Bukin  */
pt_blk_tick(struct pt_block_decoder * decoder,uint64_t ip)19574fe6c29SRuslan Bukin static int pt_blk_tick(struct pt_block_decoder *decoder, uint64_t ip)
19674fe6c29SRuslan Bukin {
19774fe6c29SRuslan Bukin 	struct pt_event *ev;
19874fe6c29SRuslan Bukin 	uint64_t tsc;
19974fe6c29SRuslan Bukin 	uint32_t lost_mtc, lost_cyc;
20074fe6c29SRuslan Bukin 	int errcode;
20174fe6c29SRuslan Bukin 
20274fe6c29SRuslan Bukin 	if (!decoder)
20374fe6c29SRuslan Bukin 		return -pte_internal;
20474fe6c29SRuslan Bukin 
20574fe6c29SRuslan Bukin 	/* We're not generating tick events if tracing is disabled. */
20674fe6c29SRuslan Bukin 	if (!decoder->enabled)
20774fe6c29SRuslan Bukin 		return -pte_internal;
20874fe6c29SRuslan Bukin 
20974fe6c29SRuslan Bukin 	/* Events already provide a timestamp so there is no need to synthesize
21074fe6c29SRuslan Bukin 	 * an artificial tick event.  There's no room, either, since this would
21174fe6c29SRuslan Bukin 	 * overwrite the in-progress event.
21274fe6c29SRuslan Bukin 	 *
21374fe6c29SRuslan Bukin 	 * In rare cases where we need to proceed to an event location using
21474fe6c29SRuslan Bukin 	 * trace this may cause us to miss a timing update if the event is not
21574fe6c29SRuslan Bukin 	 * forwarded to the user.
21674fe6c29SRuslan Bukin 	 *
21774fe6c29SRuslan Bukin 	 * The only case I can come up with at the moment is a MODE.EXEC binding
21874fe6c29SRuslan Bukin 	 * to the TIP IP of a far branch.
21974fe6c29SRuslan Bukin 	 */
22074fe6c29SRuslan Bukin 	if (decoder->process_event)
22174fe6c29SRuslan Bukin 		return 0;
22274fe6c29SRuslan Bukin 
22374fe6c29SRuslan Bukin 	errcode = pt_qry_time(&decoder->query, &tsc, &lost_mtc, &lost_cyc);
22474fe6c29SRuslan Bukin 	if (errcode < 0) {
22574fe6c29SRuslan Bukin 		/* If we don't have wall-clock time, we use relative time. */
22674fe6c29SRuslan Bukin 		if (errcode != -pte_no_time)
22774fe6c29SRuslan Bukin 			return errcode;
22874fe6c29SRuslan Bukin 	}
22974fe6c29SRuslan Bukin 
23074fe6c29SRuslan Bukin 	ev = &decoder->event;
23174fe6c29SRuslan Bukin 
23274fe6c29SRuslan Bukin 	/* We're done if time has not changed since the last event. */
23374fe6c29SRuslan Bukin 	if (tsc == ev->tsc)
23474fe6c29SRuslan Bukin 		return 0;
23574fe6c29SRuslan Bukin 
23674fe6c29SRuslan Bukin 	/* Time has changed so we create a new tick event. */
23774fe6c29SRuslan Bukin 	memset(ev, 0, sizeof(*ev));
23874fe6c29SRuslan Bukin 	ev->type = ptev_tick;
23974fe6c29SRuslan Bukin 	ev->variant.tick.ip = ip;
24074fe6c29SRuslan Bukin 
24174fe6c29SRuslan Bukin 	/* Indicate if we have wall-clock time or only relative time. */
24274fe6c29SRuslan Bukin 	if (errcode != -pte_no_time)
24374fe6c29SRuslan Bukin 		ev->has_tsc = 1;
24474fe6c29SRuslan Bukin 	ev->tsc = tsc;
24574fe6c29SRuslan Bukin 	ev->lost_mtc = lost_mtc;
24674fe6c29SRuslan Bukin 	ev->lost_cyc = lost_cyc;
24774fe6c29SRuslan Bukin 
24874fe6c29SRuslan Bukin 	/* We now have an event to process. */
24974fe6c29SRuslan Bukin 	decoder->process_event = 1;
25074fe6c29SRuslan Bukin 
25174fe6c29SRuslan Bukin 	return 1;
25274fe6c29SRuslan Bukin }
25374fe6c29SRuslan Bukin 
25474fe6c29SRuslan Bukin /* Query an indirect branch.
25574fe6c29SRuslan Bukin  *
25674fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
25774fe6c29SRuslan Bukin  */
pt_blk_indirect_branch(struct pt_block_decoder * decoder,uint64_t * ip)25874fe6c29SRuslan Bukin static int pt_blk_indirect_branch(struct pt_block_decoder *decoder,
25974fe6c29SRuslan Bukin 				  uint64_t *ip)
26074fe6c29SRuslan Bukin {
26174fe6c29SRuslan Bukin 	uint64_t evip;
26274fe6c29SRuslan Bukin 	int status, errcode;
26374fe6c29SRuslan Bukin 
26474fe6c29SRuslan Bukin 	if (!decoder)
26574fe6c29SRuslan Bukin 		return -pte_internal;
26674fe6c29SRuslan Bukin 
26774fe6c29SRuslan Bukin 	evip = decoder->ip;
26874fe6c29SRuslan Bukin 
26974fe6c29SRuslan Bukin 	status = pt_qry_indirect_branch(&decoder->query, ip);
27074fe6c29SRuslan Bukin 	if (status < 0)
27174fe6c29SRuslan Bukin 		return status;
27274fe6c29SRuslan Bukin 
27374fe6c29SRuslan Bukin 	if (decoder->flags.variant.block.enable_tick_events) {
27474fe6c29SRuslan Bukin 		errcode = pt_blk_tick(decoder, evip);
27574fe6c29SRuslan Bukin 		if (errcode < 0)
27674fe6c29SRuslan Bukin 			return errcode;
27774fe6c29SRuslan Bukin 	}
27874fe6c29SRuslan Bukin 
27974fe6c29SRuslan Bukin 	return status;
28074fe6c29SRuslan Bukin }
28174fe6c29SRuslan Bukin 
28274fe6c29SRuslan Bukin /* Query a conditional branch.
28374fe6c29SRuslan Bukin  *
28474fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
28574fe6c29SRuslan Bukin  */
pt_blk_cond_branch(struct pt_block_decoder * decoder,int * taken)28674fe6c29SRuslan Bukin static int pt_blk_cond_branch(struct pt_block_decoder *decoder, int *taken)
28774fe6c29SRuslan Bukin {
28874fe6c29SRuslan Bukin 	int status, errcode;
28974fe6c29SRuslan Bukin 
29074fe6c29SRuslan Bukin 	if (!decoder)
29174fe6c29SRuslan Bukin 		return -pte_internal;
29274fe6c29SRuslan Bukin 
29374fe6c29SRuslan Bukin 	status = pt_qry_cond_branch(&decoder->query, taken);
29474fe6c29SRuslan Bukin 	if (status < 0)
29574fe6c29SRuslan Bukin 		return status;
29674fe6c29SRuslan Bukin 
29774fe6c29SRuslan Bukin 	if (decoder->flags.variant.block.enable_tick_events) {
29874fe6c29SRuslan Bukin 		errcode = pt_blk_tick(decoder, decoder->ip);
29974fe6c29SRuslan Bukin 		if (errcode < 0)
30074fe6c29SRuslan Bukin 			return errcode;
30174fe6c29SRuslan Bukin 	}
30274fe6c29SRuslan Bukin 
30374fe6c29SRuslan Bukin 	return status;
30474fe6c29SRuslan Bukin }
30574fe6c29SRuslan Bukin 
pt_blk_start(struct pt_block_decoder * decoder,int status)30674fe6c29SRuslan Bukin static int pt_blk_start(struct pt_block_decoder *decoder, int status)
30774fe6c29SRuslan Bukin {
30874fe6c29SRuslan Bukin 	if (!decoder)
30974fe6c29SRuslan Bukin 		return -pte_internal;
31074fe6c29SRuslan Bukin 
31174fe6c29SRuslan Bukin 	if (status < 0)
31274fe6c29SRuslan Bukin 		return status;
31374fe6c29SRuslan Bukin 
31474fe6c29SRuslan Bukin 	decoder->status = status;
31574fe6c29SRuslan Bukin 	if (!(status & pts_ip_suppressed))
31674fe6c29SRuslan Bukin 		decoder->enabled = 1;
31774fe6c29SRuslan Bukin 
31874fe6c29SRuslan Bukin 	/* We will always have an event.
31974fe6c29SRuslan Bukin 	 *
32074fe6c29SRuslan Bukin 	 * If we synchronized onto an empty PSB+, tracing is disabled and we'll
32174fe6c29SRuslan Bukin 	 * process events until the enabled event.
32274fe6c29SRuslan Bukin 	 *
32374fe6c29SRuslan Bukin 	 * If tracing is enabled, PSB+ must at least provide the execution mode,
32474fe6c29SRuslan Bukin 	 * which we're going to forward to the user.
32574fe6c29SRuslan Bukin 	 */
32674fe6c29SRuslan Bukin 	return pt_blk_proceed_trailing_event(decoder, NULL);
32774fe6c29SRuslan Bukin }
32874fe6c29SRuslan Bukin 
pt_blk_sync_reset(struct pt_block_decoder * decoder)32974fe6c29SRuslan Bukin static int pt_blk_sync_reset(struct pt_block_decoder *decoder)
33074fe6c29SRuslan Bukin {
33174fe6c29SRuslan Bukin 	if (!decoder)
33274fe6c29SRuslan Bukin 		return -pte_internal;
33374fe6c29SRuslan Bukin 
33474fe6c29SRuslan Bukin 	pt_blk_reset(decoder);
33574fe6c29SRuslan Bukin 
33674fe6c29SRuslan Bukin 	return 0;
33774fe6c29SRuslan Bukin }
33874fe6c29SRuslan Bukin 
pt_blk_sync_forward(struct pt_block_decoder * decoder)33974fe6c29SRuslan Bukin int pt_blk_sync_forward(struct pt_block_decoder *decoder)
34074fe6c29SRuslan Bukin {
34174fe6c29SRuslan Bukin 	int errcode, status;
34274fe6c29SRuslan Bukin 
34374fe6c29SRuslan Bukin 	if (!decoder)
34474fe6c29SRuslan Bukin 		return -pte_invalid;
34574fe6c29SRuslan Bukin 
34674fe6c29SRuslan Bukin 	errcode = pt_blk_sync_reset(decoder);
34774fe6c29SRuslan Bukin 	if (errcode < 0)
34874fe6c29SRuslan Bukin 		return errcode;
34974fe6c29SRuslan Bukin 
35074fe6c29SRuslan Bukin 	status = pt_qry_sync_forward(&decoder->query, &decoder->ip);
35174fe6c29SRuslan Bukin 
35274fe6c29SRuslan Bukin 	return pt_blk_start(decoder, status);
35374fe6c29SRuslan Bukin }
35474fe6c29SRuslan Bukin 
pt_blk_sync_backward(struct pt_block_decoder * decoder)35574fe6c29SRuslan Bukin int pt_blk_sync_backward(struct pt_block_decoder *decoder)
35674fe6c29SRuslan Bukin {
35774fe6c29SRuslan Bukin 	int errcode, status;
35874fe6c29SRuslan Bukin 
35974fe6c29SRuslan Bukin 	if (!decoder)
36074fe6c29SRuslan Bukin 		return -pte_invalid;
36174fe6c29SRuslan Bukin 
36274fe6c29SRuslan Bukin 	errcode = pt_blk_sync_reset(decoder);
36374fe6c29SRuslan Bukin 	if (errcode < 0)
36474fe6c29SRuslan Bukin 		return errcode;
36574fe6c29SRuslan Bukin 
36674fe6c29SRuslan Bukin 	status = pt_qry_sync_backward(&decoder->query, &decoder->ip);
36774fe6c29SRuslan Bukin 
36874fe6c29SRuslan Bukin 	return pt_blk_start(decoder, status);
36974fe6c29SRuslan Bukin }
37074fe6c29SRuslan Bukin 
pt_blk_sync_set(struct pt_block_decoder * decoder,uint64_t offset)37174fe6c29SRuslan Bukin int pt_blk_sync_set(struct pt_block_decoder *decoder, uint64_t offset)
37274fe6c29SRuslan Bukin {
37374fe6c29SRuslan Bukin 	int errcode, status;
37474fe6c29SRuslan Bukin 
37574fe6c29SRuslan Bukin 	if (!decoder)
37674fe6c29SRuslan Bukin 		return -pte_invalid;
37774fe6c29SRuslan Bukin 
37874fe6c29SRuslan Bukin 	errcode = pt_blk_sync_reset(decoder);
37974fe6c29SRuslan Bukin 	if (errcode < 0)
38074fe6c29SRuslan Bukin 		return errcode;
38174fe6c29SRuslan Bukin 
38274fe6c29SRuslan Bukin 	status = pt_qry_sync_set(&decoder->query, &decoder->ip, offset);
38374fe6c29SRuslan Bukin 
38474fe6c29SRuslan Bukin 	return pt_blk_start(decoder, status);
38574fe6c29SRuslan Bukin }
38674fe6c29SRuslan Bukin 
pt_blk_get_offset(const struct pt_block_decoder * decoder,uint64_t * offset)38774fe6c29SRuslan Bukin int pt_blk_get_offset(const struct pt_block_decoder *decoder, uint64_t *offset)
38874fe6c29SRuslan Bukin {
38974fe6c29SRuslan Bukin 	if (!decoder)
39074fe6c29SRuslan Bukin 		return -pte_invalid;
39174fe6c29SRuslan Bukin 
39274fe6c29SRuslan Bukin 	return pt_qry_get_offset(&decoder->query, offset);
39374fe6c29SRuslan Bukin }
39474fe6c29SRuslan Bukin 
pt_blk_get_sync_offset(const struct pt_block_decoder * decoder,uint64_t * offset)39574fe6c29SRuslan Bukin int pt_blk_get_sync_offset(const struct pt_block_decoder *decoder,
39674fe6c29SRuslan Bukin 			   uint64_t *offset)
39774fe6c29SRuslan Bukin {
39874fe6c29SRuslan Bukin 	if (!decoder)
39974fe6c29SRuslan Bukin 		return -pte_invalid;
40074fe6c29SRuslan Bukin 
40174fe6c29SRuslan Bukin 	return pt_qry_get_sync_offset(&decoder->query, offset);
40274fe6c29SRuslan Bukin }
40374fe6c29SRuslan Bukin 
pt_blk_get_image(struct pt_block_decoder * decoder)40474fe6c29SRuslan Bukin struct pt_image *pt_blk_get_image(struct pt_block_decoder *decoder)
40574fe6c29SRuslan Bukin {
40674fe6c29SRuslan Bukin 	if (!decoder)
40774fe6c29SRuslan Bukin 		return NULL;
40874fe6c29SRuslan Bukin 
40974fe6c29SRuslan Bukin 	return decoder->image;
41074fe6c29SRuslan Bukin }
41174fe6c29SRuslan Bukin 
pt_blk_set_image(struct pt_block_decoder * decoder,struct pt_image * image)41274fe6c29SRuslan Bukin int pt_blk_set_image(struct pt_block_decoder *decoder, struct pt_image *image)
41374fe6c29SRuslan Bukin {
41474fe6c29SRuslan Bukin 	if (!decoder)
41574fe6c29SRuslan Bukin 		return -pte_invalid;
41674fe6c29SRuslan Bukin 
41774fe6c29SRuslan Bukin 	if (!image)
41874fe6c29SRuslan Bukin 		image = &decoder->default_image;
41974fe6c29SRuslan Bukin 
42074fe6c29SRuslan Bukin 	decoder->image = image;
42174fe6c29SRuslan Bukin 	return 0;
42274fe6c29SRuslan Bukin }
42374fe6c29SRuslan Bukin 
42474fe6c29SRuslan Bukin const struct pt_config *
pt_blk_get_config(const struct pt_block_decoder * decoder)42574fe6c29SRuslan Bukin pt_blk_get_config(const struct pt_block_decoder *decoder)
42674fe6c29SRuslan Bukin {
42774fe6c29SRuslan Bukin 	if (!decoder)
42874fe6c29SRuslan Bukin 		return NULL;
42974fe6c29SRuslan Bukin 
43074fe6c29SRuslan Bukin 	return pt_qry_get_config(&decoder->query);
43174fe6c29SRuslan Bukin }
43274fe6c29SRuslan Bukin 
pt_blk_time(struct pt_block_decoder * decoder,uint64_t * time,uint32_t * lost_mtc,uint32_t * lost_cyc)43374fe6c29SRuslan Bukin int pt_blk_time(struct pt_block_decoder *decoder, uint64_t *time,
43474fe6c29SRuslan Bukin 		uint32_t *lost_mtc, uint32_t *lost_cyc)
43574fe6c29SRuslan Bukin {
43674fe6c29SRuslan Bukin 	if (!decoder || !time)
43774fe6c29SRuslan Bukin 		return -pte_invalid;
43874fe6c29SRuslan Bukin 
43974fe6c29SRuslan Bukin 	return pt_qry_time(&decoder->query, time, lost_mtc, lost_cyc);
44074fe6c29SRuslan Bukin }
44174fe6c29SRuslan Bukin 
pt_blk_core_bus_ratio(struct pt_block_decoder * decoder,uint32_t * cbr)44274fe6c29SRuslan Bukin int pt_blk_core_bus_ratio(struct pt_block_decoder *decoder, uint32_t *cbr)
44374fe6c29SRuslan Bukin {
44474fe6c29SRuslan Bukin 	if (!decoder || !cbr)
44574fe6c29SRuslan Bukin 		return -pte_invalid;
44674fe6c29SRuslan Bukin 
44774fe6c29SRuslan Bukin 	return pt_qry_core_bus_ratio(&decoder->query, cbr);
44874fe6c29SRuslan Bukin }
44974fe6c29SRuslan Bukin 
pt_blk_asid(const struct pt_block_decoder * decoder,struct pt_asid * asid,size_t size)45074fe6c29SRuslan Bukin int pt_blk_asid(const struct pt_block_decoder *decoder, struct pt_asid *asid,
45174fe6c29SRuslan Bukin 		size_t size)
45274fe6c29SRuslan Bukin {
45374fe6c29SRuslan Bukin 	if (!decoder || !asid)
45474fe6c29SRuslan Bukin 		return -pte_invalid;
45574fe6c29SRuslan Bukin 
45674fe6c29SRuslan Bukin 	return pt_asid_to_user(asid, &decoder->asid, size);
45774fe6c29SRuslan Bukin }
45874fe6c29SRuslan Bukin 
45974fe6c29SRuslan Bukin /* Fetch the next pending event.
46074fe6c29SRuslan Bukin  *
46174fe6c29SRuslan Bukin  * Checks for pending events.  If an event is pending, fetches it (if not
46274fe6c29SRuslan Bukin  * already in process).
46374fe6c29SRuslan Bukin  *
46474fe6c29SRuslan Bukin  * Returns zero if no event is pending.
46574fe6c29SRuslan Bukin  * Returns a positive integer if an event is pending or in process.
46674fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
46774fe6c29SRuslan Bukin  */
pt_blk_fetch_event(struct pt_block_decoder * decoder)46874fe6c29SRuslan Bukin static inline int pt_blk_fetch_event(struct pt_block_decoder *decoder)
46974fe6c29SRuslan Bukin {
47074fe6c29SRuslan Bukin 	int status;
47174fe6c29SRuslan Bukin 
47274fe6c29SRuslan Bukin 	if (!decoder)
47374fe6c29SRuslan Bukin 		return -pte_internal;
47474fe6c29SRuslan Bukin 
47574fe6c29SRuslan Bukin 	if (decoder->process_event)
47674fe6c29SRuslan Bukin 		return 1;
47774fe6c29SRuslan Bukin 
47874fe6c29SRuslan Bukin 	if (!(decoder->status & pts_event_pending))
47974fe6c29SRuslan Bukin 		return 0;
48074fe6c29SRuslan Bukin 
48174fe6c29SRuslan Bukin 	status = pt_qry_event(&decoder->query, &decoder->event,
48274fe6c29SRuslan Bukin 			      sizeof(decoder->event));
48374fe6c29SRuslan Bukin 	if (status < 0)
48474fe6c29SRuslan Bukin 		return status;
48574fe6c29SRuslan Bukin 
48674fe6c29SRuslan Bukin 	decoder->process_event = 1;
48774fe6c29SRuslan Bukin 	decoder->status = status;
48874fe6c29SRuslan Bukin 
48974fe6c29SRuslan Bukin 	return 1;
49074fe6c29SRuslan Bukin }
49174fe6c29SRuslan Bukin 
pt_blk_block_is_empty(const struct pt_block * block)49274fe6c29SRuslan Bukin static inline int pt_blk_block_is_empty(const struct pt_block *block)
49374fe6c29SRuslan Bukin {
49474fe6c29SRuslan Bukin 	if (!block)
49574fe6c29SRuslan Bukin 		return 1;
49674fe6c29SRuslan Bukin 
49774fe6c29SRuslan Bukin 	return !block->ninsn;
49874fe6c29SRuslan Bukin }
49974fe6c29SRuslan Bukin 
block_to_user(struct pt_block * ublock,size_t size,const struct pt_block * block)50074fe6c29SRuslan Bukin static inline int block_to_user(struct pt_block *ublock, size_t size,
50174fe6c29SRuslan Bukin 				const struct pt_block *block)
50274fe6c29SRuslan Bukin {
50374fe6c29SRuslan Bukin 	if (!ublock || !block)
50474fe6c29SRuslan Bukin 		return -pte_internal;
50574fe6c29SRuslan Bukin 
50674fe6c29SRuslan Bukin 	if (ublock == block)
50774fe6c29SRuslan Bukin 		return 0;
50874fe6c29SRuslan Bukin 
50974fe6c29SRuslan Bukin 	/* Zero out any unknown bytes. */
51074fe6c29SRuslan Bukin 	if (sizeof(*block) < size) {
51174fe6c29SRuslan Bukin 		memset(ublock + sizeof(*block), 0, size - sizeof(*block));
51274fe6c29SRuslan Bukin 
51374fe6c29SRuslan Bukin 		size = sizeof(*block);
51474fe6c29SRuslan Bukin 	}
51574fe6c29SRuslan Bukin 
51674fe6c29SRuslan Bukin 	memcpy(ublock, block, size);
51774fe6c29SRuslan Bukin 
51874fe6c29SRuslan Bukin 	return 0;
51974fe6c29SRuslan Bukin }
52074fe6c29SRuslan Bukin 
pt_insn_false(const struct pt_insn * insn,const struct pt_insn_ext * iext)52174fe6c29SRuslan Bukin static int pt_insn_false(const struct pt_insn *insn,
52274fe6c29SRuslan Bukin 			 const struct pt_insn_ext *iext)
52374fe6c29SRuslan Bukin {
52474fe6c29SRuslan Bukin 	(void) insn;
52574fe6c29SRuslan Bukin 	(void) iext;
52674fe6c29SRuslan Bukin 
52774fe6c29SRuslan Bukin 	return 0;
52874fe6c29SRuslan Bukin }
52974fe6c29SRuslan Bukin 
53074fe6c29SRuslan Bukin /* Determine the next IP using trace.
53174fe6c29SRuslan Bukin  *
53274fe6c29SRuslan Bukin  * Tries to determine the IP of the next instruction using trace and provides it
53374fe6c29SRuslan Bukin  * in @pip.
53474fe6c29SRuslan Bukin  *
53574fe6c29SRuslan Bukin  * Not requiring trace to determine the IP is treated as an internal error.
53674fe6c29SRuslan Bukin  *
53774fe6c29SRuslan Bukin  * Does not update the return compression stack for indirect calls.  This is
53874fe6c29SRuslan Bukin  * expected to have been done, already, when trying to determine the next IP
53974fe6c29SRuslan Bukin  * without using trace.
54074fe6c29SRuslan Bukin  *
54174fe6c29SRuslan Bukin  * Does not update @decoder->status.  The caller is expected to do that.
54274fe6c29SRuslan Bukin  *
54374fe6c29SRuslan Bukin  * Returns a non-negative pt_status_flag bit-vector on success, a negative error
54474fe6c29SRuslan Bukin  * code otherwise.
54574fe6c29SRuslan Bukin  * Returns -pte_internal if @pip, @decoder, @insn, or @iext are NULL.
54674fe6c29SRuslan Bukin  * Returns -pte_internal if no trace is required.
54774fe6c29SRuslan Bukin  */
pt_blk_next_ip(uint64_t * pip,struct pt_block_decoder * decoder,const struct pt_insn * insn,const struct pt_insn_ext * iext)54874fe6c29SRuslan Bukin static int pt_blk_next_ip(uint64_t *pip, struct pt_block_decoder *decoder,
54974fe6c29SRuslan Bukin 			  const struct pt_insn *insn,
55074fe6c29SRuslan Bukin 			  const struct pt_insn_ext *iext)
55174fe6c29SRuslan Bukin {
55274fe6c29SRuslan Bukin 	int status, errcode;
55374fe6c29SRuslan Bukin 
55474fe6c29SRuslan Bukin 	if (!pip || !decoder || !insn || !iext)
55574fe6c29SRuslan Bukin 		return -pte_internal;
55674fe6c29SRuslan Bukin 
55774fe6c29SRuslan Bukin 	/* We handle non-taken conditional branches, and compressed returns
55874fe6c29SRuslan Bukin 	 * directly in the switch.
55974fe6c29SRuslan Bukin 	 *
56074fe6c29SRuslan Bukin 	 * All kinds of branches are handled below the switch.
56174fe6c29SRuslan Bukin 	 */
56274fe6c29SRuslan Bukin 	switch (insn->iclass) {
56374fe6c29SRuslan Bukin 	case ptic_cond_jump: {
56474fe6c29SRuslan Bukin 		uint64_t ip;
56574fe6c29SRuslan Bukin 		int taken;
56674fe6c29SRuslan Bukin 
56774fe6c29SRuslan Bukin 		status = pt_blk_cond_branch(decoder, &taken);
56874fe6c29SRuslan Bukin 		if (status < 0)
56974fe6c29SRuslan Bukin 			return status;
57074fe6c29SRuslan Bukin 
57174fe6c29SRuslan Bukin 		ip = insn->ip + insn->size;
57274fe6c29SRuslan Bukin 		if (taken)
573*85f87cf4SRuslan Bukin 			ip += (uint64_t) (int64_t)
574*85f87cf4SRuslan Bukin 				iext->variant.branch.displacement;
57574fe6c29SRuslan Bukin 
57674fe6c29SRuslan Bukin 		*pip = ip;
57774fe6c29SRuslan Bukin 		return status;
57874fe6c29SRuslan Bukin 	}
57974fe6c29SRuslan Bukin 
58074fe6c29SRuslan Bukin 	case ptic_return: {
58174fe6c29SRuslan Bukin 		int taken;
58274fe6c29SRuslan Bukin 
58374fe6c29SRuslan Bukin 		/* Check for a compressed return. */
58474fe6c29SRuslan Bukin 		status = pt_blk_cond_branch(decoder, &taken);
58574fe6c29SRuslan Bukin 		if (status < 0) {
58674fe6c29SRuslan Bukin 			if (status != -pte_bad_query)
58774fe6c29SRuslan Bukin 				return status;
58874fe6c29SRuslan Bukin 
58974fe6c29SRuslan Bukin 			break;
59074fe6c29SRuslan Bukin 		}
59174fe6c29SRuslan Bukin 
59274fe6c29SRuslan Bukin 		/* A compressed return is indicated by a taken conditional
59374fe6c29SRuslan Bukin 		 * branch.
59474fe6c29SRuslan Bukin 		 */
59574fe6c29SRuslan Bukin 		if (!taken)
59674fe6c29SRuslan Bukin 			return -pte_bad_retcomp;
59774fe6c29SRuslan Bukin 
59874fe6c29SRuslan Bukin 		errcode = pt_retstack_pop(&decoder->retstack, pip);
59974fe6c29SRuslan Bukin 		if (errcode < 0)
60074fe6c29SRuslan Bukin 			return errcode;
60174fe6c29SRuslan Bukin 
60274fe6c29SRuslan Bukin 		return status;
60374fe6c29SRuslan Bukin 	}
60474fe6c29SRuslan Bukin 
60574fe6c29SRuslan Bukin 	case ptic_jump:
60674fe6c29SRuslan Bukin 	case ptic_call:
60774fe6c29SRuslan Bukin 		/* A direct jump or call wouldn't require trace. */
60874fe6c29SRuslan Bukin 		if (iext->variant.branch.is_direct)
60974fe6c29SRuslan Bukin 			return -pte_internal;
61074fe6c29SRuslan Bukin 
61174fe6c29SRuslan Bukin 		break;
61274fe6c29SRuslan Bukin 
61374fe6c29SRuslan Bukin 	case ptic_far_call:
61474fe6c29SRuslan Bukin 	case ptic_far_return:
61574fe6c29SRuslan Bukin 	case ptic_far_jump:
61674fe6c29SRuslan Bukin 		break;
61774fe6c29SRuslan Bukin 
61874fe6c29SRuslan Bukin 	case ptic_ptwrite:
61974fe6c29SRuslan Bukin 	case ptic_other:
62074fe6c29SRuslan Bukin 		return -pte_internal;
62174fe6c29SRuslan Bukin 
62274fe6c29SRuslan Bukin 	case ptic_error:
62374fe6c29SRuslan Bukin 		return -pte_bad_insn;
62474fe6c29SRuslan Bukin 	}
62574fe6c29SRuslan Bukin 
62674fe6c29SRuslan Bukin 	/* Process an indirect branch.
62774fe6c29SRuslan Bukin 	 *
62874fe6c29SRuslan Bukin 	 * This covers indirect jumps and calls, non-compressed returns, and all
62974fe6c29SRuslan Bukin 	 * flavors of far transfers.
63074fe6c29SRuslan Bukin 	 */
63174fe6c29SRuslan Bukin 	return pt_blk_indirect_branch(decoder, pip);
63274fe6c29SRuslan Bukin }
63374fe6c29SRuslan Bukin 
63474fe6c29SRuslan Bukin /* Proceed to the next IP using trace.
63574fe6c29SRuslan Bukin  *
63674fe6c29SRuslan Bukin  * We failed to proceed without trace.  This ends the current block.  Now use
63774fe6c29SRuslan Bukin  * trace to do one final step to determine the start IP of the next block.
63874fe6c29SRuslan Bukin  *
63974fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
64074fe6c29SRuslan Bukin  */
pt_blk_proceed_with_trace(struct pt_block_decoder * decoder,const struct pt_insn * insn,const struct pt_insn_ext * iext)64174fe6c29SRuslan Bukin static int pt_blk_proceed_with_trace(struct pt_block_decoder *decoder,
64274fe6c29SRuslan Bukin 				     const struct pt_insn *insn,
64374fe6c29SRuslan Bukin 				     const struct pt_insn_ext *iext)
64474fe6c29SRuslan Bukin {
64574fe6c29SRuslan Bukin 	int status;
64674fe6c29SRuslan Bukin 
64774fe6c29SRuslan Bukin 	if (!decoder)
64874fe6c29SRuslan Bukin 		return -pte_internal;
64974fe6c29SRuslan Bukin 
65074fe6c29SRuslan Bukin 	status = pt_blk_next_ip(&decoder->ip, decoder, insn, iext);
65174fe6c29SRuslan Bukin 	if (status < 0)
65274fe6c29SRuslan Bukin 		return status;
65374fe6c29SRuslan Bukin 
65474fe6c29SRuslan Bukin 	/* Preserve the query decoder's response which indicates upcoming
65574fe6c29SRuslan Bukin 	 * events.
65674fe6c29SRuslan Bukin 	 */
65774fe6c29SRuslan Bukin 	decoder->status = status;
65874fe6c29SRuslan Bukin 
65974fe6c29SRuslan Bukin 	/* We do need an IP in order to proceed. */
66074fe6c29SRuslan Bukin 	if (status & pts_ip_suppressed)
66174fe6c29SRuslan Bukin 		return -pte_noip;
66274fe6c29SRuslan Bukin 
66374fe6c29SRuslan Bukin 	return 0;
66474fe6c29SRuslan Bukin }
66574fe6c29SRuslan Bukin 
66674fe6c29SRuslan Bukin /* Decode one instruction in a known section.
66774fe6c29SRuslan Bukin  *
66874fe6c29SRuslan Bukin  * Decode the instruction at @insn->ip in @msec assuming execution mode
66974fe6c29SRuslan Bukin  * @insn->mode.
67074fe6c29SRuslan Bukin  *
67174fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
67274fe6c29SRuslan Bukin  */
pt_blk_decode_in_section(struct pt_insn * insn,struct pt_insn_ext * iext,const struct pt_mapped_section * msec)67374fe6c29SRuslan Bukin static int pt_blk_decode_in_section(struct pt_insn *insn,
67474fe6c29SRuslan Bukin 				    struct pt_insn_ext *iext,
67574fe6c29SRuslan Bukin 				    const struct pt_mapped_section *msec)
67674fe6c29SRuslan Bukin {
67774fe6c29SRuslan Bukin 	int status;
67874fe6c29SRuslan Bukin 
67974fe6c29SRuslan Bukin 	if (!insn || !iext)
68074fe6c29SRuslan Bukin 		return -pte_internal;
68174fe6c29SRuslan Bukin 
68274fe6c29SRuslan Bukin 	/* We know that @ip is contained in @section.
68374fe6c29SRuslan Bukin 	 *
68474fe6c29SRuslan Bukin 	 * Note that we need to translate @ip into a section offset.
68574fe6c29SRuslan Bukin 	 */
68674fe6c29SRuslan Bukin 	status = pt_msec_read(msec, insn->raw, sizeof(insn->raw), insn->ip);
68774fe6c29SRuslan Bukin 	if (status < 0)
68874fe6c29SRuslan Bukin 		return status;
68974fe6c29SRuslan Bukin 
69074fe6c29SRuslan Bukin 	/* We initialize @insn->size to the maximal possible size.  It will be
69174fe6c29SRuslan Bukin 	 * set to the actual size during instruction decode.
69274fe6c29SRuslan Bukin 	 */
69374fe6c29SRuslan Bukin 	insn->size = (uint8_t) status;
69474fe6c29SRuslan Bukin 
69574fe6c29SRuslan Bukin 	return pt_ild_decode(insn, iext);
69674fe6c29SRuslan Bukin }
69774fe6c29SRuslan Bukin 
69874fe6c29SRuslan Bukin /* Update the return-address stack if @insn is a near call.
69974fe6c29SRuslan Bukin  *
70074fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
70174fe6c29SRuslan Bukin  */
pt_blk_log_call(struct pt_block_decoder * decoder,const struct pt_insn * insn,const struct pt_insn_ext * iext)70274fe6c29SRuslan Bukin static inline int pt_blk_log_call(struct pt_block_decoder *decoder,
70374fe6c29SRuslan Bukin 				  const struct pt_insn *insn,
70474fe6c29SRuslan Bukin 				  const struct pt_insn_ext *iext)
70574fe6c29SRuslan Bukin {
70674fe6c29SRuslan Bukin 	if (!decoder || !insn || !iext)
70774fe6c29SRuslan Bukin 		return -pte_internal;
70874fe6c29SRuslan Bukin 
70974fe6c29SRuslan Bukin 	if (insn->iclass != ptic_call)
71074fe6c29SRuslan Bukin 		return 0;
71174fe6c29SRuslan Bukin 
71274fe6c29SRuslan Bukin 	/* Ignore direct calls to the next instruction that are used for
71374fe6c29SRuslan Bukin 	 * position independent code.
71474fe6c29SRuslan Bukin 	 */
71574fe6c29SRuslan Bukin 	if (iext->variant.branch.is_direct &&
71674fe6c29SRuslan Bukin 	    !iext->variant.branch.displacement)
71774fe6c29SRuslan Bukin 		return 0;
71874fe6c29SRuslan Bukin 
71974fe6c29SRuslan Bukin 	return pt_retstack_push(&decoder->retstack, insn->ip + insn->size);
72074fe6c29SRuslan Bukin }
72174fe6c29SRuslan Bukin 
72274fe6c29SRuslan Bukin /* Proceed by one instruction.
72374fe6c29SRuslan Bukin  *
72474fe6c29SRuslan Bukin  * Tries to decode the instruction at @decoder->ip and, on success, adds it to
72574fe6c29SRuslan Bukin  * @block and provides it in @pinsn and @piext.
72674fe6c29SRuslan Bukin  *
72774fe6c29SRuslan Bukin  * The instruction will not be added if:
72874fe6c29SRuslan Bukin  *
72974fe6c29SRuslan Bukin  *   - the memory could not be read:  return error
73074fe6c29SRuslan Bukin  *   - it could not be decoded:       return error
73174fe6c29SRuslan Bukin  *   - @block is already full:        return zero
73274fe6c29SRuslan Bukin  *   - @block would switch sections:  return zero
73374fe6c29SRuslan Bukin  *
73474fe6c29SRuslan Bukin  * Returns a positive integer if the instruction was added.
73574fe6c29SRuslan Bukin  * Returns zero if the instruction didn't fit into @block.
73674fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
73774fe6c29SRuslan Bukin  */
pt_blk_proceed_one_insn(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_insn * pinsn,struct pt_insn_ext * piext)73874fe6c29SRuslan Bukin static int pt_blk_proceed_one_insn(struct pt_block_decoder *decoder,
73974fe6c29SRuslan Bukin 				   struct pt_block *block,
74074fe6c29SRuslan Bukin 				   struct pt_insn *pinsn,
74174fe6c29SRuslan Bukin 				   struct pt_insn_ext *piext)
74274fe6c29SRuslan Bukin {
74374fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
74474fe6c29SRuslan Bukin 	struct pt_insn insn;
74574fe6c29SRuslan Bukin 	uint16_t ninsn;
74674fe6c29SRuslan Bukin 	int status;
74774fe6c29SRuslan Bukin 
74874fe6c29SRuslan Bukin 	if (!decoder || !block || !pinsn || !piext)
74974fe6c29SRuslan Bukin 		return -pte_internal;
75074fe6c29SRuslan Bukin 
75174fe6c29SRuslan Bukin 	/* There's nothing to do if there is no room in @block. */
75274fe6c29SRuslan Bukin 	ninsn = block->ninsn + 1;
75374fe6c29SRuslan Bukin 	if (!ninsn)
75474fe6c29SRuslan Bukin 		return 0;
75574fe6c29SRuslan Bukin 
75674fe6c29SRuslan Bukin 	/* The truncated instruction must be last. */
75774fe6c29SRuslan Bukin 	if (block->truncated)
75874fe6c29SRuslan Bukin 		return 0;
75974fe6c29SRuslan Bukin 
76074fe6c29SRuslan Bukin 	memset(&insn, 0, sizeof(insn));
76174fe6c29SRuslan Bukin 	memset(&iext, 0, sizeof(iext));
76274fe6c29SRuslan Bukin 
76374fe6c29SRuslan Bukin 	insn.mode = decoder->mode;
76474fe6c29SRuslan Bukin 	insn.ip = decoder->ip;
76574fe6c29SRuslan Bukin 
76674fe6c29SRuslan Bukin 	status = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
76774fe6c29SRuslan Bukin 	if (status < 0)
76874fe6c29SRuslan Bukin 		return status;
76974fe6c29SRuslan Bukin 
77074fe6c29SRuslan Bukin 	/* We do not switch sections inside a block. */
77174fe6c29SRuslan Bukin 	if (insn.isid != block->isid) {
77274fe6c29SRuslan Bukin 		if (!pt_blk_block_is_empty(block))
77374fe6c29SRuslan Bukin 			return 0;
77474fe6c29SRuslan Bukin 
77574fe6c29SRuslan Bukin 		block->isid = insn.isid;
77674fe6c29SRuslan Bukin 	}
77774fe6c29SRuslan Bukin 
77874fe6c29SRuslan Bukin 	/* If we couldn't read @insn's memory in one chunk from @insn.isid, we
77974fe6c29SRuslan Bukin 	 * provide the memory in @block.
78074fe6c29SRuslan Bukin 	 */
78174fe6c29SRuslan Bukin 	if (insn.truncated) {
78274fe6c29SRuslan Bukin 		memcpy(block->raw, insn.raw, insn.size);
78374fe6c29SRuslan Bukin 		block->size = insn.size;
78474fe6c29SRuslan Bukin 		block->truncated = 1;
78574fe6c29SRuslan Bukin 	}
78674fe6c29SRuslan Bukin 
78774fe6c29SRuslan Bukin 	/* Log calls' return addresses for return compression. */
78874fe6c29SRuslan Bukin 	status = pt_blk_log_call(decoder, &insn, &iext);
78974fe6c29SRuslan Bukin 	if (status < 0)
79074fe6c29SRuslan Bukin 		return status;
79174fe6c29SRuslan Bukin 
79274fe6c29SRuslan Bukin 	/* We have a new instruction. */
79374fe6c29SRuslan Bukin 	block->iclass = insn.iclass;
79474fe6c29SRuslan Bukin 	block->end_ip = insn.ip;
79574fe6c29SRuslan Bukin 	block->ninsn = ninsn;
79674fe6c29SRuslan Bukin 
79774fe6c29SRuslan Bukin 	*pinsn = insn;
79874fe6c29SRuslan Bukin 	*piext = iext;
79974fe6c29SRuslan Bukin 
80074fe6c29SRuslan Bukin 	return 1;
80174fe6c29SRuslan Bukin }
80274fe6c29SRuslan Bukin 
80374fe6c29SRuslan Bukin 
80474fe6c29SRuslan Bukin /* Proceed to a particular type of instruction without using trace.
80574fe6c29SRuslan Bukin  *
80674fe6c29SRuslan Bukin  * Proceed until we reach an instruction for which @predicate returns a positive
80774fe6c29SRuslan Bukin  * integer or until:
80874fe6c29SRuslan Bukin  *
80974fe6c29SRuslan Bukin  *   - @predicate returns an error:  return error
81074fe6c29SRuslan Bukin  *   - @block is full:               return zero
81174fe6c29SRuslan Bukin  *   - @block would switch sections: return zero
81274fe6c29SRuslan Bukin  *   - we would need trace:          return -pte_bad_query
81374fe6c29SRuslan Bukin  *
81474fe6c29SRuslan Bukin  * Provide the last instruction that was reached in @insn and @iext.
81574fe6c29SRuslan Bukin  *
81674fe6c29SRuslan Bukin  * Update @decoder->ip to point to the last IP that was reached.  If we fail due
81774fe6c29SRuslan Bukin  * to lack of trace or if we reach a desired instruction, this is @insn->ip;
81874fe6c29SRuslan Bukin  * otherwise this is the next instruction's IP.
81974fe6c29SRuslan Bukin  *
82074fe6c29SRuslan Bukin  * Returns a positive integer if a suitable instruction was reached.
82174fe6c29SRuslan Bukin  * Returns zero if no such instruction was reached.
82274fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
82374fe6c29SRuslan Bukin  */
pt_blk_proceed_to_insn(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_insn * insn,struct pt_insn_ext * iext,int (* predicate)(const struct pt_insn *,const struct pt_insn_ext *))82474fe6c29SRuslan Bukin static int pt_blk_proceed_to_insn(struct pt_block_decoder *decoder,
82574fe6c29SRuslan Bukin 				  struct pt_block *block,
82674fe6c29SRuslan Bukin 				  struct pt_insn *insn,
82774fe6c29SRuslan Bukin 				  struct pt_insn_ext *iext,
82874fe6c29SRuslan Bukin 				  int (*predicate)(const struct pt_insn *,
82974fe6c29SRuslan Bukin 						   const struct pt_insn_ext *))
83074fe6c29SRuslan Bukin {
83174fe6c29SRuslan Bukin 	int status;
83274fe6c29SRuslan Bukin 
83374fe6c29SRuslan Bukin 	if (!decoder || !insn || !predicate)
83474fe6c29SRuslan Bukin 		return -pte_internal;
83574fe6c29SRuslan Bukin 
83674fe6c29SRuslan Bukin 	for (;;) {
83774fe6c29SRuslan Bukin 		status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
83874fe6c29SRuslan Bukin 		if (status <= 0)
83974fe6c29SRuslan Bukin 			return status;
84074fe6c29SRuslan Bukin 
84174fe6c29SRuslan Bukin 		/* We're done if this instruction matches the spec (positive
84274fe6c29SRuslan Bukin 		 * status) or we run into an error (negative status).
84374fe6c29SRuslan Bukin 		 */
84474fe6c29SRuslan Bukin 		status = predicate(insn, iext);
84574fe6c29SRuslan Bukin 		if (status != 0)
84674fe6c29SRuslan Bukin 			return status;
84774fe6c29SRuslan Bukin 
84874fe6c29SRuslan Bukin 		/* Let's see if we can proceed to the next IP without trace. */
84974fe6c29SRuslan Bukin 		status = pt_insn_next_ip(&decoder->ip, insn, iext);
85074fe6c29SRuslan Bukin 		if (status < 0)
85174fe6c29SRuslan Bukin 			return status;
85274fe6c29SRuslan Bukin 
85374fe6c29SRuslan Bukin 		/* End the block if the user asked us to.
85474fe6c29SRuslan Bukin 		 *
85574fe6c29SRuslan Bukin 		 * We only need to take care about direct near branches.
85674fe6c29SRuslan Bukin 		 * Indirect and far branches require trace and will naturally
85774fe6c29SRuslan Bukin 		 * end a block.
85874fe6c29SRuslan Bukin 		 */
85974fe6c29SRuslan Bukin 		if ((decoder->flags.variant.block.end_on_call &&
86074fe6c29SRuslan Bukin 		     (insn->iclass == ptic_call)) ||
86174fe6c29SRuslan Bukin 		    (decoder->flags.variant.block.end_on_jump &&
86274fe6c29SRuslan Bukin 		     (insn->iclass == ptic_jump)))
86374fe6c29SRuslan Bukin 			return 0;
86474fe6c29SRuslan Bukin 	}
86574fe6c29SRuslan Bukin }
86674fe6c29SRuslan Bukin 
86774fe6c29SRuslan Bukin /* Proceed to a particular IP without using trace.
86874fe6c29SRuslan Bukin  *
86974fe6c29SRuslan Bukin  * Proceed until we reach @ip or until:
87074fe6c29SRuslan Bukin  *
87174fe6c29SRuslan Bukin  *   - @block is full:               return zero
87274fe6c29SRuslan Bukin  *   - @block would switch sections: return zero
87374fe6c29SRuslan Bukin  *   - we would need trace:          return -pte_bad_query
87474fe6c29SRuslan Bukin  *
87574fe6c29SRuslan Bukin  * Provide the last instruction that was reached in @insn and @iext.  If we
87674fe6c29SRuslan Bukin  * reached @ip, this is the instruction preceding it.
87774fe6c29SRuslan Bukin  *
87874fe6c29SRuslan Bukin  * Update @decoder->ip to point to the last IP that was reached.  If we fail due
87974fe6c29SRuslan Bukin  * to lack of trace, this is @insn->ip; otherwise this is the next instruction's
88074fe6c29SRuslan Bukin  * IP.
88174fe6c29SRuslan Bukin  *
88274fe6c29SRuslan Bukin  * Returns a positive integer if @ip was reached.
88374fe6c29SRuslan Bukin  * Returns zero if no such instruction was reached.
88474fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
88574fe6c29SRuslan Bukin  */
pt_blk_proceed_to_ip(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_insn * insn,struct pt_insn_ext * iext,uint64_t ip)88674fe6c29SRuslan Bukin static int pt_blk_proceed_to_ip(struct pt_block_decoder *decoder,
88774fe6c29SRuslan Bukin 				struct pt_block *block, struct pt_insn *insn,
88874fe6c29SRuslan Bukin 				struct pt_insn_ext *iext, uint64_t ip)
88974fe6c29SRuslan Bukin {
89074fe6c29SRuslan Bukin 	int status;
89174fe6c29SRuslan Bukin 
89274fe6c29SRuslan Bukin 	if (!decoder || !insn)
89374fe6c29SRuslan Bukin 		return -pte_internal;
89474fe6c29SRuslan Bukin 
89574fe6c29SRuslan Bukin 	for (;;) {
89674fe6c29SRuslan Bukin 		/* We're done when we reach @ip.  We may not even have to decode
89774fe6c29SRuslan Bukin 		 * a single instruction in some cases.
89874fe6c29SRuslan Bukin 		 */
89974fe6c29SRuslan Bukin 		if (decoder->ip == ip)
90074fe6c29SRuslan Bukin 			return 1;
90174fe6c29SRuslan Bukin 
90274fe6c29SRuslan Bukin 		status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
90374fe6c29SRuslan Bukin 		if (status <= 0)
90474fe6c29SRuslan Bukin 			return status;
90574fe6c29SRuslan Bukin 
90674fe6c29SRuslan Bukin 		/* Let's see if we can proceed to the next IP without trace. */
90774fe6c29SRuslan Bukin 		status = pt_insn_next_ip(&decoder->ip, insn, iext);
90874fe6c29SRuslan Bukin 		if (status < 0)
90974fe6c29SRuslan Bukin 			return status;
91074fe6c29SRuslan Bukin 
91174fe6c29SRuslan Bukin 		/* End the block if the user asked us to.
91274fe6c29SRuslan Bukin 		 *
91374fe6c29SRuslan Bukin 		 * We only need to take care about direct near branches.
91474fe6c29SRuslan Bukin 		 * Indirect and far branches require trace and will naturally
91574fe6c29SRuslan Bukin 		 * end a block.
91674fe6c29SRuslan Bukin 		 *
91774fe6c29SRuslan Bukin 		 * The call at the end of the block may have reached @ip; make
91874fe6c29SRuslan Bukin 		 * sure to indicate that.
91974fe6c29SRuslan Bukin 		 */
92074fe6c29SRuslan Bukin 		if ((decoder->flags.variant.block.end_on_call &&
92174fe6c29SRuslan Bukin 		     (insn->iclass == ptic_call)) ||
92274fe6c29SRuslan Bukin 		    (decoder->flags.variant.block.end_on_jump &&
92374fe6c29SRuslan Bukin 		     (insn->iclass == ptic_jump))) {
92474fe6c29SRuslan Bukin 			return (decoder->ip == ip ? 1 : 0);
92574fe6c29SRuslan Bukin 		}
92674fe6c29SRuslan Bukin 	}
92774fe6c29SRuslan Bukin }
92874fe6c29SRuslan Bukin 
92974fe6c29SRuslan Bukin /* Proceed to a particular IP with trace, if necessary.
93074fe6c29SRuslan Bukin  *
93174fe6c29SRuslan Bukin  * Proceed until we reach @ip or until:
93274fe6c29SRuslan Bukin  *
93374fe6c29SRuslan Bukin  *   - @block is full:               return zero
93474fe6c29SRuslan Bukin  *   - @block would switch sections: return zero
93574fe6c29SRuslan Bukin  *   - we need trace:                return zero
93674fe6c29SRuslan Bukin  *
93774fe6c29SRuslan Bukin  * Update @decoder->ip to point to the last IP that was reached.
93874fe6c29SRuslan Bukin  *
93974fe6c29SRuslan Bukin  * A return of zero ends @block.
94074fe6c29SRuslan Bukin  *
94174fe6c29SRuslan Bukin  * Returns a positive integer if @ip was reached.
94274fe6c29SRuslan Bukin  * Returns zero if no such instruction was reached.
94374fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
94474fe6c29SRuslan Bukin  */
pt_blk_proceed_to_ip_with_trace(struct pt_block_decoder * decoder,struct pt_block * block,uint64_t ip)94574fe6c29SRuslan Bukin static int pt_blk_proceed_to_ip_with_trace(struct pt_block_decoder *decoder,
94674fe6c29SRuslan Bukin 					   struct pt_block *block,
94774fe6c29SRuslan Bukin 					   uint64_t ip)
94874fe6c29SRuslan Bukin {
94974fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
95074fe6c29SRuslan Bukin 	struct pt_insn insn;
95174fe6c29SRuslan Bukin 	int status;
95274fe6c29SRuslan Bukin 
95374fe6c29SRuslan Bukin 	/* Try to reach @ip without trace.
95474fe6c29SRuslan Bukin 	 *
95574fe6c29SRuslan Bukin 	 * We're also OK if @block overflowed or we switched sections and we
95674fe6c29SRuslan Bukin 	 * have to try again in the next iteration.
95774fe6c29SRuslan Bukin 	 */
95874fe6c29SRuslan Bukin 	status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext, ip);
95974fe6c29SRuslan Bukin 	if (status != -pte_bad_query)
96074fe6c29SRuslan Bukin 		return status;
96174fe6c29SRuslan Bukin 
96274fe6c29SRuslan Bukin 	/* Needing trace is not an error.  We use trace to determine the next
96374fe6c29SRuslan Bukin 	 * start IP and end the block.
96474fe6c29SRuslan Bukin 	 */
96574fe6c29SRuslan Bukin 	return pt_blk_proceed_with_trace(decoder, &insn, &iext);
96674fe6c29SRuslan Bukin }
96774fe6c29SRuslan Bukin 
pt_insn_skl014(const struct pt_insn * insn,const struct pt_insn_ext * iext)96874fe6c29SRuslan Bukin static int pt_insn_skl014(const struct pt_insn *insn,
96974fe6c29SRuslan Bukin 			  const struct pt_insn_ext *iext)
97074fe6c29SRuslan Bukin {
97174fe6c29SRuslan Bukin 	if (!insn || !iext)
97274fe6c29SRuslan Bukin 		return 0;
97374fe6c29SRuslan Bukin 
97474fe6c29SRuslan Bukin 	switch (insn->iclass) {
97574fe6c29SRuslan Bukin 	default:
97674fe6c29SRuslan Bukin 		return 0;
97774fe6c29SRuslan Bukin 
97874fe6c29SRuslan Bukin 	case ptic_call:
97974fe6c29SRuslan Bukin 	case ptic_jump:
98074fe6c29SRuslan Bukin 		return iext->variant.branch.is_direct;
98174fe6c29SRuslan Bukin 
98274fe6c29SRuslan Bukin 	case ptic_other:
98374fe6c29SRuslan Bukin 		return pt_insn_changes_cr3(insn, iext);
98474fe6c29SRuslan Bukin 	}
98574fe6c29SRuslan Bukin }
98674fe6c29SRuslan Bukin 
98774fe6c29SRuslan Bukin /* Proceed to the location of a synchronous disabled event with suppressed IP
98874fe6c29SRuslan Bukin  * considering SKL014.
98974fe6c29SRuslan Bukin  *
99074fe6c29SRuslan Bukin  * We have a (synchronous) disabled event pending.  Proceed to the event
99174fe6c29SRuslan Bukin  * location and indicate whether we were able to reach it.
99274fe6c29SRuslan Bukin  *
99374fe6c29SRuslan Bukin  * With SKL014 a TIP.PGD with suppressed IP may also be generated by a direct
99474fe6c29SRuslan Bukin  * unconditional branch that clears FilterEn by jumping out of a filter region
99574fe6c29SRuslan Bukin  * or into a TraceStop region.  Use the filter configuration to determine the
99674fe6c29SRuslan Bukin  * exact branch the event binds to.
99774fe6c29SRuslan Bukin  *
99874fe6c29SRuslan Bukin  * The last instruction that was reached is stored in @insn/@iext.
99974fe6c29SRuslan Bukin  *
100074fe6c29SRuslan Bukin  * Returns a positive integer if the event location was reached.
100174fe6c29SRuslan Bukin  * Returns zero if the event location was not reached.
100274fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
100374fe6c29SRuslan Bukin  */
pt_blk_proceed_skl014(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_insn * insn,struct pt_insn_ext * iext)100474fe6c29SRuslan Bukin static int pt_blk_proceed_skl014(struct pt_block_decoder *decoder,
100574fe6c29SRuslan Bukin 				 struct pt_block *block, struct pt_insn *insn,
100674fe6c29SRuslan Bukin 				 struct pt_insn_ext *iext)
100774fe6c29SRuslan Bukin {
100874fe6c29SRuslan Bukin 	const struct pt_conf_addr_filter *addr_filter;
100974fe6c29SRuslan Bukin 	int status;
101074fe6c29SRuslan Bukin 
101174fe6c29SRuslan Bukin 	if (!decoder || !block || !insn || !iext)
101274fe6c29SRuslan Bukin 		return -pte_internal;
101374fe6c29SRuslan Bukin 
101474fe6c29SRuslan Bukin 	addr_filter = &decoder->query.config.addr_filter;
101574fe6c29SRuslan Bukin 	for (;;) {
101674fe6c29SRuslan Bukin 		uint64_t ip;
101774fe6c29SRuslan Bukin 
101874fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_insn(decoder, block, insn, iext,
101974fe6c29SRuslan Bukin 						pt_insn_skl014);
102074fe6c29SRuslan Bukin 		if (status <= 0)
102174fe6c29SRuslan Bukin 			break;
102274fe6c29SRuslan Bukin 
102374fe6c29SRuslan Bukin 		/* The erratum doesn't apply if we can bind the event to a
102474fe6c29SRuslan Bukin 		 * CR3-changing instruction.
102574fe6c29SRuslan Bukin 		 */
102674fe6c29SRuslan Bukin 		if (pt_insn_changes_cr3(insn, iext))
102774fe6c29SRuslan Bukin 			break;
102874fe6c29SRuslan Bukin 
102974fe6c29SRuslan Bukin 		/* Check the filter against the branch target. */
103074fe6c29SRuslan Bukin 		status = pt_insn_next_ip(&ip, insn, iext);
103174fe6c29SRuslan Bukin 		if (status < 0)
103274fe6c29SRuslan Bukin 			break;
103374fe6c29SRuslan Bukin 
103474fe6c29SRuslan Bukin 		status = pt_filter_addr_check(addr_filter, ip);
103574fe6c29SRuslan Bukin 		if (status <= 0) {
103674fe6c29SRuslan Bukin 			/* We need to flip the indication.
103774fe6c29SRuslan Bukin 			 *
103874fe6c29SRuslan Bukin 			 * We reached the event location when @ip lies inside a
103974fe6c29SRuslan Bukin 			 * tracing-disabled region.
104074fe6c29SRuslan Bukin 			 */
104174fe6c29SRuslan Bukin 			if (!status)
104274fe6c29SRuslan Bukin 				status = 1;
104374fe6c29SRuslan Bukin 
104474fe6c29SRuslan Bukin 			break;
104574fe6c29SRuslan Bukin 		}
104674fe6c29SRuslan Bukin 
104774fe6c29SRuslan Bukin 		/* This is not the correct instruction.  Proceed past it and try
104874fe6c29SRuslan Bukin 		 * again.
104974fe6c29SRuslan Bukin 		 */
105074fe6c29SRuslan Bukin 		decoder->ip = ip;
105174fe6c29SRuslan Bukin 
105274fe6c29SRuslan Bukin 		/* End the block if the user asked us to.
105374fe6c29SRuslan Bukin 		 *
105474fe6c29SRuslan Bukin 		 * We only need to take care about direct near branches.
105574fe6c29SRuslan Bukin 		 * Indirect and far branches require trace and will naturally
105674fe6c29SRuslan Bukin 		 * end a block.
105774fe6c29SRuslan Bukin 		 */
105874fe6c29SRuslan Bukin 		if ((decoder->flags.variant.block.end_on_call &&
105974fe6c29SRuslan Bukin 		    (insn->iclass == ptic_call)) ||
106074fe6c29SRuslan Bukin 		    (decoder->flags.variant.block.end_on_jump &&
106174fe6c29SRuslan Bukin 		    (insn->iclass == ptic_jump)))
106274fe6c29SRuslan Bukin 			break;
106374fe6c29SRuslan Bukin 	}
106474fe6c29SRuslan Bukin 
106574fe6c29SRuslan Bukin 	return status;
106674fe6c29SRuslan Bukin }
106774fe6c29SRuslan Bukin 
106874fe6c29SRuslan Bukin /* Proceed to the event location for a disabled event.
106974fe6c29SRuslan Bukin  *
107074fe6c29SRuslan Bukin  * We have a (synchronous) disabled event pending.  Proceed to the event
107174fe6c29SRuslan Bukin  * location and indicate whether we were able to reach it.
107274fe6c29SRuslan Bukin  *
107374fe6c29SRuslan Bukin  * The last instruction that was reached is stored in @insn/@iext.
107474fe6c29SRuslan Bukin  *
107574fe6c29SRuslan Bukin  * Returns a positive integer if the event location was reached.
107674fe6c29SRuslan Bukin  * Returns zero if the event location was not reached.
107774fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
107874fe6c29SRuslan Bukin  */
pt_blk_proceed_to_disabled(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_insn * insn,struct pt_insn_ext * iext,const struct pt_event * ev)107974fe6c29SRuslan Bukin static int pt_blk_proceed_to_disabled(struct pt_block_decoder *decoder,
108074fe6c29SRuslan Bukin 				      struct pt_block *block,
108174fe6c29SRuslan Bukin 				      struct pt_insn *insn,
108274fe6c29SRuslan Bukin 				      struct pt_insn_ext *iext,
108374fe6c29SRuslan Bukin 				      const struct pt_event *ev)
108474fe6c29SRuslan Bukin {
108574fe6c29SRuslan Bukin 	if (!decoder || !block || !ev)
108674fe6c29SRuslan Bukin 		return -pte_internal;
108774fe6c29SRuslan Bukin 
108874fe6c29SRuslan Bukin 	if (ev->ip_suppressed) {
108974fe6c29SRuslan Bukin 		/* Due to SKL014 the TIP.PGD payload may be suppressed also for
109074fe6c29SRuslan Bukin 		 * direct branches.
109174fe6c29SRuslan Bukin 		 *
109274fe6c29SRuslan Bukin 		 * If we don't have a filter configuration we assume that no
109374fe6c29SRuslan Bukin 		 * address filters were used and the erratum does not apply.
109474fe6c29SRuslan Bukin 		 *
109574fe6c29SRuslan Bukin 		 * We might otherwise disable tracing too early.
109674fe6c29SRuslan Bukin 		 */
109774fe6c29SRuslan Bukin 		if (decoder->query.config.addr_filter.config.addr_cfg &&
109874fe6c29SRuslan Bukin 		    decoder->query.config.errata.skl014)
109974fe6c29SRuslan Bukin 			return pt_blk_proceed_skl014(decoder, block, insn,
110074fe6c29SRuslan Bukin 						     iext);
110174fe6c29SRuslan Bukin 
110274fe6c29SRuslan Bukin 		/* A synchronous disabled event also binds to far branches and
110374fe6c29SRuslan Bukin 		 * CPL-changing instructions.  Both would require trace,
110474fe6c29SRuslan Bukin 		 * however, and are thus implicitly handled by erroring out.
110574fe6c29SRuslan Bukin 		 *
110674fe6c29SRuslan Bukin 		 * The would-require-trace error is handled by our caller.
110774fe6c29SRuslan Bukin 		 */
110874fe6c29SRuslan Bukin 		return pt_blk_proceed_to_insn(decoder, block, insn, iext,
110974fe6c29SRuslan Bukin 					      pt_insn_changes_cr3);
111074fe6c29SRuslan Bukin 	} else
111174fe6c29SRuslan Bukin 		return pt_blk_proceed_to_ip(decoder, block, insn, iext,
111274fe6c29SRuslan Bukin 					    ev->variant.disabled.ip);
111374fe6c29SRuslan Bukin }
111474fe6c29SRuslan Bukin 
111574fe6c29SRuslan Bukin /* Set the expected resume address for a synchronous disable.
111674fe6c29SRuslan Bukin  *
111774fe6c29SRuslan Bukin  * On a synchronous disable, @decoder->ip still points to the instruction to
111874fe6c29SRuslan Bukin  * which the event bound.  That's not where we expect tracing to resume.
111974fe6c29SRuslan Bukin  *
112074fe6c29SRuslan Bukin  * For calls, a fair assumption is that tracing resumes after returning from the
112174fe6c29SRuslan Bukin  * called function.  For other types of instructions, we simply don't know.
112274fe6c29SRuslan Bukin  *
112374fe6c29SRuslan Bukin  * Returns zero on success, a negative pt_error_code otherwise.
112474fe6c29SRuslan Bukin  */
pt_blk_set_disable_resume_ip(struct pt_block_decoder * decoder,const struct pt_insn * insn)112574fe6c29SRuslan Bukin static int pt_blk_set_disable_resume_ip(struct pt_block_decoder *decoder,
112674fe6c29SRuslan Bukin 					const struct pt_insn *insn)
112774fe6c29SRuslan Bukin {
112874fe6c29SRuslan Bukin 	if (!decoder || !insn)
112974fe6c29SRuslan Bukin 		return -pte_internal;
113074fe6c29SRuslan Bukin 
113174fe6c29SRuslan Bukin 	switch (insn->iclass) {
113274fe6c29SRuslan Bukin 	case ptic_call:
113374fe6c29SRuslan Bukin 	case ptic_far_call:
113474fe6c29SRuslan Bukin 		decoder->ip = insn->ip + insn->size;
113574fe6c29SRuslan Bukin 		break;
113674fe6c29SRuslan Bukin 
113774fe6c29SRuslan Bukin 	default:
113874fe6c29SRuslan Bukin 		decoder->ip = 0ull;
113974fe6c29SRuslan Bukin 		break;
114074fe6c29SRuslan Bukin 	}
114174fe6c29SRuslan Bukin 
114274fe6c29SRuslan Bukin 	return 0;
114374fe6c29SRuslan Bukin }
114474fe6c29SRuslan Bukin 
114574fe6c29SRuslan Bukin /* Proceed to the event location for an async paging event.
114674fe6c29SRuslan Bukin  *
114774fe6c29SRuslan Bukin  * We have an async paging event pending.  Proceed to the event location and
114874fe6c29SRuslan Bukin  * indicate whether we were able to reach it.  Needing trace in order to proceed
114974fe6c29SRuslan Bukin  * is not an error in this case but ends the block.
115074fe6c29SRuslan Bukin  *
115174fe6c29SRuslan Bukin  * Returns a positive integer if the event location was reached.
115274fe6c29SRuslan Bukin  * Returns zero if the event location was not reached.
115374fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
115474fe6c29SRuslan Bukin  */
pt_blk_proceed_to_async_paging(struct pt_block_decoder * decoder,struct pt_block * block,const struct pt_event * ev)115574fe6c29SRuslan Bukin static int pt_blk_proceed_to_async_paging(struct pt_block_decoder *decoder,
115674fe6c29SRuslan Bukin 					  struct pt_block *block,
115774fe6c29SRuslan Bukin 					  const struct pt_event *ev)
115874fe6c29SRuslan Bukin {
115974fe6c29SRuslan Bukin 	int status;
116074fe6c29SRuslan Bukin 
116174fe6c29SRuslan Bukin 	if (!decoder || !ev)
116274fe6c29SRuslan Bukin 		return -pte_internal;
116374fe6c29SRuslan Bukin 
116474fe6c29SRuslan Bukin 	/* Apply the event immediately if we don't have an IP. */
116574fe6c29SRuslan Bukin 	if (ev->ip_suppressed)
116674fe6c29SRuslan Bukin 		return 1;
116774fe6c29SRuslan Bukin 
116874fe6c29SRuslan Bukin 	status = pt_blk_proceed_to_ip_with_trace(decoder, block,
116974fe6c29SRuslan Bukin 						 ev->variant.async_paging.ip);
117074fe6c29SRuslan Bukin 	if (status < 0)
117174fe6c29SRuslan Bukin 		return status;
117274fe6c29SRuslan Bukin 
117374fe6c29SRuslan Bukin 	/* We may have reached the IP. */
117474fe6c29SRuslan Bukin 	return (decoder->ip == ev->variant.async_paging.ip ? 1 : 0);
117574fe6c29SRuslan Bukin }
117674fe6c29SRuslan Bukin 
117774fe6c29SRuslan Bukin /* Proceed to the event location for an async vmcs event.
117874fe6c29SRuslan Bukin  *
117974fe6c29SRuslan Bukin  * We have an async vmcs event pending.  Proceed to the event location and
118074fe6c29SRuslan Bukin  * indicate whether we were able to reach it.  Needing trace in order to proceed
118174fe6c29SRuslan Bukin  * is not an error in this case but ends the block.
118274fe6c29SRuslan Bukin  *
118374fe6c29SRuslan Bukin  * Returns a positive integer if the event location was reached.
118474fe6c29SRuslan Bukin  * Returns zero if the event location was not reached.
118574fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
118674fe6c29SRuslan Bukin  */
pt_blk_proceed_to_async_vmcs(struct pt_block_decoder * decoder,struct pt_block * block,const struct pt_event * ev)118774fe6c29SRuslan Bukin static int pt_blk_proceed_to_async_vmcs(struct pt_block_decoder *decoder,
118874fe6c29SRuslan Bukin 					struct pt_block *block,
118974fe6c29SRuslan Bukin 					const struct pt_event *ev)
119074fe6c29SRuslan Bukin {
119174fe6c29SRuslan Bukin 	int status;
119274fe6c29SRuslan Bukin 
119374fe6c29SRuslan Bukin 	if (!decoder || !ev)
119474fe6c29SRuslan Bukin 		return -pte_internal;
119574fe6c29SRuslan Bukin 
119674fe6c29SRuslan Bukin 	/* Apply the event immediately if we don't have an IP. */
119774fe6c29SRuslan Bukin 	if (ev->ip_suppressed)
119874fe6c29SRuslan Bukin 		return 1;
119974fe6c29SRuslan Bukin 
120074fe6c29SRuslan Bukin 	status = pt_blk_proceed_to_ip_with_trace(decoder, block,
120174fe6c29SRuslan Bukin 						 ev->variant.async_vmcs.ip);
120274fe6c29SRuslan Bukin 	if (status < 0)
120374fe6c29SRuslan Bukin 		return status;
120474fe6c29SRuslan Bukin 
120574fe6c29SRuslan Bukin 	/* We may have reached the IP. */
120674fe6c29SRuslan Bukin 	return (decoder->ip == ev->variant.async_vmcs.ip ? 1 : 0);
120774fe6c29SRuslan Bukin }
120874fe6c29SRuslan Bukin 
120974fe6c29SRuslan Bukin /* Proceed to the event location for an exec mode event.
121074fe6c29SRuslan Bukin  *
121174fe6c29SRuslan Bukin  * We have an exec mode event pending.  Proceed to the event location and
121274fe6c29SRuslan Bukin  * indicate whether we were able to reach it.  Needing trace in order to proceed
121374fe6c29SRuslan Bukin  * is not an error in this case but ends the block.
121474fe6c29SRuslan Bukin  *
121574fe6c29SRuslan Bukin  * Returns a positive integer if the event location was reached.
121674fe6c29SRuslan Bukin  * Returns zero if the event location was not reached.
121774fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
121874fe6c29SRuslan Bukin  */
pt_blk_proceed_to_exec_mode(struct pt_block_decoder * decoder,struct pt_block * block,const struct pt_event * ev)121974fe6c29SRuslan Bukin static int pt_blk_proceed_to_exec_mode(struct pt_block_decoder *decoder,
122074fe6c29SRuslan Bukin 				       struct pt_block *block,
122174fe6c29SRuslan Bukin 				       const struct pt_event *ev)
122274fe6c29SRuslan Bukin {
122374fe6c29SRuslan Bukin 	int status;
122474fe6c29SRuslan Bukin 
122574fe6c29SRuslan Bukin 	if (!decoder || !ev)
122674fe6c29SRuslan Bukin 		return -pte_internal;
122774fe6c29SRuslan Bukin 
122874fe6c29SRuslan Bukin 	/* Apply the event immediately if we don't have an IP. */
122974fe6c29SRuslan Bukin 	if (ev->ip_suppressed)
123074fe6c29SRuslan Bukin 		return 1;
123174fe6c29SRuslan Bukin 
123274fe6c29SRuslan Bukin 	status = pt_blk_proceed_to_ip_with_trace(decoder, block,
123374fe6c29SRuslan Bukin 						 ev->variant.exec_mode.ip);
123474fe6c29SRuslan Bukin 	if (status < 0)
123574fe6c29SRuslan Bukin 		return status;
123674fe6c29SRuslan Bukin 
123774fe6c29SRuslan Bukin 	/* We may have reached the IP. */
123874fe6c29SRuslan Bukin 	return (decoder->ip == ev->variant.exec_mode.ip ? 1 : 0);
123974fe6c29SRuslan Bukin }
124074fe6c29SRuslan Bukin 
124174fe6c29SRuslan Bukin /* Proceed to the event location for a ptwrite event.
124274fe6c29SRuslan Bukin  *
124374fe6c29SRuslan Bukin  * We have a ptwrite event pending.  Proceed to the event location and indicate
124474fe6c29SRuslan Bukin  * whether we were able to reach it.
124574fe6c29SRuslan Bukin  *
124674fe6c29SRuslan Bukin  * In case of the event binding to a ptwrite instruction, we pass beyond that
124774fe6c29SRuslan Bukin  * instruction and update the event to provide the instruction's IP.
124874fe6c29SRuslan Bukin  *
124974fe6c29SRuslan Bukin  * In the case of the event binding to an IP provided in the event, we move
125074fe6c29SRuslan Bukin  * beyond the instruction at that IP.
125174fe6c29SRuslan Bukin  *
125274fe6c29SRuslan Bukin  * Returns a positive integer if the event location was reached.
125374fe6c29SRuslan Bukin  * Returns zero if the event location was not reached.
125474fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
125574fe6c29SRuslan Bukin  */
pt_blk_proceed_to_ptwrite(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_insn * insn,struct pt_insn_ext * iext,struct pt_event * ev)125674fe6c29SRuslan Bukin static int pt_blk_proceed_to_ptwrite(struct pt_block_decoder *decoder,
125774fe6c29SRuslan Bukin 				     struct pt_block *block,
125874fe6c29SRuslan Bukin 				     struct pt_insn *insn,
125974fe6c29SRuslan Bukin 				     struct pt_insn_ext *iext,
126074fe6c29SRuslan Bukin 				     struct pt_event *ev)
126174fe6c29SRuslan Bukin {
126274fe6c29SRuslan Bukin 	int status;
126374fe6c29SRuslan Bukin 
126474fe6c29SRuslan Bukin 	if (!insn || !ev)
126574fe6c29SRuslan Bukin 		return -pte_internal;
126674fe6c29SRuslan Bukin 
126774fe6c29SRuslan Bukin 	/* If we don't have an IP, the event binds to the next PTWRITE
126874fe6c29SRuslan Bukin 	 * instruction.
126974fe6c29SRuslan Bukin 	 *
127074fe6c29SRuslan Bukin 	 * If we have an IP it still binds to the next PTWRITE instruction but
127174fe6c29SRuslan Bukin 	 * now the IP tells us where that instruction is.  This makes most sense
127274fe6c29SRuslan Bukin 	 * when tracing is disabled and we don't have any other means of finding
127374fe6c29SRuslan Bukin 	 * the PTWRITE instruction.  We nevertheless distinguish the two cases,
127474fe6c29SRuslan Bukin 	 * here.
127574fe6c29SRuslan Bukin 	 *
127674fe6c29SRuslan Bukin 	 * In both cases, we move beyond the PTWRITE instruction, so it will be
127774fe6c29SRuslan Bukin 	 * the last instruction in the current block and @decoder->ip will point
127874fe6c29SRuslan Bukin 	 * to the instruction following it.
127974fe6c29SRuslan Bukin 	 */
128074fe6c29SRuslan Bukin 	if (ev->ip_suppressed) {
128174fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_insn(decoder, block, insn, iext,
128274fe6c29SRuslan Bukin 						pt_insn_is_ptwrite);
128374fe6c29SRuslan Bukin 		if (status <= 0)
128474fe6c29SRuslan Bukin 			return status;
128574fe6c29SRuslan Bukin 
128674fe6c29SRuslan Bukin 		/* We now know the IP of the PTWRITE instruction corresponding
128774fe6c29SRuslan Bukin 		 * to this event.  Fill it in to make it more convenient for the
128874fe6c29SRuslan Bukin 		 * user to process the event.
128974fe6c29SRuslan Bukin 		 */
129074fe6c29SRuslan Bukin 		ev->variant.ptwrite.ip = insn->ip;
129174fe6c29SRuslan Bukin 		ev->ip_suppressed = 0;
129274fe6c29SRuslan Bukin 	} else {
129374fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ip(decoder, block, insn, iext,
129474fe6c29SRuslan Bukin 					      ev->variant.ptwrite.ip);
129574fe6c29SRuslan Bukin 		if (status <= 0)
129674fe6c29SRuslan Bukin 			return status;
129774fe6c29SRuslan Bukin 
129874fe6c29SRuslan Bukin 		/* We reached the PTWRITE instruction and @decoder->ip points to
129974fe6c29SRuslan Bukin 		 * it; @insn/@iext still contain the preceding instruction.
130074fe6c29SRuslan Bukin 		 *
130174fe6c29SRuslan Bukin 		 * Proceed beyond the PTWRITE to account for it.  Note that we
130274fe6c29SRuslan Bukin 		 * may still overflow the block, which would cause us to
130374fe6c29SRuslan Bukin 		 * postpone both instruction and event to the next block.
130474fe6c29SRuslan Bukin 		 */
130574fe6c29SRuslan Bukin 		status = pt_blk_proceed_one_insn(decoder, block, insn, iext);
130674fe6c29SRuslan Bukin 		if (status <= 0)
130774fe6c29SRuslan Bukin 			return status;
130874fe6c29SRuslan Bukin 	}
130974fe6c29SRuslan Bukin 
131074fe6c29SRuslan Bukin 	return 1;
131174fe6c29SRuslan Bukin }
131274fe6c29SRuslan Bukin 
131374fe6c29SRuslan Bukin /* Try to work around erratum SKD022.
131474fe6c29SRuslan Bukin  *
131574fe6c29SRuslan Bukin  * If we get an asynchronous disable on VMLAUNCH or VMRESUME, the FUP that
131674fe6c29SRuslan Bukin  * caused the disable to be asynchronous might have been bogous.
131774fe6c29SRuslan Bukin  *
131874fe6c29SRuslan Bukin  * Returns a positive integer if the erratum has been handled.
131974fe6c29SRuslan Bukin  * Returns zero if the erratum does not apply.
132074fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
132174fe6c29SRuslan Bukin  */
pt_blk_handle_erratum_skd022(struct pt_block_decoder * decoder,struct pt_event * ev)132274fe6c29SRuslan Bukin static int pt_blk_handle_erratum_skd022(struct pt_block_decoder *decoder,
132374fe6c29SRuslan Bukin 					struct pt_event *ev)
132474fe6c29SRuslan Bukin {
132574fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
132674fe6c29SRuslan Bukin 	struct pt_insn insn;
132774fe6c29SRuslan Bukin 	int errcode;
132874fe6c29SRuslan Bukin 
132974fe6c29SRuslan Bukin 	if (!decoder || !ev)
133074fe6c29SRuslan Bukin 		return -pte_internal;
133174fe6c29SRuslan Bukin 
133274fe6c29SRuslan Bukin 	insn.mode = decoder->mode;
133374fe6c29SRuslan Bukin 	insn.ip = ev->variant.async_disabled.at;
133474fe6c29SRuslan Bukin 
133574fe6c29SRuslan Bukin 	errcode = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
133674fe6c29SRuslan Bukin 	if (errcode < 0)
133774fe6c29SRuslan Bukin 		return 0;
133874fe6c29SRuslan Bukin 
133974fe6c29SRuslan Bukin 	switch (iext.iclass) {
134074fe6c29SRuslan Bukin 	default:
134174fe6c29SRuslan Bukin 		/* The erratum does not apply. */
134274fe6c29SRuslan Bukin 		return 0;
134374fe6c29SRuslan Bukin 
134474fe6c29SRuslan Bukin 	case PTI_INST_VMLAUNCH:
134574fe6c29SRuslan Bukin 	case PTI_INST_VMRESUME:
134674fe6c29SRuslan Bukin 		/* The erratum may apply.  We can't be sure without a lot more
134774fe6c29SRuslan Bukin 		 * analysis.  Let's assume it does.
134874fe6c29SRuslan Bukin 		 *
134974fe6c29SRuslan Bukin 		 * We turn the async disable into a sync disable.  Our caller
135074fe6c29SRuslan Bukin 		 * will restart event processing.
135174fe6c29SRuslan Bukin 		 */
135274fe6c29SRuslan Bukin 		ev->type = ptev_disabled;
135374fe6c29SRuslan Bukin 		ev->variant.disabled.ip = ev->variant.async_disabled.ip;
135474fe6c29SRuslan Bukin 
135574fe6c29SRuslan Bukin 		return 1;
135674fe6c29SRuslan Bukin 	}
135774fe6c29SRuslan Bukin }
135874fe6c29SRuslan Bukin 
135974fe6c29SRuslan Bukin /* Postpone proceeding past @insn/@iext and indicate a pending event.
136074fe6c29SRuslan Bukin  *
136174fe6c29SRuslan Bukin  * There may be further events pending on @insn/@iext.  Postpone proceeding past
136274fe6c29SRuslan Bukin  * @insn/@iext until we processed all events that bind to it.
136374fe6c29SRuslan Bukin  *
136474fe6c29SRuslan Bukin  * Returns a non-negative pt_status_flag bit-vector indicating a pending event
136574fe6c29SRuslan Bukin  * on success, a negative pt_error_code otherwise.
136674fe6c29SRuslan Bukin  */
pt_blk_postpone_insn(struct pt_block_decoder * decoder,const struct pt_insn * insn,const struct pt_insn_ext * iext)136774fe6c29SRuslan Bukin static int pt_blk_postpone_insn(struct pt_block_decoder *decoder,
136874fe6c29SRuslan Bukin 				const struct pt_insn *insn,
136974fe6c29SRuslan Bukin 				const struct pt_insn_ext *iext)
137074fe6c29SRuslan Bukin {
137174fe6c29SRuslan Bukin 	if (!decoder || !insn || !iext)
137274fe6c29SRuslan Bukin 		return -pte_internal;
137374fe6c29SRuslan Bukin 
137474fe6c29SRuslan Bukin 	/* Only one can be active. */
137574fe6c29SRuslan Bukin 	if (decoder->process_insn)
137674fe6c29SRuslan Bukin 		return -pte_internal;
137774fe6c29SRuslan Bukin 
137874fe6c29SRuslan Bukin 	decoder->process_insn = 1;
137974fe6c29SRuslan Bukin 	decoder->insn = *insn;
138074fe6c29SRuslan Bukin 	decoder->iext = *iext;
138174fe6c29SRuslan Bukin 
138274fe6c29SRuslan Bukin 	return pt_blk_status(decoder, pts_event_pending);
138374fe6c29SRuslan Bukin }
138474fe6c29SRuslan Bukin 
138574fe6c29SRuslan Bukin /* Remove any postponed instruction from @decoder.
138674fe6c29SRuslan Bukin  *
138774fe6c29SRuslan Bukin  * Returns zero on success, a negative pt_error_code otherwise.
138874fe6c29SRuslan Bukin  */
pt_blk_clear_postponed_insn(struct pt_block_decoder * decoder)138974fe6c29SRuslan Bukin static int pt_blk_clear_postponed_insn(struct pt_block_decoder *decoder)
139074fe6c29SRuslan Bukin {
139174fe6c29SRuslan Bukin 	if (!decoder)
139274fe6c29SRuslan Bukin 		return -pte_internal;
139374fe6c29SRuslan Bukin 
139474fe6c29SRuslan Bukin 	decoder->process_insn = 0;
139574fe6c29SRuslan Bukin 	decoder->bound_paging = 0;
139674fe6c29SRuslan Bukin 	decoder->bound_vmcs = 0;
139774fe6c29SRuslan Bukin 	decoder->bound_ptwrite = 0;
139874fe6c29SRuslan Bukin 
139974fe6c29SRuslan Bukin 	return 0;
140074fe6c29SRuslan Bukin }
140174fe6c29SRuslan Bukin 
140274fe6c29SRuslan Bukin /* Proceed past a postponed instruction.
140374fe6c29SRuslan Bukin  *
140474fe6c29SRuslan Bukin  * If an instruction has been postponed in @decoder, proceed past it.
140574fe6c29SRuslan Bukin  *
140674fe6c29SRuslan Bukin  * Returns zero on success, a negative pt_error_code otherwise.
140774fe6c29SRuslan Bukin  */
pt_blk_proceed_postponed_insn(struct pt_block_decoder * decoder)140874fe6c29SRuslan Bukin static int pt_blk_proceed_postponed_insn(struct pt_block_decoder *decoder)
140974fe6c29SRuslan Bukin {
141074fe6c29SRuslan Bukin 	int status;
141174fe6c29SRuslan Bukin 
141274fe6c29SRuslan Bukin 	if (!decoder)
141374fe6c29SRuslan Bukin 		return -pte_internal;
141474fe6c29SRuslan Bukin 
141574fe6c29SRuslan Bukin 	/* There's nothing to do if we have no postponed instruction. */
141674fe6c29SRuslan Bukin 	if (!decoder->process_insn)
141774fe6c29SRuslan Bukin 		return 0;
141874fe6c29SRuslan Bukin 
141974fe6c29SRuslan Bukin 	/* There's nothing to do if tracing got disabled. */
142074fe6c29SRuslan Bukin 	if (!decoder->enabled)
142174fe6c29SRuslan Bukin 		return pt_blk_clear_postponed_insn(decoder);
142274fe6c29SRuslan Bukin 
142374fe6c29SRuslan Bukin 	status = pt_insn_next_ip(&decoder->ip, &decoder->insn, &decoder->iext);
142474fe6c29SRuslan Bukin 	if (status < 0) {
142574fe6c29SRuslan Bukin 		if (status != -pte_bad_query)
142674fe6c29SRuslan Bukin 			return status;
142774fe6c29SRuslan Bukin 
142874fe6c29SRuslan Bukin 		status = pt_blk_proceed_with_trace(decoder, &decoder->insn,
142974fe6c29SRuslan Bukin 						   &decoder->iext);
143074fe6c29SRuslan Bukin 		if (status < 0)
143174fe6c29SRuslan Bukin 			return status;
143274fe6c29SRuslan Bukin 	}
143374fe6c29SRuslan Bukin 
143474fe6c29SRuslan Bukin 	return pt_blk_clear_postponed_insn(decoder);
143574fe6c29SRuslan Bukin }
143674fe6c29SRuslan Bukin 
143774fe6c29SRuslan Bukin /* Proceed to the next event.
143874fe6c29SRuslan Bukin  *
143974fe6c29SRuslan Bukin  * We have an event pending.  Proceed to the event location and indicate the
144074fe6c29SRuslan Bukin  * event to the user.
144174fe6c29SRuslan Bukin  *
144274fe6c29SRuslan Bukin  * On our way to the event location we may also be forced to postpone the event
144374fe6c29SRuslan Bukin  * to the next block, e.g. if we overflow the number of instructions in the
144474fe6c29SRuslan Bukin  * block or if we need trace in order to reach the event location.
144574fe6c29SRuslan Bukin  *
144674fe6c29SRuslan Bukin  * If we're not able to reach the event location, we return zero.  This is what
144774fe6c29SRuslan Bukin  * pt_blk_status() would return since:
144874fe6c29SRuslan Bukin  *
144974fe6c29SRuslan Bukin  *   - we suppress pts_eos as long as we're processing events
145074fe6c29SRuslan Bukin  *   - we do not set pts_ip_suppressed since tracing must be enabled
145174fe6c29SRuslan Bukin  *
145274fe6c29SRuslan Bukin  * Returns a non-negative pt_status_flag bit-vector on success, a negative error
145374fe6c29SRuslan Bukin  * code otherwise.
145474fe6c29SRuslan Bukin  */
pt_blk_proceed_event(struct pt_block_decoder * decoder,struct pt_block * block)145574fe6c29SRuslan Bukin static int pt_blk_proceed_event(struct pt_block_decoder *decoder,
145674fe6c29SRuslan Bukin 				struct pt_block *block)
145774fe6c29SRuslan Bukin {
145874fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
145974fe6c29SRuslan Bukin 	struct pt_insn insn;
146074fe6c29SRuslan Bukin 	struct pt_event *ev;
146174fe6c29SRuslan Bukin 	int status;
146274fe6c29SRuslan Bukin 
146374fe6c29SRuslan Bukin 	if (!decoder || !decoder->process_event || !block)
146474fe6c29SRuslan Bukin 		return -pte_internal;
146574fe6c29SRuslan Bukin 
146674fe6c29SRuslan Bukin 	ev = &decoder->event;
146774fe6c29SRuslan Bukin 	switch (ev->type) {
146874fe6c29SRuslan Bukin 	case ptev_enabled:
146974fe6c29SRuslan Bukin 		break;
147074fe6c29SRuslan Bukin 
147174fe6c29SRuslan Bukin 	case ptev_disabled:
147274fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_disabled(decoder, block, &insn,
147374fe6c29SRuslan Bukin 						    &iext, ev);
147474fe6c29SRuslan Bukin 		if (status <= 0) {
147574fe6c29SRuslan Bukin 			/* A synchronous disable event also binds to the next
147674fe6c29SRuslan Bukin 			 * indirect or conditional branch, i.e. to any branch
147774fe6c29SRuslan Bukin 			 * that would have required trace.
147874fe6c29SRuslan Bukin 			 */
147974fe6c29SRuslan Bukin 			if (status != -pte_bad_query)
148074fe6c29SRuslan Bukin 				return status;
148174fe6c29SRuslan Bukin 
148274fe6c29SRuslan Bukin 			status = pt_blk_set_disable_resume_ip(decoder, &insn);
148374fe6c29SRuslan Bukin 			if (status < 0)
148474fe6c29SRuslan Bukin 				return status;
148574fe6c29SRuslan Bukin 		}
148674fe6c29SRuslan Bukin 
148774fe6c29SRuslan Bukin 		break;
148874fe6c29SRuslan Bukin 
148974fe6c29SRuslan Bukin 	case ptev_async_disabled:
149074fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
149174fe6c29SRuslan Bukin 					      ev->variant.async_disabled.at);
149274fe6c29SRuslan Bukin 		if (status <= 0)
149374fe6c29SRuslan Bukin 			return status;
149474fe6c29SRuslan Bukin 
149574fe6c29SRuslan Bukin 		if (decoder->query.config.errata.skd022) {
149674fe6c29SRuslan Bukin 			status = pt_blk_handle_erratum_skd022(decoder, ev);
149774fe6c29SRuslan Bukin 			if (status != 0) {
149874fe6c29SRuslan Bukin 				if (status < 0)
149974fe6c29SRuslan Bukin 					return status;
150074fe6c29SRuslan Bukin 
150174fe6c29SRuslan Bukin 				/* If the erratum hits, we modify the event.
150274fe6c29SRuslan Bukin 				 * Try again.
150374fe6c29SRuslan Bukin 				 */
150474fe6c29SRuslan Bukin 				return pt_blk_proceed_event(decoder, block);
150574fe6c29SRuslan Bukin 			}
150674fe6c29SRuslan Bukin 		}
150774fe6c29SRuslan Bukin 
150874fe6c29SRuslan Bukin 		break;
150974fe6c29SRuslan Bukin 
151074fe6c29SRuslan Bukin 	case ptev_async_branch:
151174fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
151274fe6c29SRuslan Bukin 					      ev->variant.async_branch.from);
151374fe6c29SRuslan Bukin 		if (status <= 0)
151474fe6c29SRuslan Bukin 			return status;
151574fe6c29SRuslan Bukin 
151674fe6c29SRuslan Bukin 		break;
151774fe6c29SRuslan Bukin 
151874fe6c29SRuslan Bukin 	case ptev_paging:
151974fe6c29SRuslan Bukin 		if (!decoder->enabled)
152074fe6c29SRuslan Bukin 			break;
152174fe6c29SRuslan Bukin 
152274fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
152374fe6c29SRuslan Bukin 						pt_insn_binds_to_pip);
152474fe6c29SRuslan Bukin 		if (status <= 0)
152574fe6c29SRuslan Bukin 			return status;
152674fe6c29SRuslan Bukin 
152774fe6c29SRuslan Bukin 		/* We bound a paging event.  Make sure we do not bind further
152874fe6c29SRuslan Bukin 		 * paging events to this instruction.
152974fe6c29SRuslan Bukin 		 */
153074fe6c29SRuslan Bukin 		decoder->bound_paging = 1;
153174fe6c29SRuslan Bukin 
153274fe6c29SRuslan Bukin 		return pt_blk_postpone_insn(decoder, &insn, &iext);
153374fe6c29SRuslan Bukin 
153474fe6c29SRuslan Bukin 	case ptev_async_paging:
153574fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_async_paging(decoder, block, ev);
153674fe6c29SRuslan Bukin 		if (status <= 0)
153774fe6c29SRuslan Bukin 			return status;
153874fe6c29SRuslan Bukin 
153974fe6c29SRuslan Bukin 		break;
154074fe6c29SRuslan Bukin 
154174fe6c29SRuslan Bukin 	case ptev_vmcs:
154274fe6c29SRuslan Bukin 		if (!decoder->enabled)
154374fe6c29SRuslan Bukin 			break;
154474fe6c29SRuslan Bukin 
154574fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
154674fe6c29SRuslan Bukin 						pt_insn_binds_to_vmcs);
154774fe6c29SRuslan Bukin 		if (status <= 0)
154874fe6c29SRuslan Bukin 			return status;
154974fe6c29SRuslan Bukin 
155074fe6c29SRuslan Bukin 		/* We bound a vmcs event.  Make sure we do not bind further vmcs
155174fe6c29SRuslan Bukin 		 * events to this instruction.
155274fe6c29SRuslan Bukin 		 */
155374fe6c29SRuslan Bukin 		decoder->bound_vmcs = 1;
155474fe6c29SRuslan Bukin 
155574fe6c29SRuslan Bukin 		return pt_blk_postpone_insn(decoder, &insn, &iext);
155674fe6c29SRuslan Bukin 
155774fe6c29SRuslan Bukin 	case ptev_async_vmcs:
155874fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_async_vmcs(decoder, block, ev);
155974fe6c29SRuslan Bukin 		if (status <= 0)
156074fe6c29SRuslan Bukin 			return status;
156174fe6c29SRuslan Bukin 
156274fe6c29SRuslan Bukin 		break;
156374fe6c29SRuslan Bukin 
156474fe6c29SRuslan Bukin 	case ptev_overflow:
156574fe6c29SRuslan Bukin 		break;
156674fe6c29SRuslan Bukin 
156774fe6c29SRuslan Bukin 	case ptev_exec_mode:
156874fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_exec_mode(decoder, block, ev);
156974fe6c29SRuslan Bukin 		if (status <= 0)
157074fe6c29SRuslan Bukin 			return status;
157174fe6c29SRuslan Bukin 
157274fe6c29SRuslan Bukin 		break;
157374fe6c29SRuslan Bukin 
157474fe6c29SRuslan Bukin 	case ptev_tsx:
157574fe6c29SRuslan Bukin 		if (ev->ip_suppressed)
157674fe6c29SRuslan Bukin 			break;
157774fe6c29SRuslan Bukin 
157874fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
157974fe6c29SRuslan Bukin 					      ev->variant.tsx.ip);
158074fe6c29SRuslan Bukin 		if (status <= 0)
158174fe6c29SRuslan Bukin 			return status;
158274fe6c29SRuslan Bukin 
158374fe6c29SRuslan Bukin 		break;
158474fe6c29SRuslan Bukin 
158574fe6c29SRuslan Bukin 	case ptev_stop:
158674fe6c29SRuslan Bukin 		break;
158774fe6c29SRuslan Bukin 
158874fe6c29SRuslan Bukin 	case ptev_exstop:
158974fe6c29SRuslan Bukin 		if (!decoder->enabled || ev->ip_suppressed)
159074fe6c29SRuslan Bukin 			break;
159174fe6c29SRuslan Bukin 
159274fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
159374fe6c29SRuslan Bukin 					      ev->variant.exstop.ip);
159474fe6c29SRuslan Bukin 		if (status <= 0)
159574fe6c29SRuslan Bukin 			return status;
159674fe6c29SRuslan Bukin 
159774fe6c29SRuslan Bukin 		break;
159874fe6c29SRuslan Bukin 
159974fe6c29SRuslan Bukin 	case ptev_mwait:
160074fe6c29SRuslan Bukin 		if (!decoder->enabled || ev->ip_suppressed)
160174fe6c29SRuslan Bukin 			break;
160274fe6c29SRuslan Bukin 
160374fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext,
160474fe6c29SRuslan Bukin 					      ev->variant.mwait.ip);
160574fe6c29SRuslan Bukin 		if (status <= 0)
160674fe6c29SRuslan Bukin 			return status;
160774fe6c29SRuslan Bukin 
160874fe6c29SRuslan Bukin 		break;
160974fe6c29SRuslan Bukin 
161074fe6c29SRuslan Bukin 	case ptev_pwre:
161174fe6c29SRuslan Bukin 	case ptev_pwrx:
161274fe6c29SRuslan Bukin 		break;
161374fe6c29SRuslan Bukin 
161474fe6c29SRuslan Bukin 	case ptev_ptwrite:
161574fe6c29SRuslan Bukin 		if (!decoder->enabled)
161674fe6c29SRuslan Bukin 			break;
161774fe6c29SRuslan Bukin 
161874fe6c29SRuslan Bukin 		status = pt_blk_proceed_to_ptwrite(decoder, block, &insn,
161974fe6c29SRuslan Bukin 						   &iext, ev);
162074fe6c29SRuslan Bukin 		if (status <= 0)
162174fe6c29SRuslan Bukin 			return status;
162274fe6c29SRuslan Bukin 
162374fe6c29SRuslan Bukin 		/* We bound a ptwrite event.  Make sure we do not bind further
162474fe6c29SRuslan Bukin 		 * ptwrite events to this instruction.
162574fe6c29SRuslan Bukin 		 */
162674fe6c29SRuslan Bukin 		decoder->bound_ptwrite = 1;
162774fe6c29SRuslan Bukin 
162874fe6c29SRuslan Bukin 		return pt_blk_postpone_insn(decoder, &insn, &iext);
162974fe6c29SRuslan Bukin 
163074fe6c29SRuslan Bukin 	case ptev_tick:
163174fe6c29SRuslan Bukin 	case ptev_cbr:
163274fe6c29SRuslan Bukin 	case ptev_mnt:
163374fe6c29SRuslan Bukin 		break;
163474fe6c29SRuslan Bukin 	}
163574fe6c29SRuslan Bukin 
163674fe6c29SRuslan Bukin 	return pt_blk_status(decoder, pts_event_pending);
163774fe6c29SRuslan Bukin }
163874fe6c29SRuslan Bukin 
163974fe6c29SRuslan Bukin /* Proceed to the next decision point without using the block cache.
164074fe6c29SRuslan Bukin  *
164174fe6c29SRuslan Bukin  * Tracing is enabled and we don't have an event pending.  Proceed as far as
164274fe6c29SRuslan Bukin  * we get without trace.  Stop when we either:
164374fe6c29SRuslan Bukin  *
164474fe6c29SRuslan Bukin  *   - need trace in order to continue
164574fe6c29SRuslan Bukin  *   - overflow the max number of instructions in a block
164674fe6c29SRuslan Bukin  *
164774fe6c29SRuslan Bukin  * We actually proceed one instruction further to get the start IP for the next
164874fe6c29SRuslan Bukin  * block.  This only updates @decoder's internal state, though.
164974fe6c29SRuslan Bukin  *
165074fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
165174fe6c29SRuslan Bukin  */
pt_blk_proceed_no_event_uncached(struct pt_block_decoder * decoder,struct pt_block * block)165274fe6c29SRuslan Bukin static int pt_blk_proceed_no_event_uncached(struct pt_block_decoder *decoder,
165374fe6c29SRuslan Bukin 					    struct pt_block *block)
165474fe6c29SRuslan Bukin {
165574fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
165674fe6c29SRuslan Bukin 	struct pt_insn insn;
165774fe6c29SRuslan Bukin 	int status;
165874fe6c29SRuslan Bukin 
165974fe6c29SRuslan Bukin 	if (!decoder || !block)
166074fe6c29SRuslan Bukin 		return -pte_internal;
166174fe6c29SRuslan Bukin 
166274fe6c29SRuslan Bukin 	/* This is overly conservative, really.  We shouldn't get a bad-query
166374fe6c29SRuslan Bukin 	 * status unless we decoded at least one instruction successfully.
166474fe6c29SRuslan Bukin 	 */
166574fe6c29SRuslan Bukin 	memset(&insn, 0, sizeof(insn));
166674fe6c29SRuslan Bukin 	memset(&iext, 0, sizeof(iext));
166774fe6c29SRuslan Bukin 
166874fe6c29SRuslan Bukin 	/* Proceed as far as we get without trace. */
166974fe6c29SRuslan Bukin 	status = pt_blk_proceed_to_insn(decoder, block, &insn, &iext,
167074fe6c29SRuslan Bukin 					pt_insn_false);
167174fe6c29SRuslan Bukin 	if (status < 0) {
167274fe6c29SRuslan Bukin 		if (status != -pte_bad_query)
167374fe6c29SRuslan Bukin 			return status;
167474fe6c29SRuslan Bukin 
167574fe6c29SRuslan Bukin 		return pt_blk_proceed_with_trace(decoder, &insn, &iext);
167674fe6c29SRuslan Bukin 	}
167774fe6c29SRuslan Bukin 
167874fe6c29SRuslan Bukin 	return 0;
167974fe6c29SRuslan Bukin }
168074fe6c29SRuslan Bukin 
168174fe6c29SRuslan Bukin /* Check if @ip is contained in @section loaded at @laddr.
168274fe6c29SRuslan Bukin  *
168374fe6c29SRuslan Bukin  * Returns non-zero if it is.
168474fe6c29SRuslan Bukin  * Returns zero if it isn't or of @section is NULL.
168574fe6c29SRuslan Bukin  */
pt_blk_is_in_section(const struct pt_mapped_section * msec,uint64_t ip)168674fe6c29SRuslan Bukin static inline int pt_blk_is_in_section(const struct pt_mapped_section *msec,
168774fe6c29SRuslan Bukin 				       uint64_t ip)
168874fe6c29SRuslan Bukin {
168974fe6c29SRuslan Bukin 	uint64_t begin, end;
169074fe6c29SRuslan Bukin 
169174fe6c29SRuslan Bukin 	begin = pt_msec_begin(msec);
169274fe6c29SRuslan Bukin 	end = pt_msec_end(msec);
169374fe6c29SRuslan Bukin 
169474fe6c29SRuslan Bukin 	return (begin <= ip && ip < end);
169574fe6c29SRuslan Bukin }
169674fe6c29SRuslan Bukin 
169774fe6c29SRuslan Bukin /* Insert a trampoline block cache entry.
169874fe6c29SRuslan Bukin  *
169974fe6c29SRuslan Bukin  * Add a trampoline block cache entry at @ip to continue at @nip, where @nip
170074fe6c29SRuslan Bukin  * must be the next instruction after @ip.
170174fe6c29SRuslan Bukin  *
170274fe6c29SRuslan Bukin  * Both @ip and @nip must be section-relative
170374fe6c29SRuslan Bukin  *
170474fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
170574fe6c29SRuslan Bukin  */
pt_blk_add_trampoline(struct pt_block_cache * bcache,uint64_t ip,uint64_t nip,enum pt_exec_mode mode)170674fe6c29SRuslan Bukin static inline int pt_blk_add_trampoline(struct pt_block_cache *bcache,
170774fe6c29SRuslan Bukin 					uint64_t ip, uint64_t nip,
170874fe6c29SRuslan Bukin 					enum pt_exec_mode mode)
170974fe6c29SRuslan Bukin {
171074fe6c29SRuslan Bukin 	struct pt_bcache_entry bce;
171174fe6c29SRuslan Bukin 	int64_t disp;
171274fe6c29SRuslan Bukin 
171374fe6c29SRuslan Bukin 	/* The displacement from @ip to @nip for the trampoline. */
171474fe6c29SRuslan Bukin 	disp = (int64_t) (nip - ip);
171574fe6c29SRuslan Bukin 
171674fe6c29SRuslan Bukin 	memset(&bce, 0, sizeof(bce));
171774fe6c29SRuslan Bukin 	bce.displacement = (int32_t) disp;
171874fe6c29SRuslan Bukin 	bce.ninsn = 1;
171974fe6c29SRuslan Bukin 	bce.mode = mode;
172074fe6c29SRuslan Bukin 	bce.qualifier = ptbq_again;
172174fe6c29SRuslan Bukin 
172274fe6c29SRuslan Bukin 	/* If we can't reach @nip without overflowing the displacement field, we
172374fe6c29SRuslan Bukin 	 * have to stop and re-decode the instruction at @ip.
172474fe6c29SRuslan Bukin 	 */
172574fe6c29SRuslan Bukin 	if ((int64_t) bce.displacement != disp) {
172674fe6c29SRuslan Bukin 
172774fe6c29SRuslan Bukin 		memset(&bce, 0, sizeof(bce));
172874fe6c29SRuslan Bukin 		bce.ninsn = 1;
172974fe6c29SRuslan Bukin 		bce.mode = mode;
173074fe6c29SRuslan Bukin 		bce.qualifier = ptbq_decode;
173174fe6c29SRuslan Bukin 	}
173274fe6c29SRuslan Bukin 
173374fe6c29SRuslan Bukin 	return pt_bcache_add(bcache, ip, bce);
173474fe6c29SRuslan Bukin }
173574fe6c29SRuslan Bukin 
173674fe6c29SRuslan Bukin /* Insert a decode block cache entry.
173774fe6c29SRuslan Bukin  *
173874fe6c29SRuslan Bukin  * Add a decode block cache entry at @ioff.
173974fe6c29SRuslan Bukin  *
174074fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
174174fe6c29SRuslan Bukin  */
pt_blk_add_decode(struct pt_block_cache * bcache,uint64_t ioff,enum pt_exec_mode mode)174274fe6c29SRuslan Bukin static inline int pt_blk_add_decode(struct pt_block_cache *bcache,
174374fe6c29SRuslan Bukin 				    uint64_t ioff, enum pt_exec_mode mode)
174474fe6c29SRuslan Bukin {
174574fe6c29SRuslan Bukin 	struct pt_bcache_entry bce;
174674fe6c29SRuslan Bukin 
174774fe6c29SRuslan Bukin 	memset(&bce, 0, sizeof(bce));
174874fe6c29SRuslan Bukin 	bce.ninsn = 1;
174974fe6c29SRuslan Bukin 	bce.mode = mode;
175074fe6c29SRuslan Bukin 	bce.qualifier = ptbq_decode;
175174fe6c29SRuslan Bukin 
175274fe6c29SRuslan Bukin 	return pt_bcache_add(bcache, ioff, bce);
175374fe6c29SRuslan Bukin }
175474fe6c29SRuslan Bukin 
175574fe6c29SRuslan Bukin enum {
175674fe6c29SRuslan Bukin 	/* The maximum number of steps when filling the block cache. */
175774fe6c29SRuslan Bukin 	bcache_fill_steps	= 0x400
175874fe6c29SRuslan Bukin };
175974fe6c29SRuslan Bukin 
176074fe6c29SRuslan Bukin /* Proceed to the next instruction and fill the block cache for @decoder->ip.
176174fe6c29SRuslan Bukin  *
176274fe6c29SRuslan Bukin  * Tracing is enabled and we don't have an event pending.  The current IP is not
176374fe6c29SRuslan Bukin  * yet cached.
176474fe6c29SRuslan Bukin  *
176574fe6c29SRuslan Bukin  * Proceed one instruction without using the block cache, then try to proceed
176674fe6c29SRuslan Bukin  * further using the block cache.
176774fe6c29SRuslan Bukin  *
176874fe6c29SRuslan Bukin  * On our way back, add a block cache entry for the IP before proceeding.  Note
176974fe6c29SRuslan Bukin  * that the recursion is bounded by @steps and ultimately by the maximum number
177074fe6c29SRuslan Bukin  * of instructions in a block.
177174fe6c29SRuslan Bukin  *
177274fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
177374fe6c29SRuslan Bukin  */
177474fe6c29SRuslan Bukin static int
pt_blk_proceed_no_event_fill_cache(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_block_cache * bcache,const struct pt_mapped_section * msec,size_t steps)177574fe6c29SRuslan Bukin pt_blk_proceed_no_event_fill_cache(struct pt_block_decoder *decoder,
177674fe6c29SRuslan Bukin 				   struct pt_block *block,
177774fe6c29SRuslan Bukin 				   struct pt_block_cache *bcache,
177874fe6c29SRuslan Bukin 				   const struct pt_mapped_section *msec,
177974fe6c29SRuslan Bukin 				   size_t steps)
178074fe6c29SRuslan Bukin {
178174fe6c29SRuslan Bukin 	struct pt_bcache_entry bce;
178274fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
178374fe6c29SRuslan Bukin 	struct pt_insn insn;
1784*85f87cf4SRuslan Bukin 	uint64_t nip, dip, ioff, noff;
1785*85f87cf4SRuslan Bukin 	int64_t disp;
178674fe6c29SRuslan Bukin 	int status;
178774fe6c29SRuslan Bukin 
178874fe6c29SRuslan Bukin 	if (!decoder || !steps)
178974fe6c29SRuslan Bukin 		return -pte_internal;
179074fe6c29SRuslan Bukin 
179174fe6c29SRuslan Bukin 	/* Proceed one instruction by decoding and examining it.
179274fe6c29SRuslan Bukin 	 *
179374fe6c29SRuslan Bukin 	 * Note that we also return on a status of zero that indicates that the
179474fe6c29SRuslan Bukin 	 * instruction didn't fit into @block.
179574fe6c29SRuslan Bukin 	 */
179674fe6c29SRuslan Bukin 	status = pt_blk_proceed_one_insn(decoder, block, &insn, &iext);
179774fe6c29SRuslan Bukin 	if (status <= 0)
179874fe6c29SRuslan Bukin 		return status;
179974fe6c29SRuslan Bukin 
180074fe6c29SRuslan Bukin 	ioff = pt_msec_unmap(msec, insn.ip);
180174fe6c29SRuslan Bukin 
180274fe6c29SRuslan Bukin 	/* Let's see if we can proceed to the next IP without trace.
180374fe6c29SRuslan Bukin 	 *
180474fe6c29SRuslan Bukin 	 * If we can't, this is certainly a decision point.
180574fe6c29SRuslan Bukin 	 */
180674fe6c29SRuslan Bukin 	status = pt_insn_next_ip(&decoder->ip, &insn, &iext);
180774fe6c29SRuslan Bukin 	if (status < 0) {
180874fe6c29SRuslan Bukin 		if (status != -pte_bad_query)
180974fe6c29SRuslan Bukin 			return status;
181074fe6c29SRuslan Bukin 
181174fe6c29SRuslan Bukin 		memset(&bce, 0, sizeof(bce));
181274fe6c29SRuslan Bukin 		bce.ninsn = 1;
181374fe6c29SRuslan Bukin 		bce.mode = insn.mode;
181474fe6c29SRuslan Bukin 		bce.isize = insn.size;
181574fe6c29SRuslan Bukin 
181674fe6c29SRuslan Bukin 		/* Clear the instruction size in case of overflows. */
181774fe6c29SRuslan Bukin 		if ((uint8_t) bce.isize != insn.size)
181874fe6c29SRuslan Bukin 			bce.isize = 0;
181974fe6c29SRuslan Bukin 
182074fe6c29SRuslan Bukin 		switch (insn.iclass) {
182174fe6c29SRuslan Bukin 		case ptic_ptwrite:
182274fe6c29SRuslan Bukin 		case ptic_error:
182374fe6c29SRuslan Bukin 		case ptic_other:
182474fe6c29SRuslan Bukin 			return -pte_internal;
182574fe6c29SRuslan Bukin 
182674fe6c29SRuslan Bukin 		case ptic_jump:
182774fe6c29SRuslan Bukin 			/* A direct jump doesn't require trace. */
182874fe6c29SRuslan Bukin 			if (iext.variant.branch.is_direct)
182974fe6c29SRuslan Bukin 				return -pte_internal;
183074fe6c29SRuslan Bukin 
183174fe6c29SRuslan Bukin 			bce.qualifier = ptbq_indirect;
183274fe6c29SRuslan Bukin 			break;
183374fe6c29SRuslan Bukin 
183474fe6c29SRuslan Bukin 		case ptic_call:
183574fe6c29SRuslan Bukin 			/* A direct call doesn't require trace. */
183674fe6c29SRuslan Bukin 			if (iext.variant.branch.is_direct)
183774fe6c29SRuslan Bukin 				return -pte_internal;
183874fe6c29SRuslan Bukin 
183974fe6c29SRuslan Bukin 			bce.qualifier = ptbq_ind_call;
184074fe6c29SRuslan Bukin 			break;
184174fe6c29SRuslan Bukin 
184274fe6c29SRuslan Bukin 		case ptic_return:
184374fe6c29SRuslan Bukin 			bce.qualifier = ptbq_return;
184474fe6c29SRuslan Bukin 			break;
184574fe6c29SRuslan Bukin 
184674fe6c29SRuslan Bukin 		case ptic_cond_jump:
184774fe6c29SRuslan Bukin 			bce.qualifier = ptbq_cond;
184874fe6c29SRuslan Bukin 			break;
184974fe6c29SRuslan Bukin 
185074fe6c29SRuslan Bukin 		case ptic_far_call:
185174fe6c29SRuslan Bukin 		case ptic_far_return:
185274fe6c29SRuslan Bukin 		case ptic_far_jump:
185374fe6c29SRuslan Bukin 			bce.qualifier = ptbq_indirect;
185474fe6c29SRuslan Bukin 			break;
185574fe6c29SRuslan Bukin 		}
185674fe6c29SRuslan Bukin 
185774fe6c29SRuslan Bukin 		/* If the block was truncated, we have to decode its last
185874fe6c29SRuslan Bukin 		 * instruction each time.
185974fe6c29SRuslan Bukin 		 *
186074fe6c29SRuslan Bukin 		 * We could have skipped the above switch and size assignment in
186174fe6c29SRuslan Bukin 		 * this case but this is already a slow and hopefully infrequent
186274fe6c29SRuslan Bukin 		 * path.
186374fe6c29SRuslan Bukin 		 */
186474fe6c29SRuslan Bukin 		if (block->truncated)
186574fe6c29SRuslan Bukin 			bce.qualifier = ptbq_decode;
186674fe6c29SRuslan Bukin 
186774fe6c29SRuslan Bukin 		status = pt_bcache_add(bcache, ioff, bce);
186874fe6c29SRuslan Bukin 		if (status < 0)
186974fe6c29SRuslan Bukin 			return status;
187074fe6c29SRuslan Bukin 
187174fe6c29SRuslan Bukin 		return pt_blk_proceed_with_trace(decoder, &insn, &iext);
187274fe6c29SRuslan Bukin 	}
187374fe6c29SRuslan Bukin 
187474fe6c29SRuslan Bukin 	/* The next instruction's IP. */
187574fe6c29SRuslan Bukin 	nip = decoder->ip;
187674fe6c29SRuslan Bukin 	noff = pt_msec_unmap(msec, nip);
187774fe6c29SRuslan Bukin 
187874fe6c29SRuslan Bukin 	/* Even if we were able to proceed without trace, we might have to stop
187974fe6c29SRuslan Bukin 	 * here for various reasons:
188074fe6c29SRuslan Bukin 	 *
188174fe6c29SRuslan Bukin 	 *   - at near direct calls to update the return-address stack
188274fe6c29SRuslan Bukin 	 *
188374fe6c29SRuslan Bukin 	 *     We are forced to re-decode @insn to get the branch displacement.
188474fe6c29SRuslan Bukin 	 *
188574fe6c29SRuslan Bukin 	 *     Even though it is constant, we don't cache it to avoid increasing
188674fe6c29SRuslan Bukin 	 *     the size of a cache entry.  Note that the displacement field is
188774fe6c29SRuslan Bukin 	 *     zero for this entry and we might be tempted to use it - but other
188874fe6c29SRuslan Bukin 	 *     entries that point to this decision point will have non-zero
188974fe6c29SRuslan Bukin 	 *     displacement.
189074fe6c29SRuslan Bukin 	 *
189174fe6c29SRuslan Bukin 	 *     We could proceed after a near direct call but we migh as well
189274fe6c29SRuslan Bukin 	 *     postpone it to the next iteration.  Make sure to end the block if
189374fe6c29SRuslan Bukin 	 *     @decoder->flags.variant.block.end_on_call is set, though.
189474fe6c29SRuslan Bukin 	 *
189574fe6c29SRuslan Bukin 	 *   - at near direct backwards jumps to detect section splits
189674fe6c29SRuslan Bukin 	 *
189774fe6c29SRuslan Bukin 	 *     In case the current section is split underneath us, we must take
189874fe6c29SRuslan Bukin 	 *     care to detect that split.
189974fe6c29SRuslan Bukin 	 *
190074fe6c29SRuslan Bukin 	 *     There is one corner case where the split is in the middle of a
190174fe6c29SRuslan Bukin 	 *     linear sequence of instructions that branches back into the
190274fe6c29SRuslan Bukin 	 *     originating section.
190374fe6c29SRuslan Bukin 	 *
190474fe6c29SRuslan Bukin 	 *     Calls, indirect branches, and far branches are already covered
190574fe6c29SRuslan Bukin 	 *     since they either require trace or already require us to stop
190674fe6c29SRuslan Bukin 	 *     (i.e. near direct calls) for other reasons.  That leaves near
190774fe6c29SRuslan Bukin 	 *     direct backward jumps.
190874fe6c29SRuslan Bukin 	 *
190974fe6c29SRuslan Bukin 	 *     Instead of the decode stop at the jump instruction we're using we
191074fe6c29SRuslan Bukin 	 *     could have made sure that other block cache entries that extend
191174fe6c29SRuslan Bukin 	 *     this one insert a trampoline to the jump's entry.  This would
191274fe6c29SRuslan Bukin 	 *     have been a bit more complicated.
191374fe6c29SRuslan Bukin 	 *
191474fe6c29SRuslan Bukin 	 *   - if we switched sections
191574fe6c29SRuslan Bukin 	 *
191674fe6c29SRuslan Bukin 	 *     This ends a block just like a branch that requires trace.
191774fe6c29SRuslan Bukin 	 *
191874fe6c29SRuslan Bukin 	 *     We need to re-decode @insn in order to determine the start IP of
191974fe6c29SRuslan Bukin 	 *     the next block.
192074fe6c29SRuslan Bukin 	 *
192174fe6c29SRuslan Bukin 	 *   - if the block is truncated
192274fe6c29SRuslan Bukin 	 *
192374fe6c29SRuslan Bukin 	 *     We need to read the last instruction's memory from multiple
192474fe6c29SRuslan Bukin 	 *     sections and provide it to the user.
192574fe6c29SRuslan Bukin 	 *
192674fe6c29SRuslan Bukin 	 *     We could still use the block cache but then we'd have to handle
192774fe6c29SRuslan Bukin 	 *     this case for each qualifier.  Truncation is hopefully rare and
192874fe6c29SRuslan Bukin 	 *     having to read the memory for the instruction from multiple
192974fe6c29SRuslan Bukin 	 *     sections is already slow.  Let's rather keep things simple and
193074fe6c29SRuslan Bukin 	 *     route it through the decode flow, where we already have
193174fe6c29SRuslan Bukin 	 *     everything in place.
193274fe6c29SRuslan Bukin 	 */
193374fe6c29SRuslan Bukin 	switch (insn.iclass) {
193474fe6c29SRuslan Bukin 	case ptic_call:
193574fe6c29SRuslan Bukin 		return pt_blk_add_decode(bcache, ioff, insn.mode);
193674fe6c29SRuslan Bukin 
193774fe6c29SRuslan Bukin 	case ptic_jump:
193874fe6c29SRuslan Bukin 		/* An indirect branch requires trace and should have been
193974fe6c29SRuslan Bukin 		 * handled above.
194074fe6c29SRuslan Bukin 		 */
194174fe6c29SRuslan Bukin 		if (!iext.variant.branch.is_direct)
194274fe6c29SRuslan Bukin 			return -pte_internal;
194374fe6c29SRuslan Bukin 
194474fe6c29SRuslan Bukin 		if (iext.variant.branch.displacement < 0 ||
194574fe6c29SRuslan Bukin 		    decoder->flags.variant.block.end_on_jump)
194674fe6c29SRuslan Bukin 			return pt_blk_add_decode(bcache, ioff, insn.mode);
194774fe6c29SRuslan Bukin 
194874fe6c29SRuslan Bukin 		fallthrough;
194974fe6c29SRuslan Bukin 	default:
195074fe6c29SRuslan Bukin 		if (!pt_blk_is_in_section(msec, nip) || block->truncated)
195174fe6c29SRuslan Bukin 			return pt_blk_add_decode(bcache, ioff, insn.mode);
195274fe6c29SRuslan Bukin 
195374fe6c29SRuslan Bukin 		break;
195474fe6c29SRuslan Bukin 	}
195574fe6c29SRuslan Bukin 
195674fe6c29SRuslan Bukin 	/* We proceeded one instruction.  Let's see if we have a cache entry for
195774fe6c29SRuslan Bukin 	 * the next instruction.
195874fe6c29SRuslan Bukin 	 */
195974fe6c29SRuslan Bukin 	status = pt_bcache_lookup(&bce, bcache, noff);
196074fe6c29SRuslan Bukin 	if (status < 0)
196174fe6c29SRuslan Bukin 		return status;
196274fe6c29SRuslan Bukin 
196374fe6c29SRuslan Bukin 	/* If we don't have a valid cache entry, yet, fill the cache some more.
196474fe6c29SRuslan Bukin 	 *
196574fe6c29SRuslan Bukin 	 * On our way back, we add a cache entry for this instruction based on
196674fe6c29SRuslan Bukin 	 * the cache entry of the succeeding instruction.
196774fe6c29SRuslan Bukin 	 */
196874fe6c29SRuslan Bukin 	if (!pt_bce_is_valid(bce)) {
196974fe6c29SRuslan Bukin 		/* If we exceeded the maximum number of allowed steps, we insert
197074fe6c29SRuslan Bukin 		 * a trampoline to the next instruction.
197174fe6c29SRuslan Bukin 		 *
197274fe6c29SRuslan Bukin 		 * The next time we encounter the same code, we will use the
197374fe6c29SRuslan Bukin 		 * trampoline to jump directly to where we left off this time
197474fe6c29SRuslan Bukin 		 * and continue from there.
197574fe6c29SRuslan Bukin 		 */
197674fe6c29SRuslan Bukin 		steps -= 1;
197774fe6c29SRuslan Bukin 		if (!steps)
197874fe6c29SRuslan Bukin 			return pt_blk_add_trampoline(bcache, ioff, noff,
197974fe6c29SRuslan Bukin 						     insn.mode);
198074fe6c29SRuslan Bukin 
198174fe6c29SRuslan Bukin 		status = pt_blk_proceed_no_event_fill_cache(decoder, block,
198274fe6c29SRuslan Bukin 							    bcache, msec,
198374fe6c29SRuslan Bukin 							    steps);
198474fe6c29SRuslan Bukin 		if (status < 0)
198574fe6c29SRuslan Bukin 			return status;
198674fe6c29SRuslan Bukin 
198774fe6c29SRuslan Bukin 		/* Let's see if we have more luck this time. */
198874fe6c29SRuslan Bukin 		status = pt_bcache_lookup(&bce, bcache, noff);
198974fe6c29SRuslan Bukin 		if (status < 0)
199074fe6c29SRuslan Bukin 			return status;
199174fe6c29SRuslan Bukin 
199274fe6c29SRuslan Bukin 		/* If we still don't have a valid cache entry, we're done.  Most
199374fe6c29SRuslan Bukin 		 * likely, @block overflowed and we couldn't proceed past the
199474fe6c29SRuslan Bukin 		 * next instruction.
199574fe6c29SRuslan Bukin 		 */
199674fe6c29SRuslan Bukin 		if (!pt_bce_is_valid(bce))
199774fe6c29SRuslan Bukin 			return 0;
199874fe6c29SRuslan Bukin 	}
199974fe6c29SRuslan Bukin 
200074fe6c29SRuslan Bukin 	/* We must not have switched execution modes.
200174fe6c29SRuslan Bukin 	 *
200274fe6c29SRuslan Bukin 	 * This would require an event and we're on the no-event flow.
200374fe6c29SRuslan Bukin 	 */
200474fe6c29SRuslan Bukin 	if (pt_bce_exec_mode(bce) != insn.mode)
200574fe6c29SRuslan Bukin 		return -pte_internal;
200674fe6c29SRuslan Bukin 
200774fe6c29SRuslan Bukin 	/* The decision point IP and the displacement from @insn.ip. */
2008*85f87cf4SRuslan Bukin 	dip = nip + (uint64_t) (int64_t) bce.displacement;
200974fe6c29SRuslan Bukin 	disp = (int64_t) (dip - insn.ip);
201074fe6c29SRuslan Bukin 
201174fe6c29SRuslan Bukin 	/* We may have switched sections if the section was split.  See
201274fe6c29SRuslan Bukin 	 * pt_blk_proceed_no_event_cached() for a more elaborate comment.
201374fe6c29SRuslan Bukin 	 *
201474fe6c29SRuslan Bukin 	 * We're not adding a block cache entry since this won't apply to the
201574fe6c29SRuslan Bukin 	 * original section which may be shared with other decoders.
201674fe6c29SRuslan Bukin 	 *
201774fe6c29SRuslan Bukin 	 * We will instead take the slow path until the end of the section.
201874fe6c29SRuslan Bukin 	 */
201974fe6c29SRuslan Bukin 	if (!pt_blk_is_in_section(msec, dip))
202074fe6c29SRuslan Bukin 		return 0;
202174fe6c29SRuslan Bukin 
202274fe6c29SRuslan Bukin 	/* Let's try to reach @nip's decision point from @insn.ip.
202374fe6c29SRuslan Bukin 	 *
202474fe6c29SRuslan Bukin 	 * There are two fields that may overflow: @bce.ninsn and
202574fe6c29SRuslan Bukin 	 * @bce.displacement.
202674fe6c29SRuslan Bukin 	 */
202774fe6c29SRuslan Bukin 	bce.ninsn += 1;
202874fe6c29SRuslan Bukin 	bce.displacement = (int32_t) disp;
202974fe6c29SRuslan Bukin 
203074fe6c29SRuslan Bukin 	/* If none of them overflowed, we're done.
203174fe6c29SRuslan Bukin 	 *
203274fe6c29SRuslan Bukin 	 * If one or both overflowed, let's try to insert a trampoline, i.e. we
203374fe6c29SRuslan Bukin 	 * try to reach @dip via a ptbq_again entry to @nip.
203474fe6c29SRuslan Bukin 	 */
203574fe6c29SRuslan Bukin 	if (!bce.ninsn || ((int64_t) bce.displacement != disp))
203674fe6c29SRuslan Bukin 		return pt_blk_add_trampoline(bcache, ioff, noff, insn.mode);
203774fe6c29SRuslan Bukin 
203874fe6c29SRuslan Bukin 	/* We're done.  Add the cache entry.
203974fe6c29SRuslan Bukin 	 *
204074fe6c29SRuslan Bukin 	 * There's a chance that other decoders updated the cache entry in the
204174fe6c29SRuslan Bukin 	 * meantime.  They should have come to the same conclusion as we,
204274fe6c29SRuslan Bukin 	 * though, and the cache entries should be identical.
204374fe6c29SRuslan Bukin 	 *
204474fe6c29SRuslan Bukin 	 * Cache updates are atomic so even if the two versions were not
204574fe6c29SRuslan Bukin 	 * identical, we wouldn't care because they are both correct.
204674fe6c29SRuslan Bukin 	 */
204774fe6c29SRuslan Bukin 	return pt_bcache_add(bcache, ioff, bce);
204874fe6c29SRuslan Bukin }
204974fe6c29SRuslan Bukin 
205074fe6c29SRuslan Bukin /* Proceed at a potentially truncated instruction.
205174fe6c29SRuslan Bukin  *
205274fe6c29SRuslan Bukin  * We were not able to decode the instruction at @decoder->ip in @decoder's
205374fe6c29SRuslan Bukin  * cached section.  This is typically caused by not having enough bytes.
205474fe6c29SRuslan Bukin  *
205574fe6c29SRuslan Bukin  * Try to decode the instruction again using the entire image.  If this succeeds
205674fe6c29SRuslan Bukin  * we expect to end up with an instruction that was truncated in the section it
205774fe6c29SRuslan Bukin  * started.  We provide the full instruction in this case and end the block.
205874fe6c29SRuslan Bukin  *
205974fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
206074fe6c29SRuslan Bukin  */
pt_blk_proceed_truncated(struct pt_block_decoder * decoder,struct pt_block * block)206174fe6c29SRuslan Bukin static int pt_blk_proceed_truncated(struct pt_block_decoder *decoder,
206274fe6c29SRuslan Bukin 				    struct pt_block *block)
206374fe6c29SRuslan Bukin {
206474fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
206574fe6c29SRuslan Bukin 	struct pt_insn insn;
206674fe6c29SRuslan Bukin 	int errcode;
206774fe6c29SRuslan Bukin 
206874fe6c29SRuslan Bukin 	if (!decoder || !block)
206974fe6c29SRuslan Bukin 		return -pte_internal;
207074fe6c29SRuslan Bukin 
207174fe6c29SRuslan Bukin 	memset(&iext, 0, sizeof(iext));
207274fe6c29SRuslan Bukin 	memset(&insn, 0, sizeof(insn));
207374fe6c29SRuslan Bukin 
207474fe6c29SRuslan Bukin 	insn.mode = decoder->mode;
207574fe6c29SRuslan Bukin 	insn.ip = decoder->ip;
207674fe6c29SRuslan Bukin 
207774fe6c29SRuslan Bukin 	errcode = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
207874fe6c29SRuslan Bukin 	if (errcode < 0)
207974fe6c29SRuslan Bukin 		return errcode;
208074fe6c29SRuslan Bukin 
208174fe6c29SRuslan Bukin 	/* We shouldn't use this function if the instruction isn't truncated. */
208274fe6c29SRuslan Bukin 	if (!insn.truncated)
208374fe6c29SRuslan Bukin 		return -pte_internal;
208474fe6c29SRuslan Bukin 
208574fe6c29SRuslan Bukin 	/* Provide the instruction in the block.  This ends the block. */
208674fe6c29SRuslan Bukin 	memcpy(block->raw, insn.raw, insn.size);
208774fe6c29SRuslan Bukin 	block->iclass = insn.iclass;
208874fe6c29SRuslan Bukin 	block->size = insn.size;
208974fe6c29SRuslan Bukin 	block->truncated = 1;
209074fe6c29SRuslan Bukin 
209174fe6c29SRuslan Bukin 	/* Log calls' return addresses for return compression. */
209274fe6c29SRuslan Bukin 	errcode = pt_blk_log_call(decoder, &insn, &iext);
209374fe6c29SRuslan Bukin 	if (errcode < 0)
209474fe6c29SRuslan Bukin 		return errcode;
209574fe6c29SRuslan Bukin 
209674fe6c29SRuslan Bukin 	/* Let's see if we can proceed to the next IP without trace.
209774fe6c29SRuslan Bukin 	 *
209874fe6c29SRuslan Bukin 	 * The truncated instruction ends the block but we still need to get the
209974fe6c29SRuslan Bukin 	 * next block's start IP.
210074fe6c29SRuslan Bukin 	 */
210174fe6c29SRuslan Bukin 	errcode = pt_insn_next_ip(&decoder->ip, &insn, &iext);
210274fe6c29SRuslan Bukin 	if (errcode < 0) {
210374fe6c29SRuslan Bukin 		if (errcode != -pte_bad_query)
210474fe6c29SRuslan Bukin 			return errcode;
210574fe6c29SRuslan Bukin 
210674fe6c29SRuslan Bukin 		return pt_blk_proceed_with_trace(decoder, &insn, &iext);
210774fe6c29SRuslan Bukin 	}
210874fe6c29SRuslan Bukin 
210974fe6c29SRuslan Bukin 	return 0;
211074fe6c29SRuslan Bukin }
211174fe6c29SRuslan Bukin 
211274fe6c29SRuslan Bukin /* Proceed to the next decision point using the block cache.
211374fe6c29SRuslan Bukin  *
211474fe6c29SRuslan Bukin  * Tracing is enabled and we don't have an event pending.  We already set
211574fe6c29SRuslan Bukin  * @block's isid.  All reads are done within @msec as we're not switching
211674fe6c29SRuslan Bukin  * sections between blocks.
211774fe6c29SRuslan Bukin  *
211874fe6c29SRuslan Bukin  * Proceed as far as we get without trace.  Stop when we either:
211974fe6c29SRuslan Bukin  *
212074fe6c29SRuslan Bukin  *   - need trace in order to continue
212174fe6c29SRuslan Bukin  *   - overflow the max number of instructions in a block
212274fe6c29SRuslan Bukin  *
212374fe6c29SRuslan Bukin  * We actually proceed one instruction further to get the start IP for the next
212474fe6c29SRuslan Bukin  * block.  This only updates @decoder's internal state, though.
212574fe6c29SRuslan Bukin  *
212674fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
212774fe6c29SRuslan Bukin  */
pt_blk_proceed_no_event_cached(struct pt_block_decoder * decoder,struct pt_block * block,struct pt_block_cache * bcache,const struct pt_mapped_section * msec)212874fe6c29SRuslan Bukin static int pt_blk_proceed_no_event_cached(struct pt_block_decoder *decoder,
212974fe6c29SRuslan Bukin 					  struct pt_block *block,
213074fe6c29SRuslan Bukin 					  struct pt_block_cache *bcache,
213174fe6c29SRuslan Bukin 					  const struct pt_mapped_section *msec)
213274fe6c29SRuslan Bukin {
213374fe6c29SRuslan Bukin 	struct pt_bcache_entry bce;
213474fe6c29SRuslan Bukin 	uint16_t binsn, ninsn;
213574fe6c29SRuslan Bukin 	uint64_t offset, nip;
213674fe6c29SRuslan Bukin 	int status;
213774fe6c29SRuslan Bukin 
213874fe6c29SRuslan Bukin 	if (!decoder || !block)
213974fe6c29SRuslan Bukin 		return -pte_internal;
214074fe6c29SRuslan Bukin 
214174fe6c29SRuslan Bukin 	offset = pt_msec_unmap(msec, decoder->ip);
214274fe6c29SRuslan Bukin 	status = pt_bcache_lookup(&bce, bcache, offset);
214374fe6c29SRuslan Bukin 	if (status < 0)
214474fe6c29SRuslan Bukin 		return status;
214574fe6c29SRuslan Bukin 
214674fe6c29SRuslan Bukin 	/* If we don't find a valid cache entry, fill the cache. */
214774fe6c29SRuslan Bukin 	if (!pt_bce_is_valid(bce))
214874fe6c29SRuslan Bukin 		return pt_blk_proceed_no_event_fill_cache(decoder, block,
214974fe6c29SRuslan Bukin 							  bcache, msec,
215074fe6c29SRuslan Bukin 							  bcache_fill_steps);
215174fe6c29SRuslan Bukin 
215274fe6c29SRuslan Bukin 	/* If we switched sections, the origianl section must have been split
215374fe6c29SRuslan Bukin 	 * underneath us.  A split preserves the block cache of the original
215474fe6c29SRuslan Bukin 	 * section.
215574fe6c29SRuslan Bukin 	 *
215674fe6c29SRuslan Bukin 	 * Crossing sections requires ending the block so we can indicate the
215774fe6c29SRuslan Bukin 	 * proper isid for the entire block.
215874fe6c29SRuslan Bukin 	 *
215974fe6c29SRuslan Bukin 	 * Plus there's the chance that the new section that caused the original
216074fe6c29SRuslan Bukin 	 * section to split changed instructions.
216174fe6c29SRuslan Bukin 	 *
216274fe6c29SRuslan Bukin 	 * This check will also cover changes to a linear sequence of code we
216374fe6c29SRuslan Bukin 	 * would otherwise have jumped over as long as the start and end are in
216474fe6c29SRuslan Bukin 	 * different sub-sections.
216574fe6c29SRuslan Bukin 	 *
216674fe6c29SRuslan Bukin 	 * Since we stop on every (backwards) branch (through an artificial stop
216774fe6c29SRuslan Bukin 	 * in the case of a near direct backward branch) we will detect all
216874fe6c29SRuslan Bukin 	 * section splits.
216974fe6c29SRuslan Bukin 	 *
217074fe6c29SRuslan Bukin 	 * Switch to the slow path until we reach the end of this section.
217174fe6c29SRuslan Bukin 	 */
2172*85f87cf4SRuslan Bukin 	nip = decoder->ip + (uint64_t) (int64_t) bce.displacement;
217374fe6c29SRuslan Bukin 	if (!pt_blk_is_in_section(msec, nip))
217474fe6c29SRuslan Bukin 		return pt_blk_proceed_no_event_uncached(decoder, block);
217574fe6c29SRuslan Bukin 
217674fe6c29SRuslan Bukin 	/* We have a valid cache entry.  Let's first check if the way to the
217774fe6c29SRuslan Bukin 	 * decision point still fits into @block.
217874fe6c29SRuslan Bukin 	 *
217974fe6c29SRuslan Bukin 	 * If it doesn't, we end the block without filling it as much as we
218074fe6c29SRuslan Bukin 	 * could since this would require us to switch to the slow path.
218174fe6c29SRuslan Bukin 	 *
218274fe6c29SRuslan Bukin 	 * On the next iteration, we will start with an empty block, which is
218374fe6c29SRuslan Bukin 	 * guaranteed to have enough room for at least one block cache entry.
218474fe6c29SRuslan Bukin 	 */
218574fe6c29SRuslan Bukin 	binsn = block->ninsn;
218674fe6c29SRuslan Bukin 	ninsn = binsn + (uint16_t) bce.ninsn;
218774fe6c29SRuslan Bukin 	if (ninsn < binsn)
218874fe6c29SRuslan Bukin 		return 0;
218974fe6c29SRuslan Bukin 
219074fe6c29SRuslan Bukin 	/* Jump ahead to the decision point and proceed from there.
219174fe6c29SRuslan Bukin 	 *
219274fe6c29SRuslan Bukin 	 * We're not switching execution modes so even if @block already has an
219374fe6c29SRuslan Bukin 	 * execution mode, it will be the one we're going to set.
219474fe6c29SRuslan Bukin 	 */
219574fe6c29SRuslan Bukin 	decoder->ip = nip;
219674fe6c29SRuslan Bukin 
219774fe6c29SRuslan Bukin 	/* We don't know the instruction class so we should be setting it to
219874fe6c29SRuslan Bukin 	 * ptic_error.  Since we will be able to fill it back in later in most
219974fe6c29SRuslan Bukin 	 * cases, we move the clearing to the switch cases that don't.
220074fe6c29SRuslan Bukin 	 */
220174fe6c29SRuslan Bukin 	block->end_ip = nip;
220274fe6c29SRuslan Bukin 	block->ninsn = ninsn;
220374fe6c29SRuslan Bukin 	block->mode = pt_bce_exec_mode(bce);
220474fe6c29SRuslan Bukin 
220574fe6c29SRuslan Bukin 
220674fe6c29SRuslan Bukin 	switch (pt_bce_qualifier(bce)) {
220774fe6c29SRuslan Bukin 	case ptbq_again:
220874fe6c29SRuslan Bukin 		/* We're not able to reach the actual decision point due to
220974fe6c29SRuslan Bukin 		 * overflows so we inserted a trampoline.
221074fe6c29SRuslan Bukin 		 *
221174fe6c29SRuslan Bukin 		 * We don't know the instruction and it is not guaranteed that
221274fe6c29SRuslan Bukin 		 * we will proceed further (e.g. if @block overflowed).  Let's
221374fe6c29SRuslan Bukin 		 * clear any previously stored instruction class which has
221474fe6c29SRuslan Bukin 		 * become invalid when we updated @block->ninsn.
221574fe6c29SRuslan Bukin 		 */
221674fe6c29SRuslan Bukin 		block->iclass = ptic_error;
221774fe6c29SRuslan Bukin 
221874fe6c29SRuslan Bukin 		return pt_blk_proceed_no_event_cached(decoder, block, bcache,
221974fe6c29SRuslan Bukin 						      msec);
222074fe6c29SRuslan Bukin 
222174fe6c29SRuslan Bukin 	case ptbq_cond:
222274fe6c29SRuslan Bukin 		/* We're at a conditional branch. */
222374fe6c29SRuslan Bukin 		block->iclass = ptic_cond_jump;
222474fe6c29SRuslan Bukin 
222574fe6c29SRuslan Bukin 		/* Let's first check whether we know the size of the
222674fe6c29SRuslan Bukin 		 * instruction.  If we do, we might get away without decoding
222774fe6c29SRuslan Bukin 		 * the instruction.
222874fe6c29SRuslan Bukin 		 *
222974fe6c29SRuslan Bukin 		 * If we don't know the size we might as well do the full decode
223074fe6c29SRuslan Bukin 		 * and proceed-with-trace flow we do for ptbq_decode.
223174fe6c29SRuslan Bukin 		 */
223274fe6c29SRuslan Bukin 		if (bce.isize) {
223374fe6c29SRuslan Bukin 			uint64_t ip;
223474fe6c29SRuslan Bukin 			int taken;
223574fe6c29SRuslan Bukin 
223674fe6c29SRuslan Bukin 			/* If the branch is not taken, we don't need to decode
223774fe6c29SRuslan Bukin 			 * the instruction at @decoder->ip.
223874fe6c29SRuslan Bukin 			 *
223974fe6c29SRuslan Bukin 			 * If it is taken, we have to implement everything here.
224074fe6c29SRuslan Bukin 			 * We can't use the normal decode and proceed-with-trace
224174fe6c29SRuslan Bukin 			 * flow since we already consumed the TNT bit.
224274fe6c29SRuslan Bukin 			 */
224374fe6c29SRuslan Bukin 			status = pt_blk_cond_branch(decoder, &taken);
224474fe6c29SRuslan Bukin 			if (status < 0)
224574fe6c29SRuslan Bukin 				return status;
224674fe6c29SRuslan Bukin 
224774fe6c29SRuslan Bukin 			/* Preserve the query decoder's response which indicates
224874fe6c29SRuslan Bukin 			 * upcoming events.
224974fe6c29SRuslan Bukin 			 */
225074fe6c29SRuslan Bukin 			decoder->status = status;
225174fe6c29SRuslan Bukin 
225274fe6c29SRuslan Bukin 			ip = decoder->ip;
225374fe6c29SRuslan Bukin 			if (taken) {
225474fe6c29SRuslan Bukin 				struct pt_insn_ext iext;
225574fe6c29SRuslan Bukin 				struct pt_insn insn;
225674fe6c29SRuslan Bukin 
225774fe6c29SRuslan Bukin 				memset(&iext, 0, sizeof(iext));
225874fe6c29SRuslan Bukin 				memset(&insn, 0, sizeof(insn));
225974fe6c29SRuslan Bukin 
226074fe6c29SRuslan Bukin 				insn.mode = pt_bce_exec_mode(bce);
226174fe6c29SRuslan Bukin 				insn.ip = ip;
226274fe6c29SRuslan Bukin 
226374fe6c29SRuslan Bukin 				status = pt_blk_decode_in_section(&insn, &iext,
226474fe6c29SRuslan Bukin 								  msec);
226574fe6c29SRuslan Bukin 				if (status < 0)
226674fe6c29SRuslan Bukin 					return status;
226774fe6c29SRuslan Bukin 
2268*85f87cf4SRuslan Bukin 				ip += (uint64_t) (int64_t)
2269*85f87cf4SRuslan Bukin 					iext.variant.branch.displacement;
227074fe6c29SRuslan Bukin 			}
227174fe6c29SRuslan Bukin 
227274fe6c29SRuslan Bukin 			decoder->ip = ip + bce.isize;
227374fe6c29SRuslan Bukin 			break;
227474fe6c29SRuslan Bukin 		}
227574fe6c29SRuslan Bukin 
227674fe6c29SRuslan Bukin 		fallthrough;
227774fe6c29SRuslan Bukin 	case ptbq_decode: {
227874fe6c29SRuslan Bukin 		struct pt_insn_ext iext;
227974fe6c29SRuslan Bukin 		struct pt_insn insn;
228074fe6c29SRuslan Bukin 
228174fe6c29SRuslan Bukin 		/* We need to decode the instruction at @decoder->ip and decide
228274fe6c29SRuslan Bukin 		 * what to do based on that.
228374fe6c29SRuslan Bukin 		 *
228474fe6c29SRuslan Bukin 		 * We already accounted for the instruction so we can't just
228574fe6c29SRuslan Bukin 		 * call pt_blk_proceed_one_insn().
228674fe6c29SRuslan Bukin 		 */
228774fe6c29SRuslan Bukin 
228874fe6c29SRuslan Bukin 		memset(&iext, 0, sizeof(iext));
228974fe6c29SRuslan Bukin 		memset(&insn, 0, sizeof(insn));
229074fe6c29SRuslan Bukin 
229174fe6c29SRuslan Bukin 		insn.mode = pt_bce_exec_mode(bce);
229274fe6c29SRuslan Bukin 		insn.ip = decoder->ip;
229374fe6c29SRuslan Bukin 
229474fe6c29SRuslan Bukin 		status = pt_blk_decode_in_section(&insn, &iext, msec);
229574fe6c29SRuslan Bukin 		if (status < 0) {
229674fe6c29SRuslan Bukin 			if (status != -pte_bad_insn)
229774fe6c29SRuslan Bukin 				return status;
229874fe6c29SRuslan Bukin 
229974fe6c29SRuslan Bukin 			return pt_blk_proceed_truncated(decoder, block);
230074fe6c29SRuslan Bukin 		}
230174fe6c29SRuslan Bukin 
230274fe6c29SRuslan Bukin 		/* We just decoded @insn so we know the instruction class. */
230374fe6c29SRuslan Bukin 		block->iclass = insn.iclass;
230474fe6c29SRuslan Bukin 
230574fe6c29SRuslan Bukin 		/* Log calls' return addresses for return compression. */
230674fe6c29SRuslan Bukin 		status = pt_blk_log_call(decoder, &insn, &iext);
230774fe6c29SRuslan Bukin 		if (status < 0)
230874fe6c29SRuslan Bukin 			return status;
230974fe6c29SRuslan Bukin 
231074fe6c29SRuslan Bukin 		/* Let's see if we can proceed to the next IP without trace.
231174fe6c29SRuslan Bukin 		 *
231274fe6c29SRuslan Bukin 		 * Note that we also stop due to displacement overflows or to
231374fe6c29SRuslan Bukin 		 * maintain the return-address stack for near direct calls.
231474fe6c29SRuslan Bukin 		 */
231574fe6c29SRuslan Bukin 		status = pt_insn_next_ip(&decoder->ip, &insn, &iext);
231674fe6c29SRuslan Bukin 		if (status < 0) {
231774fe6c29SRuslan Bukin 			if (status != -pte_bad_query)
231874fe6c29SRuslan Bukin 				return status;
231974fe6c29SRuslan Bukin 
232074fe6c29SRuslan Bukin 			/* We can't, so let's proceed with trace, which
232174fe6c29SRuslan Bukin 			 * completes the block.
232274fe6c29SRuslan Bukin 			 */
232374fe6c29SRuslan Bukin 			return pt_blk_proceed_with_trace(decoder, &insn, &iext);
232474fe6c29SRuslan Bukin 		}
232574fe6c29SRuslan Bukin 
232674fe6c29SRuslan Bukin 		/* End the block if the user asked us to.
232774fe6c29SRuslan Bukin 		 *
232874fe6c29SRuslan Bukin 		 * We only need to take care about direct near branches.
232974fe6c29SRuslan Bukin 		 * Indirect and far branches require trace and will naturally
233074fe6c29SRuslan Bukin 		 * end a block.
233174fe6c29SRuslan Bukin 		 */
233274fe6c29SRuslan Bukin 		if ((decoder->flags.variant.block.end_on_call &&
233374fe6c29SRuslan Bukin 		     (insn.iclass == ptic_call)) ||
233474fe6c29SRuslan Bukin 		    (decoder->flags.variant.block.end_on_jump &&
233574fe6c29SRuslan Bukin 		     (insn.iclass == ptic_jump)))
233674fe6c29SRuslan Bukin 			break;
233774fe6c29SRuslan Bukin 
233874fe6c29SRuslan Bukin 		/* If we can proceed without trace and we stay in @msec we may
233974fe6c29SRuslan Bukin 		 * proceed further.
234074fe6c29SRuslan Bukin 		 *
234174fe6c29SRuslan Bukin 		 * We're done if we switch sections, though.
234274fe6c29SRuslan Bukin 		 */
234374fe6c29SRuslan Bukin 		if (!pt_blk_is_in_section(msec, decoder->ip))
234474fe6c29SRuslan Bukin 			break;
234574fe6c29SRuslan Bukin 
234674fe6c29SRuslan Bukin 		return pt_blk_proceed_no_event_cached(decoder, block, bcache,
234774fe6c29SRuslan Bukin 						      msec);
234874fe6c29SRuslan Bukin 	}
234974fe6c29SRuslan Bukin 
235074fe6c29SRuslan Bukin 	case ptbq_ind_call: {
235174fe6c29SRuslan Bukin 		uint64_t ip;
235274fe6c29SRuslan Bukin 
235374fe6c29SRuslan Bukin 		/* We're at a near indirect call. */
235474fe6c29SRuslan Bukin 		block->iclass = ptic_call;
235574fe6c29SRuslan Bukin 
235674fe6c29SRuslan Bukin 		/* We need to update the return-address stack and query the
235774fe6c29SRuslan Bukin 		 * destination IP.
235874fe6c29SRuslan Bukin 		 */
235974fe6c29SRuslan Bukin 		ip = decoder->ip;
236074fe6c29SRuslan Bukin 
236174fe6c29SRuslan Bukin 		/* If we already know the size of the instruction, we don't need
236274fe6c29SRuslan Bukin 		 * to re-decode it.
236374fe6c29SRuslan Bukin 		 */
236474fe6c29SRuslan Bukin 		if (bce.isize)
236574fe6c29SRuslan Bukin 			ip += bce.isize;
236674fe6c29SRuslan Bukin 		else {
236774fe6c29SRuslan Bukin 			struct pt_insn_ext iext;
236874fe6c29SRuslan Bukin 			struct pt_insn insn;
236974fe6c29SRuslan Bukin 
237074fe6c29SRuslan Bukin 			memset(&iext, 0, sizeof(iext));
237174fe6c29SRuslan Bukin 			memset(&insn, 0, sizeof(insn));
237274fe6c29SRuslan Bukin 
237374fe6c29SRuslan Bukin 			insn.mode = pt_bce_exec_mode(bce);
237474fe6c29SRuslan Bukin 			insn.ip = ip;
237574fe6c29SRuslan Bukin 
237674fe6c29SRuslan Bukin 			status = pt_blk_decode_in_section(&insn, &iext, msec);
237774fe6c29SRuslan Bukin 			if (status < 0)
237874fe6c29SRuslan Bukin 				return status;
237974fe6c29SRuslan Bukin 
238074fe6c29SRuslan Bukin 			ip += insn.size;
238174fe6c29SRuslan Bukin 		}
238274fe6c29SRuslan Bukin 
238374fe6c29SRuslan Bukin 		status = pt_retstack_push(&decoder->retstack, ip);
238474fe6c29SRuslan Bukin 		if (status < 0)
238574fe6c29SRuslan Bukin 			return status;
238674fe6c29SRuslan Bukin 
238774fe6c29SRuslan Bukin 		status = pt_blk_indirect_branch(decoder, &decoder->ip);
238874fe6c29SRuslan Bukin 		if (status < 0)
238974fe6c29SRuslan Bukin 			return status;
239074fe6c29SRuslan Bukin 
239174fe6c29SRuslan Bukin 		/* Preserve the query decoder's response which indicates
239274fe6c29SRuslan Bukin 		 * upcoming events.
239374fe6c29SRuslan Bukin 		 */
239474fe6c29SRuslan Bukin 		decoder->status = status;
239574fe6c29SRuslan Bukin 		break;
239674fe6c29SRuslan Bukin 	}
239774fe6c29SRuslan Bukin 
239874fe6c29SRuslan Bukin 	case ptbq_return: {
239974fe6c29SRuslan Bukin 		int taken;
240074fe6c29SRuslan Bukin 
240174fe6c29SRuslan Bukin 		/* We're at a near return. */
240274fe6c29SRuslan Bukin 		block->iclass = ptic_return;
240374fe6c29SRuslan Bukin 
240474fe6c29SRuslan Bukin 		/* Check for a compressed return. */
240574fe6c29SRuslan Bukin 		status = pt_blk_cond_branch(decoder, &taken);
240674fe6c29SRuslan Bukin 		if (status < 0) {
240774fe6c29SRuslan Bukin 			if (status != -pte_bad_query)
240874fe6c29SRuslan Bukin 				return status;
240974fe6c29SRuslan Bukin 
241074fe6c29SRuslan Bukin 			/* The return is not compressed.  We need another query
241174fe6c29SRuslan Bukin 			 * to determine the destination IP.
241274fe6c29SRuslan Bukin 			 */
241374fe6c29SRuslan Bukin 			status = pt_blk_indirect_branch(decoder, &decoder->ip);
241474fe6c29SRuslan Bukin 			if (status < 0)
241574fe6c29SRuslan Bukin 				return status;
241674fe6c29SRuslan Bukin 
241774fe6c29SRuslan Bukin 			/* Preserve the query decoder's response which indicates
241874fe6c29SRuslan Bukin 			 * upcoming events.
241974fe6c29SRuslan Bukin 			 */
242074fe6c29SRuslan Bukin 			decoder->status = status;
242174fe6c29SRuslan Bukin 			break;
242274fe6c29SRuslan Bukin 		}
242374fe6c29SRuslan Bukin 
242474fe6c29SRuslan Bukin 		/* Preserve the query decoder's response which indicates
242574fe6c29SRuslan Bukin 		 * upcoming events.
242674fe6c29SRuslan Bukin 		 */
242774fe6c29SRuslan Bukin 		decoder->status = status;
242874fe6c29SRuslan Bukin 
242974fe6c29SRuslan Bukin 		/* A compressed return is indicated by a taken conditional
243074fe6c29SRuslan Bukin 		 * branch.
243174fe6c29SRuslan Bukin 		 */
243274fe6c29SRuslan Bukin 		if (!taken)
243374fe6c29SRuslan Bukin 			return -pte_bad_retcomp;
243474fe6c29SRuslan Bukin 
243574fe6c29SRuslan Bukin 		return pt_retstack_pop(&decoder->retstack, &decoder->ip);
243674fe6c29SRuslan Bukin 	}
243774fe6c29SRuslan Bukin 
243874fe6c29SRuslan Bukin 	case ptbq_indirect:
243974fe6c29SRuslan Bukin 		/* We're at an indirect jump or far transfer.
244074fe6c29SRuslan Bukin 		 *
244174fe6c29SRuslan Bukin 		 * We don't know the exact instruction class and there's no
244274fe6c29SRuslan Bukin 		 * reason to decode the instruction for any other purpose.
244374fe6c29SRuslan Bukin 		 *
244474fe6c29SRuslan Bukin 		 * Indicate that we don't know the instruction class and leave
244574fe6c29SRuslan Bukin 		 * it to our caller to decode the instruction if needed.
244674fe6c29SRuslan Bukin 		 */
244774fe6c29SRuslan Bukin 		block->iclass = ptic_error;
244874fe6c29SRuslan Bukin 
244974fe6c29SRuslan Bukin 		/* This is neither a near call nor return so we don't need to
245074fe6c29SRuslan Bukin 		 * touch the return-address stack.
245174fe6c29SRuslan Bukin 		 *
245274fe6c29SRuslan Bukin 		 * Just query the destination IP.
245374fe6c29SRuslan Bukin 		 */
245474fe6c29SRuslan Bukin 		status = pt_blk_indirect_branch(decoder, &decoder->ip);
245574fe6c29SRuslan Bukin 		if (status < 0)
245674fe6c29SRuslan Bukin 			return status;
245774fe6c29SRuslan Bukin 
245874fe6c29SRuslan Bukin 		/* Preserve the query decoder's response which indicates
245974fe6c29SRuslan Bukin 		 * upcoming events.
246074fe6c29SRuslan Bukin 		 */
246174fe6c29SRuslan Bukin 		decoder->status = status;
246274fe6c29SRuslan Bukin 		break;
246374fe6c29SRuslan Bukin 	}
246474fe6c29SRuslan Bukin 
246574fe6c29SRuslan Bukin 	return 0;
246674fe6c29SRuslan Bukin }
246774fe6c29SRuslan Bukin 
pt_blk_msec_fill(struct pt_block_decoder * decoder,const struct pt_mapped_section ** pmsec)246874fe6c29SRuslan Bukin static int pt_blk_msec_fill(struct pt_block_decoder *decoder,
246974fe6c29SRuslan Bukin 			    const struct pt_mapped_section **pmsec)
247074fe6c29SRuslan Bukin {
247174fe6c29SRuslan Bukin 	const struct pt_mapped_section *msec;
247274fe6c29SRuslan Bukin 	struct pt_section *section;
247374fe6c29SRuslan Bukin 	int isid, errcode;
247474fe6c29SRuslan Bukin 
247574fe6c29SRuslan Bukin 	if (!decoder || !pmsec)
247674fe6c29SRuslan Bukin 		return -pte_internal;
247774fe6c29SRuslan Bukin 
247874fe6c29SRuslan Bukin 	isid = pt_msec_cache_fill(&decoder->scache, &msec,  decoder->image,
247974fe6c29SRuslan Bukin 				  &decoder->asid, decoder->ip);
248074fe6c29SRuslan Bukin 	if (isid < 0)
248174fe6c29SRuslan Bukin 		return isid;
248274fe6c29SRuslan Bukin 
248374fe6c29SRuslan Bukin 	section = pt_msec_section(msec);
248474fe6c29SRuslan Bukin 	if (!section)
248574fe6c29SRuslan Bukin 		return -pte_internal;
248674fe6c29SRuslan Bukin 
248774fe6c29SRuslan Bukin 	*pmsec = msec;
248874fe6c29SRuslan Bukin 
248974fe6c29SRuslan Bukin 	errcode = pt_section_request_bcache(section);
249074fe6c29SRuslan Bukin 	if (errcode < 0)
249174fe6c29SRuslan Bukin 		return errcode;
249274fe6c29SRuslan Bukin 
249374fe6c29SRuslan Bukin 	return isid;
249474fe6c29SRuslan Bukin }
249574fe6c29SRuslan Bukin 
pt_blk_msec_lookup(struct pt_block_decoder * decoder,const struct pt_mapped_section ** pmsec)249674fe6c29SRuslan Bukin static inline int pt_blk_msec_lookup(struct pt_block_decoder *decoder,
249774fe6c29SRuslan Bukin 				     const struct pt_mapped_section **pmsec)
249874fe6c29SRuslan Bukin {
249974fe6c29SRuslan Bukin 	int isid;
250074fe6c29SRuslan Bukin 
250174fe6c29SRuslan Bukin 	if (!decoder)
250274fe6c29SRuslan Bukin 		return -pte_internal;
250374fe6c29SRuslan Bukin 
250474fe6c29SRuslan Bukin 	isid = pt_msec_cache_read(&decoder->scache, pmsec, decoder->image,
250574fe6c29SRuslan Bukin 				  decoder->ip);
250674fe6c29SRuslan Bukin 	if (isid < 0) {
250774fe6c29SRuslan Bukin 		if (isid != -pte_nomap)
250874fe6c29SRuslan Bukin 			return isid;
250974fe6c29SRuslan Bukin 
251074fe6c29SRuslan Bukin 		return pt_blk_msec_fill(decoder, pmsec);
251174fe6c29SRuslan Bukin 	}
251274fe6c29SRuslan Bukin 
251374fe6c29SRuslan Bukin 	return isid;
251474fe6c29SRuslan Bukin }
251574fe6c29SRuslan Bukin 
251674fe6c29SRuslan Bukin /* Proceed to the next decision point - try using the cache.
251774fe6c29SRuslan Bukin  *
251874fe6c29SRuslan Bukin  * Tracing is enabled and we don't have an event pending.  Proceed as far as
251974fe6c29SRuslan Bukin  * we get without trace.  Stop when we either:
252074fe6c29SRuslan Bukin  *
252174fe6c29SRuslan Bukin  *   - need trace in order to continue
252274fe6c29SRuslan Bukin  *   - overflow the max number of instructions in a block
252374fe6c29SRuslan Bukin  *
252474fe6c29SRuslan Bukin  * We actually proceed one instruction further to get the start IP for the next
252574fe6c29SRuslan Bukin  * block.  This only updates @decoder's internal state, though.
252674fe6c29SRuslan Bukin  *
252774fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
252874fe6c29SRuslan Bukin  */
pt_blk_proceed_no_event(struct pt_block_decoder * decoder,struct pt_block * block)252974fe6c29SRuslan Bukin static int pt_blk_proceed_no_event(struct pt_block_decoder *decoder,
253074fe6c29SRuslan Bukin 				   struct pt_block *block)
253174fe6c29SRuslan Bukin {
253274fe6c29SRuslan Bukin 	const struct pt_mapped_section *msec;
253374fe6c29SRuslan Bukin 	struct pt_block_cache *bcache;
253474fe6c29SRuslan Bukin 	struct pt_section *section;
253574fe6c29SRuslan Bukin 	int isid;
253674fe6c29SRuslan Bukin 
253774fe6c29SRuslan Bukin 	if (!decoder || !block)
253874fe6c29SRuslan Bukin 		return -pte_internal;
253974fe6c29SRuslan Bukin 
254074fe6c29SRuslan Bukin 	isid = pt_blk_msec_lookup(decoder, &msec);
254174fe6c29SRuslan Bukin 	if (isid < 0) {
254274fe6c29SRuslan Bukin 		if (isid != -pte_nomap)
254374fe6c29SRuslan Bukin 			return isid;
254474fe6c29SRuslan Bukin 
254574fe6c29SRuslan Bukin 		/* Even if there is no such section in the image, we may still
254674fe6c29SRuslan Bukin 		 * read the memory via the callback function.
254774fe6c29SRuslan Bukin 		 */
254874fe6c29SRuslan Bukin 		return pt_blk_proceed_no_event_uncached(decoder, block);
254974fe6c29SRuslan Bukin 	}
255074fe6c29SRuslan Bukin 
255174fe6c29SRuslan Bukin 	/* We do not switch sections inside a block. */
255274fe6c29SRuslan Bukin 	if (isid != block->isid) {
255374fe6c29SRuslan Bukin 		if (!pt_blk_block_is_empty(block))
255474fe6c29SRuslan Bukin 			return 0;
255574fe6c29SRuslan Bukin 
255674fe6c29SRuslan Bukin 		block->isid = isid;
255774fe6c29SRuslan Bukin 	}
255874fe6c29SRuslan Bukin 
255974fe6c29SRuslan Bukin 	section = pt_msec_section(msec);
256074fe6c29SRuslan Bukin 	if (!section)
256174fe6c29SRuslan Bukin 		return -pte_internal;
256274fe6c29SRuslan Bukin 
256374fe6c29SRuslan Bukin 	bcache = pt_section_bcache(section);
256474fe6c29SRuslan Bukin 	if (!bcache)
256574fe6c29SRuslan Bukin 		return pt_blk_proceed_no_event_uncached(decoder, block);
256674fe6c29SRuslan Bukin 
256774fe6c29SRuslan Bukin 	return pt_blk_proceed_no_event_cached(decoder, block, bcache, msec);
256874fe6c29SRuslan Bukin }
256974fe6c29SRuslan Bukin 
257074fe6c29SRuslan Bukin /* Proceed to the next event or decision point.
257174fe6c29SRuslan Bukin  *
257274fe6c29SRuslan Bukin  * Returns a non-negative pt_status_flag bit-vector on success, a negative error
257374fe6c29SRuslan Bukin  * code otherwise.
257474fe6c29SRuslan Bukin  */
pt_blk_proceed(struct pt_block_decoder * decoder,struct pt_block * block)257574fe6c29SRuslan Bukin static int pt_blk_proceed(struct pt_block_decoder *decoder,
257674fe6c29SRuslan Bukin 			  struct pt_block *block)
257774fe6c29SRuslan Bukin {
257874fe6c29SRuslan Bukin 	int status;
257974fe6c29SRuslan Bukin 
258074fe6c29SRuslan Bukin 	status = pt_blk_fetch_event(decoder);
258174fe6c29SRuslan Bukin 	if (status != 0) {
258274fe6c29SRuslan Bukin 		if (status < 0)
258374fe6c29SRuslan Bukin 			return status;
258474fe6c29SRuslan Bukin 
258574fe6c29SRuslan Bukin 		return pt_blk_proceed_event(decoder, block);
258674fe6c29SRuslan Bukin 	}
258774fe6c29SRuslan Bukin 
258874fe6c29SRuslan Bukin 	/* If tracing is disabled we should either be out of trace or we should
258974fe6c29SRuslan Bukin 	 * have taken the event flow above.
259074fe6c29SRuslan Bukin 	 */
259174fe6c29SRuslan Bukin 	if (!decoder->enabled) {
259274fe6c29SRuslan Bukin 		if (decoder->status & pts_eos)
259374fe6c29SRuslan Bukin 			return -pte_eos;
259474fe6c29SRuslan Bukin 
259574fe6c29SRuslan Bukin 		return -pte_no_enable;
259674fe6c29SRuslan Bukin 	}
259774fe6c29SRuslan Bukin 
259874fe6c29SRuslan Bukin 	status = pt_blk_proceed_no_event(decoder, block);
259974fe6c29SRuslan Bukin 	if (status < 0)
260074fe6c29SRuslan Bukin 		return status;
260174fe6c29SRuslan Bukin 
260274fe6c29SRuslan Bukin 	return pt_blk_proceed_trailing_event(decoder, block);
260374fe6c29SRuslan Bukin }
260474fe6c29SRuslan Bukin 
260574fe6c29SRuslan Bukin enum {
260674fe6c29SRuslan Bukin 	/* The maximum number of steps to take when determining whether the
260774fe6c29SRuslan Bukin 	 * event location can be reached.
260874fe6c29SRuslan Bukin 	 */
260974fe6c29SRuslan Bukin 	bdm64_max_steps	= 0x100
261074fe6c29SRuslan Bukin };
261174fe6c29SRuslan Bukin 
261274fe6c29SRuslan Bukin /* Try to work around erratum BDM64.
261374fe6c29SRuslan Bukin  *
261474fe6c29SRuslan Bukin  * If we got a transaction abort immediately following a branch that produced
261574fe6c29SRuslan Bukin  * trace, the trace for that branch might have been corrupted.
261674fe6c29SRuslan Bukin  *
261774fe6c29SRuslan Bukin  * Returns a positive integer if the erratum was handled.
261874fe6c29SRuslan Bukin  * Returns zero if the erratum does not seem to apply.
261974fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
262074fe6c29SRuslan Bukin  */
pt_blk_handle_erratum_bdm64(struct pt_block_decoder * decoder,const struct pt_block * block,const struct pt_event * ev)262174fe6c29SRuslan Bukin static int pt_blk_handle_erratum_bdm64(struct pt_block_decoder *decoder,
262274fe6c29SRuslan Bukin 				       const struct pt_block *block,
262374fe6c29SRuslan Bukin 				       const struct pt_event *ev)
262474fe6c29SRuslan Bukin {
262574fe6c29SRuslan Bukin 	struct pt_insn_ext iext;
262674fe6c29SRuslan Bukin 	struct pt_insn insn;
262774fe6c29SRuslan Bukin 	int status;
262874fe6c29SRuslan Bukin 
262974fe6c29SRuslan Bukin 	if (!decoder || !block || !ev)
263074fe6c29SRuslan Bukin 		return -pte_internal;
263174fe6c29SRuslan Bukin 
263274fe6c29SRuslan Bukin 	/* This only affects aborts. */
263374fe6c29SRuslan Bukin 	if (!ev->variant.tsx.aborted)
263474fe6c29SRuslan Bukin 		return 0;
263574fe6c29SRuslan Bukin 
263674fe6c29SRuslan Bukin 	/* This only affects branches that require trace.
263774fe6c29SRuslan Bukin 	 *
263874fe6c29SRuslan Bukin 	 * If the erratum hits, that branch ended the current block and brought
263974fe6c29SRuslan Bukin 	 * us to the trailing event flow.
264074fe6c29SRuslan Bukin 	 */
264174fe6c29SRuslan Bukin 	if (pt_blk_block_is_empty(block))
264274fe6c29SRuslan Bukin 		return 0;
264374fe6c29SRuslan Bukin 
264474fe6c29SRuslan Bukin 	insn.mode = block->mode;
264574fe6c29SRuslan Bukin 	insn.ip = block->end_ip;
264674fe6c29SRuslan Bukin 
264774fe6c29SRuslan Bukin 	status = pt_insn_decode(&insn, &iext, decoder->image, &decoder->asid);
264874fe6c29SRuslan Bukin 	if (status < 0)
264974fe6c29SRuslan Bukin 		return 0;
265074fe6c29SRuslan Bukin 
265174fe6c29SRuslan Bukin 	if (!pt_insn_is_branch(&insn, &iext))
265274fe6c29SRuslan Bukin 		return 0;
265374fe6c29SRuslan Bukin 
265474fe6c29SRuslan Bukin 	/* Let's check if we can reach the event location from here.
265574fe6c29SRuslan Bukin 	 *
265674fe6c29SRuslan Bukin 	 * If we can, let's assume the erratum did not hit.  We might still be
265774fe6c29SRuslan Bukin 	 * wrong but we're not able to tell.
265874fe6c29SRuslan Bukin 	 */
265974fe6c29SRuslan Bukin 	status = pt_insn_range_is_contiguous(decoder->ip, ev->variant.tsx.ip,
266074fe6c29SRuslan Bukin 					     decoder->mode, decoder->image,
266174fe6c29SRuslan Bukin 					     &decoder->asid, bdm64_max_steps);
266274fe6c29SRuslan Bukin 	if (status > 0)
266374fe6c29SRuslan Bukin 		return status;
266474fe6c29SRuslan Bukin 
266574fe6c29SRuslan Bukin 	/* We can't reach the event location.  This could either mean that we
266674fe6c29SRuslan Bukin 	 * stopped too early (and status is zero) or that the erratum hit.
266774fe6c29SRuslan Bukin 	 *
266874fe6c29SRuslan Bukin 	 * We assume the latter and pretend that the previous branch brought us
266974fe6c29SRuslan Bukin 	 * to the event location, instead.
267074fe6c29SRuslan Bukin 	 */
267174fe6c29SRuslan Bukin 	decoder->ip = ev->variant.tsx.ip;
267274fe6c29SRuslan Bukin 
267374fe6c29SRuslan Bukin 	return 1;
267474fe6c29SRuslan Bukin }
267574fe6c29SRuslan Bukin 
267674fe6c29SRuslan Bukin /* Check whether a trailing TSX event should be postponed.
267774fe6c29SRuslan Bukin  *
267874fe6c29SRuslan Bukin  * This involves handling erratum BDM64.
267974fe6c29SRuslan Bukin  *
268074fe6c29SRuslan Bukin  * Returns a positive integer if the event is to be postponed.
268174fe6c29SRuslan Bukin  * Returns zero if the event should be processed.
268274fe6c29SRuslan Bukin  * Returns a negative error code otherwise.
268374fe6c29SRuslan Bukin  */
pt_blk_postpone_trailing_tsx(struct pt_block_decoder * decoder,struct pt_block * block,const struct pt_event * ev)268474fe6c29SRuslan Bukin static inline int pt_blk_postpone_trailing_tsx(struct pt_block_decoder *decoder,
268574fe6c29SRuslan Bukin 					       struct pt_block *block,
268674fe6c29SRuslan Bukin 					       const struct pt_event *ev)
268774fe6c29SRuslan Bukin {
268874fe6c29SRuslan Bukin 	int status;
268974fe6c29SRuslan Bukin 
269074fe6c29SRuslan Bukin 	if (!decoder || !ev)
269174fe6c29SRuslan Bukin 		return -pte_internal;
269274fe6c29SRuslan Bukin 
269374fe6c29SRuslan Bukin 	if (ev->ip_suppressed)
269474fe6c29SRuslan Bukin 		return 0;
269574fe6c29SRuslan Bukin 
269674fe6c29SRuslan Bukin 	if (block && decoder->query.config.errata.bdm64) {
269774fe6c29SRuslan Bukin 		status = pt_blk_handle_erratum_bdm64(decoder, block, ev);
269874fe6c29SRuslan Bukin 		if (status < 0)
269974fe6c29SRuslan Bukin 			return 1;
270074fe6c29SRuslan Bukin 	}
270174fe6c29SRuslan Bukin 
270274fe6c29SRuslan Bukin 	if (decoder->ip != ev->variant.tsx.ip)
270374fe6c29SRuslan Bukin 		return 1;
270474fe6c29SRuslan Bukin 
270574fe6c29SRuslan Bukin 	return 0;
270674fe6c29SRuslan Bukin }
270774fe6c29SRuslan Bukin 
270874fe6c29SRuslan Bukin /* Proceed with events that bind to the current decoder IP.
270974fe6c29SRuslan Bukin  *
271074fe6c29SRuslan Bukin  * This function is used in the following scenarios:
271174fe6c29SRuslan Bukin  *
271274fe6c29SRuslan Bukin  *   - we just synchronized onto the trace stream
271374fe6c29SRuslan Bukin  *   - we ended a block and proceeded to the next IP
271474fe6c29SRuslan Bukin  *   - we processed an event that was indicated by this function
271574fe6c29SRuslan Bukin  *
271674fe6c29SRuslan Bukin  * Check if there is an event at the current IP that needs to be indicated to
271774fe6c29SRuslan Bukin  * the user.
271874fe6c29SRuslan Bukin  *
271974fe6c29SRuslan Bukin  * Returns a non-negative pt_status_flag bit-vector on success, a negative error
272074fe6c29SRuslan Bukin  * code otherwise.
272174fe6c29SRuslan Bukin  */
pt_blk_proceed_trailing_event(struct pt_block_decoder * decoder,struct pt_block * block)272274fe6c29SRuslan Bukin static int pt_blk_proceed_trailing_event(struct pt_block_decoder *decoder,
272374fe6c29SRuslan Bukin 					 struct pt_block *block)
272474fe6c29SRuslan Bukin {
272574fe6c29SRuslan Bukin 	struct pt_event *ev;
272674fe6c29SRuslan Bukin 	int status;
272774fe6c29SRuslan Bukin 
272874fe6c29SRuslan Bukin 	if (!decoder)
272974fe6c29SRuslan Bukin 		return -pte_internal;
273074fe6c29SRuslan Bukin 
273174fe6c29SRuslan Bukin 	status = pt_blk_fetch_event(decoder);
273274fe6c29SRuslan Bukin 	if (status <= 0) {
273374fe6c29SRuslan Bukin 		if (status < 0)
273474fe6c29SRuslan Bukin 			return status;
273574fe6c29SRuslan Bukin 
273674fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
273774fe6c29SRuslan Bukin 		if (status < 0)
273874fe6c29SRuslan Bukin 			return status;
273974fe6c29SRuslan Bukin 
274074fe6c29SRuslan Bukin 		return pt_blk_status(decoder, 0);
274174fe6c29SRuslan Bukin 	}
274274fe6c29SRuslan Bukin 
274374fe6c29SRuslan Bukin 	ev = &decoder->event;
274474fe6c29SRuslan Bukin 	switch (ev->type) {
274574fe6c29SRuslan Bukin 	case ptev_disabled:
274674fe6c29SRuslan Bukin 		/* Synchronous disable events are normally indicated on the
274774fe6c29SRuslan Bukin 		 * event flow.
274874fe6c29SRuslan Bukin 		 */
274974fe6c29SRuslan Bukin 		if (!decoder->process_insn)
275074fe6c29SRuslan Bukin 			break;
275174fe6c29SRuslan Bukin 
275274fe6c29SRuslan Bukin 		/* A sync disable may bind to a CR3 changing instruction. */
275374fe6c29SRuslan Bukin 		if (ev->ip_suppressed &&
275474fe6c29SRuslan Bukin 		    pt_insn_changes_cr3(&decoder->insn, &decoder->iext))
275574fe6c29SRuslan Bukin 			return pt_blk_status(decoder, pts_event_pending);
275674fe6c29SRuslan Bukin 
275774fe6c29SRuslan Bukin 		/* Or it binds to the next branch that would require trace.
275874fe6c29SRuslan Bukin 		 *
275974fe6c29SRuslan Bukin 		 * Try to complete processing the current instruction by
276074fe6c29SRuslan Bukin 		 * proceeding past it.  If that fails because it would require
276174fe6c29SRuslan Bukin 		 * trace, we can apply the disabled event.
276274fe6c29SRuslan Bukin 		 */
276374fe6c29SRuslan Bukin 		status = pt_insn_next_ip(&decoder->ip, &decoder->insn,
276474fe6c29SRuslan Bukin 					 &decoder->iext);
276574fe6c29SRuslan Bukin 		if (status < 0) {
276674fe6c29SRuslan Bukin 			if (status != -pte_bad_query)
276774fe6c29SRuslan Bukin 				return status;
276874fe6c29SRuslan Bukin 
276974fe6c29SRuslan Bukin 			status = pt_blk_set_disable_resume_ip(decoder,
277074fe6c29SRuslan Bukin 							      &decoder->insn);
277174fe6c29SRuslan Bukin 			if (status < 0)
277274fe6c29SRuslan Bukin 				return status;
277374fe6c29SRuslan Bukin 
277474fe6c29SRuslan Bukin 			return pt_blk_status(decoder, pts_event_pending);
277574fe6c29SRuslan Bukin 		}
277674fe6c29SRuslan Bukin 
277774fe6c29SRuslan Bukin 		/* We proceeded past the current instruction. */
277874fe6c29SRuslan Bukin 		status = pt_blk_clear_postponed_insn(decoder);
277974fe6c29SRuslan Bukin 		if (status < 0)
278074fe6c29SRuslan Bukin 			return status;
278174fe6c29SRuslan Bukin 
278274fe6c29SRuslan Bukin 		/* This might have brought us to the disable IP. */
278374fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
278474fe6c29SRuslan Bukin 		    decoder->ip == ev->variant.disabled.ip)
278574fe6c29SRuslan Bukin 			return pt_blk_status(decoder, pts_event_pending);
278674fe6c29SRuslan Bukin 
278774fe6c29SRuslan Bukin 		break;
278874fe6c29SRuslan Bukin 
278974fe6c29SRuslan Bukin 	case ptev_enabled:
279074fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
279174fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
279274fe6c29SRuslan Bukin 		if (status < 0)
279374fe6c29SRuslan Bukin 			return status;
279474fe6c29SRuslan Bukin 
279574fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
279674fe6c29SRuslan Bukin 
279774fe6c29SRuslan Bukin 	case ptev_async_disabled:
279874fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
279974fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
280074fe6c29SRuslan Bukin 		if (status < 0)
280174fe6c29SRuslan Bukin 			return status;
280274fe6c29SRuslan Bukin 
280374fe6c29SRuslan Bukin 		if (decoder->ip != ev->variant.async_disabled.at)
280474fe6c29SRuslan Bukin 			break;
280574fe6c29SRuslan Bukin 
280674fe6c29SRuslan Bukin 		if (decoder->query.config.errata.skd022) {
280774fe6c29SRuslan Bukin 			status = pt_blk_handle_erratum_skd022(decoder, ev);
280874fe6c29SRuslan Bukin 			if (status != 0) {
280974fe6c29SRuslan Bukin 				if (status < 0)
281074fe6c29SRuslan Bukin 					return status;
281174fe6c29SRuslan Bukin 
281274fe6c29SRuslan Bukin 				/* If the erratum applies, the event is modified
281374fe6c29SRuslan Bukin 				 * to a synchronous disable event that will be
281474fe6c29SRuslan Bukin 				 * processed on the next pt_blk_proceed_event()
281574fe6c29SRuslan Bukin 				 * call.  We're done.
281674fe6c29SRuslan Bukin 				 */
281774fe6c29SRuslan Bukin 				break;
281874fe6c29SRuslan Bukin 			}
281974fe6c29SRuslan Bukin 		}
282074fe6c29SRuslan Bukin 
282174fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
282274fe6c29SRuslan Bukin 
282374fe6c29SRuslan Bukin 	case ptev_async_branch:
282474fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
282574fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
282674fe6c29SRuslan Bukin 		if (status < 0)
282774fe6c29SRuslan Bukin 			return status;
282874fe6c29SRuslan Bukin 
282974fe6c29SRuslan Bukin 		if (decoder->ip != ev->variant.async_branch.from)
283074fe6c29SRuslan Bukin 			break;
283174fe6c29SRuslan Bukin 
283274fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
283374fe6c29SRuslan Bukin 
283474fe6c29SRuslan Bukin 	case ptev_paging:
283574fe6c29SRuslan Bukin 		/* We apply the event immediately if we're not tracing. */
283674fe6c29SRuslan Bukin 		if (!decoder->enabled)
283774fe6c29SRuslan Bukin 			return pt_blk_status(decoder, pts_event_pending);
283874fe6c29SRuslan Bukin 
283974fe6c29SRuslan Bukin 		/* Synchronous paging events are normally indicated on the event
284074fe6c29SRuslan Bukin 		 * flow, unless they bind to the same instruction as a previous
284174fe6c29SRuslan Bukin 		 * event.
284274fe6c29SRuslan Bukin 		 *
284374fe6c29SRuslan Bukin 		 * We bind at most one paging event to an instruction, though.
284474fe6c29SRuslan Bukin 		 */
284574fe6c29SRuslan Bukin 		if (!decoder->process_insn || decoder->bound_paging)
284674fe6c29SRuslan Bukin 			break;
284774fe6c29SRuslan Bukin 
284874fe6c29SRuslan Bukin 		/* We're done if we're not binding to the currently postponed
284974fe6c29SRuslan Bukin 		 * instruction.  We will process the event on the normal event
285074fe6c29SRuslan Bukin 		 * flow in the next iteration.
285174fe6c29SRuslan Bukin 		 */
285274fe6c29SRuslan Bukin 		if (!pt_insn_binds_to_pip(&decoder->insn, &decoder->iext))
285374fe6c29SRuslan Bukin 			break;
285474fe6c29SRuslan Bukin 
285574fe6c29SRuslan Bukin 		/* We bound a paging event.  Make sure we do not bind further
285674fe6c29SRuslan Bukin 		 * paging events to this instruction.
285774fe6c29SRuslan Bukin 		 */
285874fe6c29SRuslan Bukin 		decoder->bound_paging = 1;
285974fe6c29SRuslan Bukin 
286074fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
286174fe6c29SRuslan Bukin 
286274fe6c29SRuslan Bukin 	case ptev_async_paging:
286374fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
286474fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
286574fe6c29SRuslan Bukin 		if (status < 0)
286674fe6c29SRuslan Bukin 			return status;
286774fe6c29SRuslan Bukin 
286874fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
286974fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.async_paging.ip)
287074fe6c29SRuslan Bukin 			break;
287174fe6c29SRuslan Bukin 
287274fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
287374fe6c29SRuslan Bukin 
287474fe6c29SRuslan Bukin 	case ptev_vmcs:
287574fe6c29SRuslan Bukin 		/* We apply the event immediately if we're not tracing. */
287674fe6c29SRuslan Bukin 		if (!decoder->enabled)
287774fe6c29SRuslan Bukin 			return pt_blk_status(decoder, pts_event_pending);
287874fe6c29SRuslan Bukin 
287974fe6c29SRuslan Bukin 		/* Synchronous vmcs events are normally indicated on the event
288074fe6c29SRuslan Bukin 		 * flow, unless they bind to the same instruction as a previous
288174fe6c29SRuslan Bukin 		 * event.
288274fe6c29SRuslan Bukin 		 *
288374fe6c29SRuslan Bukin 		 * We bind at most one vmcs event to an instruction, though.
288474fe6c29SRuslan Bukin 		 */
288574fe6c29SRuslan Bukin 		if (!decoder->process_insn || decoder->bound_vmcs)
288674fe6c29SRuslan Bukin 			break;
288774fe6c29SRuslan Bukin 
288874fe6c29SRuslan Bukin 		/* We're done if we're not binding to the currently postponed
288974fe6c29SRuslan Bukin 		 * instruction.  We will process the event on the normal event
289074fe6c29SRuslan Bukin 		 * flow in the next iteration.
289174fe6c29SRuslan Bukin 		 */
289274fe6c29SRuslan Bukin 		if (!pt_insn_binds_to_vmcs(&decoder->insn, &decoder->iext))
289374fe6c29SRuslan Bukin 			break;
289474fe6c29SRuslan Bukin 
289574fe6c29SRuslan Bukin 		/* We bound a vmcs event.  Make sure we do not bind further vmcs
289674fe6c29SRuslan Bukin 		 * events to this instruction.
289774fe6c29SRuslan Bukin 		 */
289874fe6c29SRuslan Bukin 		decoder->bound_vmcs = 1;
289974fe6c29SRuslan Bukin 
290074fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
290174fe6c29SRuslan Bukin 
290274fe6c29SRuslan Bukin 	case ptev_async_vmcs:
290374fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
290474fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
290574fe6c29SRuslan Bukin 		if (status < 0)
290674fe6c29SRuslan Bukin 			return status;
290774fe6c29SRuslan Bukin 
290874fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
290974fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.async_vmcs.ip)
291074fe6c29SRuslan Bukin 			break;
291174fe6c29SRuslan Bukin 
291274fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
291374fe6c29SRuslan Bukin 
291474fe6c29SRuslan Bukin 	case ptev_overflow:
291574fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
291674fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
291774fe6c29SRuslan Bukin 		if (status < 0)
291874fe6c29SRuslan Bukin 			return status;
291974fe6c29SRuslan Bukin 
292074fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
292174fe6c29SRuslan Bukin 
292274fe6c29SRuslan Bukin 	case ptev_exec_mode:
292374fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
292474fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
292574fe6c29SRuslan Bukin 		if (status < 0)
292674fe6c29SRuslan Bukin 			return status;
292774fe6c29SRuslan Bukin 
292874fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
292974fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.exec_mode.ip)
293074fe6c29SRuslan Bukin 			break;
293174fe6c29SRuslan Bukin 
293274fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
293374fe6c29SRuslan Bukin 
293474fe6c29SRuslan Bukin 	case ptev_tsx:
293574fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
293674fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
293774fe6c29SRuslan Bukin 		if (status < 0)
293874fe6c29SRuslan Bukin 			return status;
293974fe6c29SRuslan Bukin 
294074fe6c29SRuslan Bukin 		status = pt_blk_postpone_trailing_tsx(decoder, block, ev);
294174fe6c29SRuslan Bukin 		if (status != 0) {
294274fe6c29SRuslan Bukin 			if (status < 0)
294374fe6c29SRuslan Bukin 				return status;
294474fe6c29SRuslan Bukin 
294574fe6c29SRuslan Bukin 			break;
294674fe6c29SRuslan Bukin 		}
294774fe6c29SRuslan Bukin 
294874fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
294974fe6c29SRuslan Bukin 
295074fe6c29SRuslan Bukin 	case ptev_stop:
295174fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
295274fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
295374fe6c29SRuslan Bukin 		if (status < 0)
295474fe6c29SRuslan Bukin 			return status;
295574fe6c29SRuslan Bukin 
295674fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
295774fe6c29SRuslan Bukin 
295874fe6c29SRuslan Bukin 	case ptev_exstop:
295974fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
296074fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
296174fe6c29SRuslan Bukin 		if (status < 0)
296274fe6c29SRuslan Bukin 			return status;
296374fe6c29SRuslan Bukin 
296474fe6c29SRuslan Bukin 		if (!ev->ip_suppressed && decoder->enabled &&
296574fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.exstop.ip)
296674fe6c29SRuslan Bukin 			break;
296774fe6c29SRuslan Bukin 
296874fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
296974fe6c29SRuslan Bukin 
297074fe6c29SRuslan Bukin 	case ptev_mwait:
297174fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
297274fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
297374fe6c29SRuslan Bukin 		if (status < 0)
297474fe6c29SRuslan Bukin 			return status;
297574fe6c29SRuslan Bukin 
297674fe6c29SRuslan Bukin 		if (!ev->ip_suppressed && decoder->enabled &&
297774fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.mwait.ip)
297874fe6c29SRuslan Bukin 			break;
297974fe6c29SRuslan Bukin 
298074fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
298174fe6c29SRuslan Bukin 
298274fe6c29SRuslan Bukin 	case ptev_pwre:
298374fe6c29SRuslan Bukin 	case ptev_pwrx:
298474fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
298574fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
298674fe6c29SRuslan Bukin 		if (status < 0)
298774fe6c29SRuslan Bukin 			return status;
298874fe6c29SRuslan Bukin 
298974fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
299074fe6c29SRuslan Bukin 
299174fe6c29SRuslan Bukin 	case ptev_ptwrite:
299274fe6c29SRuslan Bukin 		/* We apply the event immediately if we're not tracing. */
299374fe6c29SRuslan Bukin 		if (!decoder->enabled)
299474fe6c29SRuslan Bukin 			return pt_blk_status(decoder, pts_event_pending);
299574fe6c29SRuslan Bukin 
299674fe6c29SRuslan Bukin 		/* Ptwrite events are normally indicated on the event flow,
299774fe6c29SRuslan Bukin 		 * unless they bind to the same instruction as a previous event.
299874fe6c29SRuslan Bukin 		 *
299974fe6c29SRuslan Bukin 		 * We bind at most one ptwrite event to an instruction, though.
300074fe6c29SRuslan Bukin 		 */
300174fe6c29SRuslan Bukin 		if (!decoder->process_insn || decoder->bound_ptwrite)
300274fe6c29SRuslan Bukin 			break;
300374fe6c29SRuslan Bukin 
300474fe6c29SRuslan Bukin 		/* We're done if we're not binding to the currently postponed
300574fe6c29SRuslan Bukin 		 * instruction.  We will process the event on the normal event
300674fe6c29SRuslan Bukin 		 * flow in the next iteration.
300774fe6c29SRuslan Bukin 		 */
300874fe6c29SRuslan Bukin 		if (!ev->ip_suppressed ||
300974fe6c29SRuslan Bukin 		    !pt_insn_is_ptwrite(&decoder->insn, &decoder->iext))
301074fe6c29SRuslan Bukin 			break;
301174fe6c29SRuslan Bukin 
301274fe6c29SRuslan Bukin 		/* We bound a ptwrite event.  Make sure we do not bind further
301374fe6c29SRuslan Bukin 		 * ptwrite events to this instruction.
301474fe6c29SRuslan Bukin 		 */
301574fe6c29SRuslan Bukin 		decoder->bound_ptwrite = 1;
301674fe6c29SRuslan Bukin 
301774fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
301874fe6c29SRuslan Bukin 
301974fe6c29SRuslan Bukin 	case ptev_tick:
302074fe6c29SRuslan Bukin 	case ptev_cbr:
302174fe6c29SRuslan Bukin 	case ptev_mnt:
302274fe6c29SRuslan Bukin 		/* This event does not bind to an instruction. */
302374fe6c29SRuslan Bukin 		status = pt_blk_proceed_postponed_insn(decoder);
302474fe6c29SRuslan Bukin 		if (status < 0)
302574fe6c29SRuslan Bukin 			return status;
302674fe6c29SRuslan Bukin 
302774fe6c29SRuslan Bukin 		return pt_blk_status(decoder, pts_event_pending);
302874fe6c29SRuslan Bukin 	}
302974fe6c29SRuslan Bukin 
303074fe6c29SRuslan Bukin 	/* No further events.  Proceed past any postponed instruction. */
303174fe6c29SRuslan Bukin 	status = pt_blk_proceed_postponed_insn(decoder);
303274fe6c29SRuslan Bukin 	if (status < 0)
303374fe6c29SRuslan Bukin 		return status;
303474fe6c29SRuslan Bukin 
303574fe6c29SRuslan Bukin 	return pt_blk_status(decoder, 0);
303674fe6c29SRuslan Bukin }
303774fe6c29SRuslan Bukin 
pt_blk_next(struct pt_block_decoder * decoder,struct pt_block * ublock,size_t size)303874fe6c29SRuslan Bukin int pt_blk_next(struct pt_block_decoder *decoder, struct pt_block *ublock,
303974fe6c29SRuslan Bukin 		size_t size)
304074fe6c29SRuslan Bukin {
304174fe6c29SRuslan Bukin 	struct pt_block block, *pblock;
304274fe6c29SRuslan Bukin 	int errcode, status;
304374fe6c29SRuslan Bukin 
304474fe6c29SRuslan Bukin 	if (!decoder || !ublock)
304574fe6c29SRuslan Bukin 		return -pte_invalid;
304674fe6c29SRuslan Bukin 
304774fe6c29SRuslan Bukin 	pblock = size == sizeof(block) ? ublock : &block;
304874fe6c29SRuslan Bukin 
304974fe6c29SRuslan Bukin 	/* Zero-initialize the block in case of error returns. */
305074fe6c29SRuslan Bukin 	memset(pblock, 0, sizeof(*pblock));
305174fe6c29SRuslan Bukin 
305274fe6c29SRuslan Bukin 	/* Fill in a few things from the current decode state.
305374fe6c29SRuslan Bukin 	 *
305474fe6c29SRuslan Bukin 	 * This reflects the state of the last pt_blk_next() or pt_blk_start()
305574fe6c29SRuslan Bukin 	 * call.  Note that, unless we stop with tracing disabled, we proceed
305674fe6c29SRuslan Bukin 	 * already to the start IP of the next block.
305774fe6c29SRuslan Bukin 	 *
305874fe6c29SRuslan Bukin 	 * Some of the state may later be overwritten as we process events.
305974fe6c29SRuslan Bukin 	 */
306074fe6c29SRuslan Bukin 	pblock->ip = decoder->ip;
306174fe6c29SRuslan Bukin 	pblock->mode = decoder->mode;
306274fe6c29SRuslan Bukin 	if (decoder->speculative)
306374fe6c29SRuslan Bukin 		pblock->speculative = 1;
306474fe6c29SRuslan Bukin 
306574fe6c29SRuslan Bukin 	/* Proceed one block. */
306674fe6c29SRuslan Bukin 	status = pt_blk_proceed(decoder, pblock);
306774fe6c29SRuslan Bukin 
306874fe6c29SRuslan Bukin 	errcode = block_to_user(ublock, size, pblock);
306974fe6c29SRuslan Bukin 	if (errcode < 0)
307074fe6c29SRuslan Bukin 		return errcode;
307174fe6c29SRuslan Bukin 
307274fe6c29SRuslan Bukin 	return status;
307374fe6c29SRuslan Bukin }
307474fe6c29SRuslan Bukin 
307574fe6c29SRuslan Bukin /* Process an enabled event.
307674fe6c29SRuslan Bukin  *
307774fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
307874fe6c29SRuslan Bukin  */
pt_blk_process_enabled(struct pt_block_decoder * decoder,const struct pt_event * ev)307974fe6c29SRuslan Bukin static int pt_blk_process_enabled(struct pt_block_decoder *decoder,
308074fe6c29SRuslan Bukin 				  const struct pt_event *ev)
308174fe6c29SRuslan Bukin {
308274fe6c29SRuslan Bukin 	if (!decoder || !ev)
308374fe6c29SRuslan Bukin 		return -pte_internal;
308474fe6c29SRuslan Bukin 
308574fe6c29SRuslan Bukin 	/* This event can't be a status update. */
308674fe6c29SRuslan Bukin 	if (ev->status_update)
308774fe6c29SRuslan Bukin 		return -pte_bad_context;
308874fe6c29SRuslan Bukin 
308974fe6c29SRuslan Bukin 	/* We must have an IP in order to start decoding. */
309074fe6c29SRuslan Bukin 	if (ev->ip_suppressed)
309174fe6c29SRuslan Bukin 		return -pte_noip;
309274fe6c29SRuslan Bukin 
309374fe6c29SRuslan Bukin 	/* We must currently be disabled. */
309474fe6c29SRuslan Bukin 	if (decoder->enabled)
309574fe6c29SRuslan Bukin 		return -pte_bad_context;
309674fe6c29SRuslan Bukin 
309774fe6c29SRuslan Bukin 	decoder->ip = ev->variant.enabled.ip;
309874fe6c29SRuslan Bukin 	decoder->enabled = 1;
309974fe6c29SRuslan Bukin 	decoder->process_event = 0;
310074fe6c29SRuslan Bukin 
310174fe6c29SRuslan Bukin 	return 0;
310274fe6c29SRuslan Bukin }
310374fe6c29SRuslan Bukin 
310474fe6c29SRuslan Bukin /* Process a disabled event.
310574fe6c29SRuslan Bukin  *
310674fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
310774fe6c29SRuslan Bukin  */
pt_blk_process_disabled(struct pt_block_decoder * decoder,const struct pt_event * ev)310874fe6c29SRuslan Bukin static int pt_blk_process_disabled(struct pt_block_decoder *decoder,
310974fe6c29SRuslan Bukin 				   const struct pt_event *ev)
311074fe6c29SRuslan Bukin {
311174fe6c29SRuslan Bukin 	if (!decoder || !ev)
311274fe6c29SRuslan Bukin 		return -pte_internal;
311374fe6c29SRuslan Bukin 
311474fe6c29SRuslan Bukin 	/* This event can't be a status update. */
311574fe6c29SRuslan Bukin 	if (ev->status_update)
311674fe6c29SRuslan Bukin 		return -pte_bad_context;
311774fe6c29SRuslan Bukin 
311874fe6c29SRuslan Bukin 	/* We must currently be enabled. */
311974fe6c29SRuslan Bukin 	if (!decoder->enabled)
312074fe6c29SRuslan Bukin 		return -pte_bad_context;
312174fe6c29SRuslan Bukin 
312274fe6c29SRuslan Bukin 	/* We preserve @decoder->ip.  This is where we expect tracing to resume
312374fe6c29SRuslan Bukin 	 * and we'll indicate that on the subsequent enabled event if tracing
312474fe6c29SRuslan Bukin 	 * actually does resume from there.
312574fe6c29SRuslan Bukin 	 */
312674fe6c29SRuslan Bukin 	decoder->enabled = 0;
312774fe6c29SRuslan Bukin 	decoder->process_event = 0;
312874fe6c29SRuslan Bukin 
312974fe6c29SRuslan Bukin 	return 0;
313074fe6c29SRuslan Bukin }
313174fe6c29SRuslan Bukin 
313274fe6c29SRuslan Bukin /* Process an asynchronous branch event.
313374fe6c29SRuslan Bukin  *
313474fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
313574fe6c29SRuslan Bukin  */
pt_blk_process_async_branch(struct pt_block_decoder * decoder,const struct pt_event * ev)313674fe6c29SRuslan Bukin static int pt_blk_process_async_branch(struct pt_block_decoder *decoder,
313774fe6c29SRuslan Bukin 				       const struct pt_event *ev)
313874fe6c29SRuslan Bukin {
313974fe6c29SRuslan Bukin 	if (!decoder || !ev)
314074fe6c29SRuslan Bukin 		return -pte_internal;
314174fe6c29SRuslan Bukin 
314274fe6c29SRuslan Bukin 	/* This event can't be a status update. */
314374fe6c29SRuslan Bukin 	if (ev->status_update)
314474fe6c29SRuslan Bukin 		return -pte_bad_context;
314574fe6c29SRuslan Bukin 
314674fe6c29SRuslan Bukin 	/* We must currently be enabled. */
314774fe6c29SRuslan Bukin 	if (!decoder->enabled)
314874fe6c29SRuslan Bukin 		return -pte_bad_context;
314974fe6c29SRuslan Bukin 
315074fe6c29SRuslan Bukin 	/* Jump to the branch destination.  We will continue from there in the
315174fe6c29SRuslan Bukin 	 * next iteration.
315274fe6c29SRuslan Bukin 	 */
315374fe6c29SRuslan Bukin 	decoder->ip = ev->variant.async_branch.to;
315474fe6c29SRuslan Bukin 	decoder->process_event = 0;
315574fe6c29SRuslan Bukin 
315674fe6c29SRuslan Bukin 	return 0;
315774fe6c29SRuslan Bukin }
315874fe6c29SRuslan Bukin 
315974fe6c29SRuslan Bukin /* Process a paging event.
316074fe6c29SRuslan Bukin  *
316174fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
316274fe6c29SRuslan Bukin  */
pt_blk_process_paging(struct pt_block_decoder * decoder,const struct pt_event * ev)316374fe6c29SRuslan Bukin static int pt_blk_process_paging(struct pt_block_decoder *decoder,
316474fe6c29SRuslan Bukin 				 const struct pt_event *ev)
316574fe6c29SRuslan Bukin {
316674fe6c29SRuslan Bukin 	uint64_t cr3;
316774fe6c29SRuslan Bukin 	int errcode;
316874fe6c29SRuslan Bukin 
316974fe6c29SRuslan Bukin 	if (!decoder || !ev)
317074fe6c29SRuslan Bukin 		return -pte_internal;
317174fe6c29SRuslan Bukin 
317274fe6c29SRuslan Bukin 	cr3 = ev->variant.paging.cr3;
317374fe6c29SRuslan Bukin 	if (decoder->asid.cr3 != cr3) {
317474fe6c29SRuslan Bukin 		errcode = pt_msec_cache_invalidate(&decoder->scache);
317574fe6c29SRuslan Bukin 		if (errcode < 0)
317674fe6c29SRuslan Bukin 			return errcode;
317774fe6c29SRuslan Bukin 
317874fe6c29SRuslan Bukin 		decoder->asid.cr3 = cr3;
317974fe6c29SRuslan Bukin 	}
318074fe6c29SRuslan Bukin 
318174fe6c29SRuslan Bukin 	decoder->process_event = 0;
318274fe6c29SRuslan Bukin 
318374fe6c29SRuslan Bukin 	return 0;
318474fe6c29SRuslan Bukin }
318574fe6c29SRuslan Bukin 
318674fe6c29SRuslan Bukin /* Process a vmcs event.
318774fe6c29SRuslan Bukin  *
318874fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
318974fe6c29SRuslan Bukin  */
pt_blk_process_vmcs(struct pt_block_decoder * decoder,const struct pt_event * ev)319074fe6c29SRuslan Bukin static int pt_blk_process_vmcs(struct pt_block_decoder *decoder,
319174fe6c29SRuslan Bukin 			       const struct pt_event *ev)
319274fe6c29SRuslan Bukin {
319374fe6c29SRuslan Bukin 	uint64_t vmcs;
319474fe6c29SRuslan Bukin 	int errcode;
319574fe6c29SRuslan Bukin 
319674fe6c29SRuslan Bukin 	if (!decoder || !ev)
319774fe6c29SRuslan Bukin 		return -pte_internal;
319874fe6c29SRuslan Bukin 
319974fe6c29SRuslan Bukin 	vmcs = ev->variant.vmcs.base;
320074fe6c29SRuslan Bukin 	if (decoder->asid.vmcs != vmcs) {
320174fe6c29SRuslan Bukin 		errcode = pt_msec_cache_invalidate(&decoder->scache);
320274fe6c29SRuslan Bukin 		if (errcode < 0)
320374fe6c29SRuslan Bukin 			return errcode;
320474fe6c29SRuslan Bukin 
320574fe6c29SRuslan Bukin 		decoder->asid.vmcs = vmcs;
320674fe6c29SRuslan Bukin 	}
320774fe6c29SRuslan Bukin 
320874fe6c29SRuslan Bukin 	decoder->process_event = 0;
320974fe6c29SRuslan Bukin 
321074fe6c29SRuslan Bukin 	return 0;
321174fe6c29SRuslan Bukin }
321274fe6c29SRuslan Bukin 
321374fe6c29SRuslan Bukin /* Process an overflow event.
321474fe6c29SRuslan Bukin  *
321574fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
321674fe6c29SRuslan Bukin  */
pt_blk_process_overflow(struct pt_block_decoder * decoder,const struct pt_event * ev)321774fe6c29SRuslan Bukin static int pt_blk_process_overflow(struct pt_block_decoder *decoder,
321874fe6c29SRuslan Bukin 				   const struct pt_event *ev)
321974fe6c29SRuslan Bukin {
322074fe6c29SRuslan Bukin 	if (!decoder || !ev)
322174fe6c29SRuslan Bukin 		return -pte_internal;
322274fe6c29SRuslan Bukin 
322374fe6c29SRuslan Bukin 	/* This event can't be a status update. */
322474fe6c29SRuslan Bukin 	if (ev->status_update)
322574fe6c29SRuslan Bukin 		return -pte_bad_context;
322674fe6c29SRuslan Bukin 
322774fe6c29SRuslan Bukin 	/* If the IP is suppressed, the overflow resolved while tracing was
322874fe6c29SRuslan Bukin 	 * disabled.  Otherwise it resolved while tracing was enabled.
322974fe6c29SRuslan Bukin 	 */
323074fe6c29SRuslan Bukin 	if (ev->ip_suppressed) {
323174fe6c29SRuslan Bukin 		/* Tracing is disabled.  It doesn't make sense to preserve the
323274fe6c29SRuslan Bukin 		 * previous IP.  This will just be misleading.  Even if tracing
323374fe6c29SRuslan Bukin 		 * had been disabled before, as well, we might have missed the
323474fe6c29SRuslan Bukin 		 * re-enable in the overflow.
323574fe6c29SRuslan Bukin 		 */
323674fe6c29SRuslan Bukin 		decoder->enabled = 0;
323774fe6c29SRuslan Bukin 		decoder->ip = 0ull;
323874fe6c29SRuslan Bukin 	} else {
323974fe6c29SRuslan Bukin 		/* Tracing is enabled and we're at the IP at which the overflow
324074fe6c29SRuslan Bukin 		 * resolved.
324174fe6c29SRuslan Bukin 		 */
324274fe6c29SRuslan Bukin 		decoder->enabled = 1;
324374fe6c29SRuslan Bukin 		decoder->ip = ev->variant.overflow.ip;
324474fe6c29SRuslan Bukin 	}
324574fe6c29SRuslan Bukin 
324674fe6c29SRuslan Bukin 	/* We don't know the TSX state.  Let's assume we execute normally.
324774fe6c29SRuslan Bukin 	 *
324874fe6c29SRuslan Bukin 	 * We also don't know the execution mode.  Let's keep what we have
324974fe6c29SRuslan Bukin 	 * in case we don't get an update before we have to decode the next
325074fe6c29SRuslan Bukin 	 * instruction.
325174fe6c29SRuslan Bukin 	 */
325274fe6c29SRuslan Bukin 	decoder->speculative = 0;
325374fe6c29SRuslan Bukin 	decoder->process_event = 0;
325474fe6c29SRuslan Bukin 
325574fe6c29SRuslan Bukin 	return 0;
325674fe6c29SRuslan Bukin }
325774fe6c29SRuslan Bukin 
325874fe6c29SRuslan Bukin /* Process an exec mode event.
325974fe6c29SRuslan Bukin  *
326074fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
326174fe6c29SRuslan Bukin  */
pt_blk_process_exec_mode(struct pt_block_decoder * decoder,const struct pt_event * ev)326274fe6c29SRuslan Bukin static int pt_blk_process_exec_mode(struct pt_block_decoder *decoder,
326374fe6c29SRuslan Bukin 				    const struct pt_event *ev)
326474fe6c29SRuslan Bukin {
326574fe6c29SRuslan Bukin 	enum pt_exec_mode mode;
326674fe6c29SRuslan Bukin 
326774fe6c29SRuslan Bukin 	if (!decoder || !ev)
326874fe6c29SRuslan Bukin 		return -pte_internal;
326974fe6c29SRuslan Bukin 
327074fe6c29SRuslan Bukin 	/* Use status update events to diagnose inconsistencies. */
327174fe6c29SRuslan Bukin 	mode = ev->variant.exec_mode.mode;
327274fe6c29SRuslan Bukin 	if (ev->status_update && decoder->enabled &&
327374fe6c29SRuslan Bukin 	    decoder->mode != ptem_unknown && decoder->mode != mode)
327474fe6c29SRuslan Bukin 		return -pte_bad_status_update;
327574fe6c29SRuslan Bukin 
327674fe6c29SRuslan Bukin 	decoder->mode = mode;
327774fe6c29SRuslan Bukin 	decoder->process_event = 0;
327874fe6c29SRuslan Bukin 
327974fe6c29SRuslan Bukin 	return 0;
328074fe6c29SRuslan Bukin }
328174fe6c29SRuslan Bukin 
328274fe6c29SRuslan Bukin /* Process a tsx event.
328374fe6c29SRuslan Bukin  *
328474fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
328574fe6c29SRuslan Bukin  */
pt_blk_process_tsx(struct pt_block_decoder * decoder,const struct pt_event * ev)328674fe6c29SRuslan Bukin static int pt_blk_process_tsx(struct pt_block_decoder *decoder,
328774fe6c29SRuslan Bukin 			      const struct pt_event *ev)
328874fe6c29SRuslan Bukin {
328974fe6c29SRuslan Bukin 	if (!decoder || !ev)
329074fe6c29SRuslan Bukin 		return -pte_internal;
329174fe6c29SRuslan Bukin 
329274fe6c29SRuslan Bukin 	decoder->speculative = ev->variant.tsx.speculative;
329374fe6c29SRuslan Bukin 	decoder->process_event = 0;
329474fe6c29SRuslan Bukin 
329574fe6c29SRuslan Bukin 	return 0;
329674fe6c29SRuslan Bukin }
329774fe6c29SRuslan Bukin 
329874fe6c29SRuslan Bukin /* Process a stop event.
329974fe6c29SRuslan Bukin  *
330074fe6c29SRuslan Bukin  * Returns zero on success, a negative error code otherwise.
330174fe6c29SRuslan Bukin  */
pt_blk_process_stop(struct pt_block_decoder * decoder,const struct pt_event * ev)330274fe6c29SRuslan Bukin static int pt_blk_process_stop(struct pt_block_decoder *decoder,
330374fe6c29SRuslan Bukin 			       const struct pt_event *ev)
330474fe6c29SRuslan Bukin {
330574fe6c29SRuslan Bukin 	if (!decoder || !ev)
330674fe6c29SRuslan Bukin 		return -pte_internal;
330774fe6c29SRuslan Bukin 
330874fe6c29SRuslan Bukin 	/* This event can't be a status update. */
330974fe6c29SRuslan Bukin 	if (ev->status_update)
331074fe6c29SRuslan Bukin 		return -pte_bad_context;
331174fe6c29SRuslan Bukin 
331274fe6c29SRuslan Bukin 	/* Tracing is always disabled before it is stopped. */
331374fe6c29SRuslan Bukin 	if (decoder->enabled)
331474fe6c29SRuslan Bukin 		return -pte_bad_context;
331574fe6c29SRuslan Bukin 
331674fe6c29SRuslan Bukin 	decoder->process_event = 0;
331774fe6c29SRuslan Bukin 
331874fe6c29SRuslan Bukin 	return 0;
331974fe6c29SRuslan Bukin }
332074fe6c29SRuslan Bukin 
pt_blk_event(struct pt_block_decoder * decoder,struct pt_event * uevent,size_t size)332174fe6c29SRuslan Bukin int pt_blk_event(struct pt_block_decoder *decoder, struct pt_event *uevent,
332274fe6c29SRuslan Bukin 		 size_t size)
332374fe6c29SRuslan Bukin {
332474fe6c29SRuslan Bukin 	struct pt_event *ev;
332574fe6c29SRuslan Bukin 	int status;
332674fe6c29SRuslan Bukin 
332774fe6c29SRuslan Bukin 	if (!decoder || !uevent)
332874fe6c29SRuslan Bukin 		return -pte_invalid;
332974fe6c29SRuslan Bukin 
333074fe6c29SRuslan Bukin 	/* We must currently process an event. */
333174fe6c29SRuslan Bukin 	if (!decoder->process_event)
333274fe6c29SRuslan Bukin 		return -pte_bad_query;
333374fe6c29SRuslan Bukin 
333474fe6c29SRuslan Bukin 	ev = &decoder->event;
333574fe6c29SRuslan Bukin 	switch (ev->type) {
333674fe6c29SRuslan Bukin 	case ptev_enabled:
333774fe6c29SRuslan Bukin 		/* Indicate that tracing resumes from the IP at which tracing
333874fe6c29SRuslan Bukin 		 * had been disabled before (with some special treatment for
333974fe6c29SRuslan Bukin 		 * calls).
334074fe6c29SRuslan Bukin 		 */
334174fe6c29SRuslan Bukin 		if (ev->variant.enabled.ip == decoder->ip)
334274fe6c29SRuslan Bukin 			ev->variant.enabled.resumed = 1;
334374fe6c29SRuslan Bukin 
334474fe6c29SRuslan Bukin 		status = pt_blk_process_enabled(decoder, ev);
334574fe6c29SRuslan Bukin 		if (status < 0)
334674fe6c29SRuslan Bukin 			return status;
334774fe6c29SRuslan Bukin 
334874fe6c29SRuslan Bukin 		break;
334974fe6c29SRuslan Bukin 
335074fe6c29SRuslan Bukin 	case ptev_async_disabled:
335174fe6c29SRuslan Bukin 		if (decoder->ip != ev->variant.async_disabled.at)
335274fe6c29SRuslan Bukin 			return -pte_bad_query;
335374fe6c29SRuslan Bukin 
335474fe6c29SRuslan Bukin 		fallthrough;
335574fe6c29SRuslan Bukin 	case ptev_disabled:
335674fe6c29SRuslan Bukin 
335774fe6c29SRuslan Bukin 		status = pt_blk_process_disabled(decoder, ev);
335874fe6c29SRuslan Bukin 		if (status < 0)
335974fe6c29SRuslan Bukin 			return status;
336074fe6c29SRuslan Bukin 
336174fe6c29SRuslan Bukin 		break;
336274fe6c29SRuslan Bukin 
336374fe6c29SRuslan Bukin 	case ptev_async_branch:
336474fe6c29SRuslan Bukin 		if (decoder->ip != ev->variant.async_branch.from)
336574fe6c29SRuslan Bukin 			return -pte_bad_query;
336674fe6c29SRuslan Bukin 
336774fe6c29SRuslan Bukin 		status = pt_blk_process_async_branch(decoder, ev);
336874fe6c29SRuslan Bukin 		if (status < 0)
336974fe6c29SRuslan Bukin 			return status;
337074fe6c29SRuslan Bukin 
337174fe6c29SRuslan Bukin 		break;
337274fe6c29SRuslan Bukin 
337374fe6c29SRuslan Bukin 	case ptev_async_paging:
337474fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
337574fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.async_paging.ip)
337674fe6c29SRuslan Bukin 			return -pte_bad_query;
337774fe6c29SRuslan Bukin 
337874fe6c29SRuslan Bukin 		fallthrough;
337974fe6c29SRuslan Bukin 	case ptev_paging:
338074fe6c29SRuslan Bukin 		status = pt_blk_process_paging(decoder, ev);
338174fe6c29SRuslan Bukin 		if (status < 0)
338274fe6c29SRuslan Bukin 			return status;
338374fe6c29SRuslan Bukin 
338474fe6c29SRuslan Bukin 		break;
338574fe6c29SRuslan Bukin 
338674fe6c29SRuslan Bukin 	case ptev_async_vmcs:
338774fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
338874fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.async_vmcs.ip)
338974fe6c29SRuslan Bukin 			return -pte_bad_query;
339074fe6c29SRuslan Bukin 
339174fe6c29SRuslan Bukin 		fallthrough;
339274fe6c29SRuslan Bukin 	case ptev_vmcs:
339374fe6c29SRuslan Bukin 		status = pt_blk_process_vmcs(decoder, ev);
339474fe6c29SRuslan Bukin 		if (status < 0)
339574fe6c29SRuslan Bukin 			return status;
339674fe6c29SRuslan Bukin 
339774fe6c29SRuslan Bukin 		break;
339874fe6c29SRuslan Bukin 
339974fe6c29SRuslan Bukin 	case ptev_overflow:
340074fe6c29SRuslan Bukin 		status = pt_blk_process_overflow(decoder, ev);
340174fe6c29SRuslan Bukin 		if (status < 0)
340274fe6c29SRuslan Bukin 			return status;
340374fe6c29SRuslan Bukin 
340474fe6c29SRuslan Bukin 		break;
340574fe6c29SRuslan Bukin 
340674fe6c29SRuslan Bukin 	case ptev_exec_mode:
340774fe6c29SRuslan Bukin 		if (!ev->ip_suppressed &&
340874fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.exec_mode.ip)
340974fe6c29SRuslan Bukin 			return -pte_bad_query;
341074fe6c29SRuslan Bukin 
341174fe6c29SRuslan Bukin 		status = pt_blk_process_exec_mode(decoder, ev);
341274fe6c29SRuslan Bukin 		if (status < 0)
341374fe6c29SRuslan Bukin 			return status;
341474fe6c29SRuslan Bukin 
341574fe6c29SRuslan Bukin 		break;
341674fe6c29SRuslan Bukin 
341774fe6c29SRuslan Bukin 	case ptev_tsx:
341874fe6c29SRuslan Bukin 		if (!ev->ip_suppressed && decoder->ip != ev->variant.tsx.ip)
341974fe6c29SRuslan Bukin 			return -pte_bad_query;
342074fe6c29SRuslan Bukin 
342174fe6c29SRuslan Bukin 		status = pt_blk_process_tsx(decoder, ev);
342274fe6c29SRuslan Bukin 		if (status < 0)
342374fe6c29SRuslan Bukin 			return status;
342474fe6c29SRuslan Bukin 
342574fe6c29SRuslan Bukin 		break;
342674fe6c29SRuslan Bukin 
342774fe6c29SRuslan Bukin 	case ptev_stop:
342874fe6c29SRuslan Bukin 		status = pt_blk_process_stop(decoder, ev);
342974fe6c29SRuslan Bukin 		if (status < 0)
343074fe6c29SRuslan Bukin 			return status;
343174fe6c29SRuslan Bukin 
343274fe6c29SRuslan Bukin 		break;
343374fe6c29SRuslan Bukin 
343474fe6c29SRuslan Bukin 	case ptev_exstop:
343574fe6c29SRuslan Bukin 		if (!ev->ip_suppressed && decoder->enabled &&
343674fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.exstop.ip)
343774fe6c29SRuslan Bukin 			return -pte_bad_query;
343874fe6c29SRuslan Bukin 
343974fe6c29SRuslan Bukin 		decoder->process_event = 0;
344074fe6c29SRuslan Bukin 		break;
344174fe6c29SRuslan Bukin 
344274fe6c29SRuslan Bukin 	case ptev_mwait:
344374fe6c29SRuslan Bukin 		if (!ev->ip_suppressed && decoder->enabled &&
344474fe6c29SRuslan Bukin 		    decoder->ip != ev->variant.mwait.ip)
344574fe6c29SRuslan Bukin 			return -pte_bad_query;
344674fe6c29SRuslan Bukin 
344774fe6c29SRuslan Bukin 		decoder->process_event = 0;
344874fe6c29SRuslan Bukin 		break;
344974fe6c29SRuslan Bukin 
345074fe6c29SRuslan Bukin 	case ptev_pwre:
345174fe6c29SRuslan Bukin 	case ptev_pwrx:
345274fe6c29SRuslan Bukin 	case ptev_ptwrite:
345374fe6c29SRuslan Bukin 	case ptev_tick:
345474fe6c29SRuslan Bukin 	case ptev_cbr:
345574fe6c29SRuslan Bukin 	case ptev_mnt:
345674fe6c29SRuslan Bukin 		decoder->process_event = 0;
345774fe6c29SRuslan Bukin 		break;
345874fe6c29SRuslan Bukin 	}
345974fe6c29SRuslan Bukin 
346074fe6c29SRuslan Bukin 	/* Copy the event to the user.  Make sure we're not writing beyond the
346174fe6c29SRuslan Bukin 	 * memory provided by the user.
346274fe6c29SRuslan Bukin 	 *
346374fe6c29SRuslan Bukin 	 * We might truncate details of an event but only for those events the
346474fe6c29SRuslan Bukin 	 * user can't know about, anyway.
346574fe6c29SRuslan Bukin 	 */
346674fe6c29SRuslan Bukin 	if (sizeof(*ev) < size)
346774fe6c29SRuslan Bukin 		size = sizeof(*ev);
346874fe6c29SRuslan Bukin 
346974fe6c29SRuslan Bukin 	memcpy(uevent, ev, size);
347074fe6c29SRuslan Bukin 
347174fe6c29SRuslan Bukin 	/* Indicate further events. */
347274fe6c29SRuslan Bukin 	return pt_blk_proceed_trailing_event(decoder, NULL);
347374fe6c29SRuslan Bukin }
3474