1 /*
2 * Copyright (c) 2013-2019, 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 */
40 static 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
truncate(const uint8_t * pointer,size_t alignment)47 static 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
align(const uint8_t * pointer,size_t alignment)57 static 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 */
pt_find_psb(const uint8_t * pos,const struct pt_config * config)67 static 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
pt_sync_within_bounds(const uint8_t * pos,const uint8_t * begin,const uint8_t * end)120 static 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
pt_sync_set(const uint8_t ** sync,const uint8_t * pos,const struct pt_config * config)130 int 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
pt_sync_forward(const uint8_t ** sync,const uint8_t * pos,const struct pt_config * config)161 int pt_sync_forward(const uint8_t **sync, const uint8_t *pos,
162 const struct pt_config *config)
163 {
164 const uint8_t *begin, *end, *start;
165
166 if (!sync || !pos || !config)
167 return -pte_internal;
168
169 start = pos;
170 begin = config->begin;
171 end = config->end;
172
173 if (!pt_sync_within_bounds(pos, begin, end))
174 return -pte_internal;
175
176 /* We search for a full 64bit word. It's OK to skip the current one. */
177 pos = align(pos, sizeof(*psb_pattern));
178
179 /* Search for the psb payload pattern in the buffer. */
180 for (;;) {
181 const uint8_t *current = pos;
182 uint64_t val;
183
184 pos += sizeof(uint64_t);
185 if (end < pos)
186 return -pte_eos;
187
188 val = * (const uint64_t *) current;
189
190 if ((val != psb_pattern[0]) && (val != psb_pattern[1]))
191 continue;
192
193 /* We found a 64bit word's worth of psb payload pattern. */
194 current = pt_find_psb(pos, config);
195 if (!current)
196 continue;
197
198 /* If @start points inside a PSB, we may find that one. Ignore
199 * it unless @start points to its beginning.
200 */
201 if (current < start)
202 continue;
203
204 *sync = current;
205 return 0;
206 }
207 }
208
pt_sync_backward(const uint8_t ** sync,const uint8_t * pos,const struct pt_config * config)209 int pt_sync_backward(const uint8_t **sync, const uint8_t *pos,
210 const struct pt_config *config)
211 {
212 const uint8_t *begin, *end;
213
214 if (!sync || !pos || !config)
215 return -pte_internal;
216
217 begin = config->begin;
218 end = config->end;
219
220 if (!pt_sync_within_bounds(pos, begin, end))
221 return -pte_internal;
222
223 /* We search for a full 64bit word. It's OK to skip the current one. */
224 pos = truncate(pos, sizeof(*psb_pattern));
225
226 /* Search for the psb payload pattern in the buffer. */
227 for (;;) {
228 const uint8_t *next = pos;
229 uint64_t val;
230
231 pos -= sizeof(uint64_t);
232 if (pos < begin)
233 return -pte_eos;
234
235 val = * (const uint64_t *) pos;
236
237 if ((val != psb_pattern[0]) && (val != psb_pattern[1]))
238 continue;
239
240 /* We found a 64bit word's worth of psb payload pattern. */
241 next = pt_find_psb(next, config);
242 if (!next)
243 continue;
244
245 *sync = next;
246 return 0;
247 }
248 }
249