1/*
2 * Copyright (c) 2013-2018, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 *  * Redistributions of source code must retain the above copyright notice,
8 *    this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright notice,
10 *    this list of conditions and the following disclaimer in the documentation
11 *    and/or other materials provided with the distribution.
12 *  * Neither the name of Intel Corporation nor the names of its contributors
13 *    may be used to endorse or promote products derived from this software
14 *    without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "pt_sync.h"
30#include "pt_packet.h"
31#include "pt_opcodes.h"
32
33#include "intel-pt.h"
34
35
36/* A psb packet contains a unique 2-byte repeating pattern.
37 *
38 * There are only two ways to fill up a 64bit work with such a pattern.
39 */
40static const uint64_t psb_pattern[] = {
41	((uint64_t) pt_psb_lohi		| (uint64_t) pt_psb_lohi << 16 |
42	 (uint64_t) pt_psb_lohi << 32	| (uint64_t) pt_psb_lohi << 48),
43	((uint64_t) pt_psb_hilo		| (uint64_t) pt_psb_hilo << 16 |
44	 (uint64_t) pt_psb_hilo << 32	| (uint64_t) pt_psb_hilo << 48)
45};
46
47static const uint8_t *truncate(const uint8_t *pointer, size_t alignment)
48{
49	uintptr_t raw = (uintptr_t) pointer;
50
51	raw /= alignment;
52	raw *= alignment;
53
54	return (const uint8_t *) raw;
55}
56
57static const uint8_t *align(const uint8_t *pointer, size_t alignment)
58{
59	return truncate(pointer + alignment - 1, alignment);
60}
61
62/* Find a psb packet given a position somewhere in the payload.
63 *
64 * Return the position of the psb packet.
65 * Return NULL, if this is not a psb packet.
66 */
67static const uint8_t *pt_find_psb(const uint8_t *pos,
68				  const struct pt_config *config)
69{
70	const uint8_t *begin, *end;
71	int errcode;
72
73	if (!pos || !config)
74		return NULL;
75
76	begin = config->begin;
77	end = config->end;
78
79	/* Navigate to the end of the psb payload pattern.
80	 *
81	 * Beware that PSB is an extended opcode. We must not confuse the extend
82	 * opcode of the following packet as belonging to the PSB.
83	 */
84	if (*pos != pt_psb_hi)
85		pos++;
86
87	for (; (pos + 1) < end; pos += 2) {
88		uint8_t hi, lo;
89
90		hi = pos[0];
91		lo = pos[1];
92
93		if (hi != pt_psb_hi)
94			break;
95
96		if (lo != pt_psb_lo)
97			break;
98	}
99	/*
100	 * We're right after the psb payload and within the buffer.
101	 * Navigate to the expected beginning of the psb packet.
102	 */
103	pos -= ptps_psb;
104
105	/* Check if we're still inside the buffer. */
106	if (pos < begin)
107		return NULL;
108
109	/* Check that this is indeed a psb packet we're at. */
110	if (pos[0] != pt_opc_psb || pos[1] != pt_ext_psb)
111		return NULL;
112
113	errcode = pt_pkt_read_psb(pos, config);
114	if (errcode < 0)
115		return NULL;
116
117	return pos;
118}
119
120static int pt_sync_within_bounds(const uint8_t *pos, const uint8_t *begin,
121				 const uint8_t *end)
122{
123	/* We allow @pos == @end representing the very end of the trace.
124	 *
125	 * This will result in -pte_eos when we actually try to read from @pos.
126	 */
127	return (begin <= pos) && (pos <= end);
128}
129
130int pt_sync_set(const uint8_t **sync, const uint8_t *pos,
131		const struct pt_config *config)
132{
133	const uint8_t *begin, *end;
134	int errcode;
135
136	if (!sync || !pos || !config)
137		return -pte_internal;
138
139	begin = config->begin;
140	end = config->end;
141
142	if (!pt_sync_within_bounds(pos, begin, end))
143		return -pte_eos;
144
145	if (end < pos + 2)
146		return -pte_eos;
147
148	/* Check that this is indeed a psb packet we're at. */
149	if (pos[0] != pt_opc_psb || pos[1] != pt_ext_psb)
150		return -pte_nosync;
151
152	errcode = pt_pkt_read_psb(pos, config);
153	if (errcode < 0)
154		return errcode;
155
156	*sync = pos;
157
158	return 0;
159}
160
161int pt_sync_forward(const uint8_t **sync, const uint8_t *pos,
162		    const struct pt_config *config)
163{
164	const uint8_t *begin, *end;
165
166	if (!sync || !pos || !config)
167		return -pte_internal;
168
169	begin = config->begin;
170	end = config->end;
171
172	if (!pt_sync_within_bounds(pos, begin, end))
173		return -pte_internal;
174
175	/* We search for a full 64bit word. It's OK to skip the current one. */
176	pos = align(pos, sizeof(*psb_pattern));
177
178	/* Search for the psb payload pattern in the buffer. */
179	for (;;) {
180		const uint8_t *current = pos;
181		uint64_t val;
182
183		pos += sizeof(uint64_t);
184		if (end < pos)
185			return -pte_eos;
186
187		val = * (const uint64_t *) current;
188
189		if ((val != psb_pattern[0]) && (val != psb_pattern[1]))
190			continue;
191
192		/* We found a 64bit word's worth of psb payload pattern. */
193		current = pt_find_psb(pos, config);
194		if (!current)
195			continue;
196
197		*sync = current;
198		return 0;
199	}
200}
201
202int pt_sync_backward(const uint8_t **sync, const uint8_t *pos,
203		    const struct pt_config *config)
204{
205	const uint8_t *begin, *end;
206
207	if (!sync || !pos || !config)
208		return -pte_internal;
209
210	begin = config->begin;
211	end = config->end;
212
213	if (!pt_sync_within_bounds(pos, begin, end))
214		return -pte_internal;
215
216	/* We search for a full 64bit word. It's OK to skip the current one. */
217	pos = truncate(pos, sizeof(*psb_pattern));
218
219	/* Search for the psb payload pattern in the buffer. */
220	for (;;) {
221		const uint8_t *next = pos;
222		uint64_t val;
223
224		pos -= sizeof(uint64_t);
225		if (pos < begin)
226			return -pte_eos;
227
228		val = * (const uint64_t *) pos;
229
230		if ((val != psb_pattern[0]) && (val != psb_pattern[1]))
231			continue;
232
233		/* We found a 64bit word's worth of psb payload pattern. */
234		next = pt_find_psb(next, config);
235		if (!next)
236			continue;
237
238		*sync = next;
239		return 0;
240	}
241}
242