target/arm_dpm: prevent endless loop in arm_dpm_full_context()
[openocd.git] / src / target / oocd_trace.c
1 /***************************************************************************
2 * Copyright (C) 2007 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
19 ***************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "arm.h"
26 #include "etm.h"
27 #include "oocd_trace.h"
28
29 /*
30 * This is "proof of concept" code, for prototype hardware:
31 * https://lists.berlios.de/pipermail/openocd-development/2007-September/000336.html
32 */
33
34 static int oocd_trace_read_reg(struct oocd_trace *oocd_trace, int reg, uint32_t *value)
35 {
36 size_t bytes_written, bytes_read, bytes_to_read;
37 uint8_t cmd;
38
39 cmd = 0x10 | (reg & 0x7);
40 bytes_written = write(oocd_trace->tty_fd, &cmd, 1);
41 if (bytes_written < 1)
42 return ERROR_FAIL;
43
44 bytes_to_read = 4;
45 while (bytes_to_read > 0) {
46 bytes_read = read(oocd_trace->tty_fd, ((uint8_t *)value) + 4 - bytes_to_read, bytes_to_read);
47 bytes_to_read -= bytes_read;
48 }
49
50 LOG_DEBUG("reg #%i: 0x%8.8x", reg, *value);
51
52 return ERROR_OK;
53 }
54
55 static int oocd_trace_write_reg(struct oocd_trace *oocd_trace, int reg, uint32_t value)
56 {
57 size_t bytes_written;
58 uint8_t data[5];
59
60 data[0] = 0x18 | (reg & 0x7);
61 data[1] = value & 0xff;
62 data[2] = (value & 0xff00) >> 8;
63 data[3] = (value & 0xff0000) >> 16;
64 data[4] = (value & 0xff000000) >> 24;
65
66 bytes_written = write(oocd_trace->tty_fd, data, 5);
67 if (bytes_written < 5)
68 return ERROR_FAIL;
69
70 LOG_DEBUG("reg #%i: 0x%8.8x", reg, value);
71
72 return ERROR_OK;
73 }
74
75 static int oocd_trace_read_memory(struct oocd_trace *oocd_trace, uint8_t *data, uint32_t address, uint32_t size)
76 {
77 size_t bytes_written, bytes_to_read;
78 ssize_t bytes_read;
79 uint8_t cmd;
80
81 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, address);
82 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_SDRAM_COUNTER, size);
83
84 cmd = 0x20;
85 bytes_written = write(oocd_trace->tty_fd, &cmd, 1);
86 if (bytes_written < 1)
87 return ERROR_FAIL;
88
89 bytes_to_read = size * 16;
90 while (bytes_to_read > 0) {
91 bytes_read = read(oocd_trace->tty_fd,
92 ((uint8_t *)data) + (size * 16) - bytes_to_read, bytes_to_read);
93 if (bytes_read < 0)
94 LOG_DEBUG("read() returned %zi (%s)", bytes_read, strerror(errno));
95 else
96 bytes_to_read -= bytes_read;
97 }
98
99 return ERROR_OK;
100 }
101
102 static int oocd_trace_init(struct etm_context *etm_ctx)
103 {
104 uint8_t trash[256];
105 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
106 size_t bytes_read;
107
108 oocd_trace->tty_fd = open(oocd_trace->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
109
110 if (oocd_trace->tty_fd < 0) {
111 LOG_ERROR("can't open tty");
112 return ERROR_ETM_CAPTURE_INIT_FAILED;
113 }
114
115 /* clear input & output buffers, then switch to "blocking mode" */
116 tcflush(oocd_trace->tty_fd, TCOFLUSH);
117 tcflush(oocd_trace->tty_fd, TCIFLUSH);
118 fcntl(oocd_trace->tty_fd, F_SETFL, fcntl(oocd_trace->tty_fd, F_GETFL) & ~O_NONBLOCK);
119
120 tcgetattr(oocd_trace->tty_fd, &oocd_trace->oldtio); /* save current port settings */
121
122 bzero(&oocd_trace->newtio, sizeof(oocd_trace->newtio));
123 oocd_trace->newtio.c_cflag = CS8 | CLOCAL | CREAD | B2500000;
124
125 oocd_trace->newtio.c_iflag = IGNPAR | IGNBRK | IXON | IXOFF;
126 oocd_trace->newtio.c_oflag = 0;
127
128 /* set input mode (non-canonical, no echo,...) */
129 oocd_trace->newtio.c_lflag = 0;
130
131 cfmakeraw(&oocd_trace->newtio);
132 oocd_trace->newtio.c_cc[VTIME] = 1; /* inter-character timer used */
133 oocd_trace->newtio.c_cc[VMIN] = 0; /* blocking read until 0 chars received */
134
135 tcflush(oocd_trace->tty_fd, TCIFLUSH);
136 tcsetattr(oocd_trace->tty_fd, TCSANOW, &oocd_trace->newtio);
137
138 /* occasionally one bogus character is left in the input buffer
139 * read up any leftover characters to ensure communication is in sync */
140 do {
141 bytes_read = read(oocd_trace->tty_fd, trash, sizeof(trash));
142 if (bytes_read)
143 LOG_DEBUG("%zi bytes read", bytes_read);
144 } while (bytes_read > 0);
145
146 return ERROR_OK;
147 }
148
149 static trace_status_t oocd_trace_status(struct etm_context *etm_ctx)
150 {
151 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
152 uint32_t status;
153
154 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
155
156 /* if tracing is currently idle, return this information */
157 if (etm_ctx->capture_status == TRACE_IDLE)
158 return etm_ctx->capture_status;
159 else if (etm_ctx->capture_status & TRACE_RUNNING) {
160 /* check Full bit to identify an overflow */
161 if (status & 0x4)
162 etm_ctx->capture_status |= TRACE_OVERFLOWED;
163
164 /* check Triggered bit to identify trigger condition */
165 if (status & 0x2)
166 etm_ctx->capture_status |= TRACE_TRIGGERED;
167
168 if (status & 0x1) {
169 etm_ctx->capture_status &= ~TRACE_RUNNING;
170 etm_ctx->capture_status |= TRACE_COMPLETED;
171 }
172 }
173
174 return etm_ctx->capture_status;
175 }
176
177 static int oocd_trace_read_trace(struct etm_context *etm_ctx)
178 {
179 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
180 uint32_t status, address;
181 uint32_t first_frame = 0x0;
182 uint32_t num_frames = 1048576;
183 uint8_t *trace_data;
184 uint32_t i;
185
186 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
187 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_ADDRESS, &address);
188
189 /* check if we overflowed, and adjust first frame of the trace accordingly
190 * if we didn't overflow, read only up to the frame that would be written next,
191 * i.e. don't read invalid entries
192 */
193 if (status & 0x4)
194 first_frame = address;
195 else
196 num_frames = address;
197
198 /* read data into temporary array for unpacking
199 * one frame from OpenOCD + trace corresponds to 16 trace cycles
200 */
201 trace_data = malloc(sizeof(uint8_t) * num_frames * 16);
202 oocd_trace_read_memory(oocd_trace, trace_data, first_frame, num_frames);
203
204 if (etm_ctx->trace_depth > 0)
205 free(etm_ctx->trace_data);
206
207 etm_ctx->trace_depth = num_frames * 16;
208 etm_ctx->trace_data = malloc(sizeof(struct etmv1_trace_data) * etm_ctx->trace_depth);
209
210 for (i = 0; i < num_frames * 16; i++) {
211 etm_ctx->trace_data[i].pipestat = (trace_data[i] & 0x7);
212 etm_ctx->trace_data[i].packet = (trace_data[i] & 0x78) >> 3;
213 etm_ctx->trace_data[i].flags = 0;
214
215 if ((trace_data[i] & 0x80) >> 7)
216 etm_ctx->trace_data[i].flags |= ETMV1_TRACESYNC_CYCLE;
217
218 if (etm_ctx->trace_data[i].pipestat == STAT_TR) {
219 etm_ctx->trace_data[i].pipestat = etm_ctx->trace_data[i].packet & 0x7;
220 etm_ctx->trace_data[i].flags |= ETMV1_TRIGGER_CYCLE;
221 }
222 }
223
224 free(trace_data);
225
226 return ERROR_OK;
227 }
228
229 static int oocd_trace_start_capture(struct etm_context *etm_ctx)
230 {
231 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
232 uint32_t control = 0x1; /* 0x1: enabled */
233 uint32_t trigger_count;
234
235 if (((etm_ctx->control & ETM_PORT_MODE_MASK) != ETM_PORT_NORMAL)
236 || ((etm_ctx->control & ETM_PORT_WIDTH_MASK) != ETM_PORT_4BIT)) {
237 LOG_DEBUG("OpenOCD + trace only supports normal 4-bit ETM mode");
238 return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
239 }
240
241 if ((etm_ctx->control & ETM_PORT_CLOCK_MASK) == ETM_PORT_HALF_CLOCK)
242 control |= 0x2; /* half rate clock, capture at twice the clock rate */
243
244 /* OpenOCD + trace holds up to 16 million samples,
245 * but trigger counts is set in multiples of 16 */
246 trigger_count = (1048576 * /* trigger_percent */ 50) / 100;
247
248 /* capturing always starts at address zero */
249 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, 0x0);
250 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_TRIGGER_COUNTER, trigger_count);
251 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, control);
252
253 /* we're starting a new trace, initialize capture status */
254 etm_ctx->capture_status = TRACE_RUNNING;
255
256 return ERROR_OK;
257 }
258
259 static int oocd_trace_stop_capture(struct etm_context *etm_ctx)
260 {
261 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
262
263 /* trace stopped, just clear running flag, but preserve others */
264 etm_ctx->capture_status &= ~TRACE_RUNNING;
265
266 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, 0x0);
267
268 return ERROR_OK;
269 }
270
271 COMMAND_HANDLER(handle_oocd_trace_config_command)
272 {
273 struct target *target;
274 struct arm *arm;
275
276 if (CMD_ARGC != 2)
277 return ERROR_COMMAND_SYNTAX_ERROR;
278
279 target = get_current_target(CMD_CTX);
280 arm = target_to_arm(target);
281 if (!is_arm(arm)) {
282 command_print(CMD_CTX, "current target isn't an ARM");
283 return ERROR_FAIL;
284 }
285
286 if (arm->etm) {
287 struct oocd_trace *oocd_trace = malloc(sizeof(struct oocd_trace));
288
289 arm->etm->capture_driver_priv = oocd_trace;
290 oocd_trace->etm_ctx = arm->etm;
291
292 /* copy name of TTY device used to communicate with OpenOCD + trace */
293 oocd_trace->tty = strndup(CMD_ARGV[1], 256);
294 } else
295 LOG_ERROR("target has no ETM defined, OpenOCD + trace left unconfigured");
296
297 return ERROR_OK;
298 }
299
300 COMMAND_HANDLER(handle_oocd_trace_status_command)
301 {
302 struct target *target;
303 struct arm *arm;
304 struct oocd_trace *oocd_trace;
305 uint32_t status;
306
307 target = get_current_target(CMD_CTX);
308
309 arm = target_to_arm(target);
310 if (!is_arm(arm)) {
311 command_print(CMD_CTX, "current target isn't an ARM");
312 return ERROR_FAIL;
313 }
314
315 if (!arm->etm) {
316 command_print(CMD_CTX, "current target doesn't have an ETM configured");
317 return ERROR_FAIL;
318 }
319
320 if (strcmp(arm->etm->capture_driver->name, "oocd_trace") != 0) {
321 command_print(CMD_CTX, "current target's ETM capture driver isn't 'oocd_trace'");
322 return ERROR_FAIL;
323 }
324
325 oocd_trace = (struct oocd_trace *)arm->etm->capture_driver_priv;
326
327 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
328
329 if (status & 0x8)
330 command_print(CMD_CTX, "trace clock locked");
331 else
332 command_print(CMD_CTX, "no trace clock");
333
334 return ERROR_OK;
335 }
336
337 COMMAND_HANDLER(handle_oocd_trace_resync_command)
338 {
339 struct target *target;
340 struct arm *arm;
341 struct oocd_trace *oocd_trace;
342 size_t bytes_written;
343 uint8_t cmd_array[1];
344
345 target = get_current_target(CMD_CTX);
346
347 arm = target_to_arm(target);
348 if (!is_arm(arm)) {
349 command_print(CMD_CTX, "current target isn't an ARM");
350 return ERROR_FAIL;
351 }
352
353 if (!arm->etm) {
354 command_print(CMD_CTX, "current target doesn't have an ETM configured");
355 return ERROR_FAIL;
356 }
357
358 if (strcmp(arm->etm->capture_driver->name, "oocd_trace") != 0) {
359 command_print(CMD_CTX, "current target's ETM capture driver isn't 'oocd_trace'");
360 return ERROR_FAIL;
361 }
362
363 oocd_trace = (struct oocd_trace *)arm->etm->capture_driver_priv;
364
365 cmd_array[0] = 0xf0;
366
367 bytes_written = write(oocd_trace->tty_fd, cmd_array, 1);
368 if (bytes_written < 1)
369 return ERROR_FAIL;
370
371 command_print(CMD_CTX, "requesting traceclock resync");
372 LOG_DEBUG("resyncing traceclk pll");
373
374 return ERROR_OK;
375 }
376
377 static const struct command_registration oocd_trace_all_command_handlers[] = {
378 {
379 .name = "config",
380 .handler = handle_oocd_trace_config_command,
381 .mode = COMMAND_CONFIG,
382 .usage = "<target> <tty>",
383 },
384 {
385 .name = "status",
386 .handler = handle_oocd_trace_status_command,
387 .mode = COMMAND_EXEC,
388 .usage = "",
389 .help = "display OpenOCD + trace status",
390 },
391 {
392 .name = "resync",
393 .handler = handle_oocd_trace_resync_command,
394 .mode = COMMAND_EXEC,
395 .usage = "",
396 .help = "resync OpenOCD + trace capture clock",
397 },
398 COMMAND_REGISTRATION_DONE
399 };
400 static const struct command_registration oocd_trace_command_handlers[] = {
401 {
402 .name = "oocd_trace",
403 .mode = COMMAND_ANY,
404 .help = "OpenOCD trace capture driver command group",
405 .usage = "",
406 .chain = oocd_trace_all_command_handlers,
407 },
408 COMMAND_REGISTRATION_DONE
409 };
410
411 struct etm_capture_driver oocd_trace_capture_driver = {
412 .name = "oocd_trace",
413 .commands = oocd_trace_command_handlers,
414 .init = oocd_trace_init,
415 .status = oocd_trace_status,
416 .start_capture = oocd_trace_start_capture,
417 .stop_capture = oocd_trace_stop_capture,
418 .read_trace = oocd_trace_read_trace,
419 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)