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 : █
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