openocd: src/target: replace the GPL-2.0-or-later license tag
[openocd.git] / src / target / semihosting_common.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4 * Copyright (C) 2018 by Liviu Ionescu *
5 * <ilg@livius.net> *
6 * *
7 * Copyright (C) 2018 by Marvell Technology Group Ltd. *
8 * Written by Nicolas Pitre <nico@marvell.com> *
9 * *
10 * Copyright (C) 2010 by Spencer Oliver *
11 * spen@spen-soft.co.uk *
12 * *
13 * Copyright (C) 2016 by Square, Inc. *
14 * Steven Stallion <stallion@squareup.com> *
15 ***************************************************************************/
16
17 /**
18 * @file
19 * Common ARM semihosting support.
20 *
21 * Semihosting enables code running on a target to use some of the I/O
22 * facilities on the host computer. The target application must be linked
23 * against a library that forwards operation requests by using an
24 * instruction trapped by the debugger.
25 *
26 * Details can be found in
27 * "Semihosting for AArch32 and AArch64, Release 2.0"
28 * https://static.docs.arm.com/100863/0200/semihosting.pdf
29 * from ARM Ltd.
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include "target.h"
37 #include "target_type.h"
38 #include "semihosting_common.h"
39
40 #include <helper/binarybuffer.h>
41 #include <helper/log.h>
42 #include <sys/stat.h>
43
44 /**
45 * It is not possible to use O_... flags defined in sys/stat.h because they
46 * are not guaranteed to match the values defined by the GDB Remote Protocol.
47 * See https://sourceware.org/gdb/onlinedocs/gdb/Open-Flags.html#Open-Flags
48 */
49 enum {
50 TARGET_O_RDONLY = 0x000,
51 TARGET_O_WRONLY = 0x001,
52 TARGET_O_RDWR = 0x002,
53 TARGET_O_APPEND = 0x008,
54 TARGET_O_CREAT = 0x200,
55 TARGET_O_TRUNC = 0x400,
56 /* O_EXCL=0x800 is not required in this implementation. */
57 };
58
59 /* GDB remote protocol does not differentiate between text and binary open modes. */
60 static const int open_gdb_modeflags[12] = {
61 TARGET_O_RDONLY,
62 TARGET_O_RDONLY,
63 TARGET_O_RDWR,
64 TARGET_O_RDWR,
65 TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_TRUNC,
66 TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_TRUNC,
67 TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_TRUNC,
68 TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_TRUNC,
69 TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_APPEND,
70 TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_APPEND,
71 TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_APPEND,
72 TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_APPEND
73 };
74
75 static const int open_host_modeflags[12] = {
76 O_RDONLY,
77 O_RDONLY | O_BINARY,
78 O_RDWR,
79 O_RDWR | O_BINARY,
80 O_WRONLY | O_CREAT | O_TRUNC,
81 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
82 O_RDWR | O_CREAT | O_TRUNC,
83 O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
84 O_WRONLY | O_CREAT | O_APPEND,
85 O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
86 O_RDWR | O_CREAT | O_APPEND,
87 O_RDWR | O_CREAT | O_APPEND | O_BINARY
88 };
89
90 static int semihosting_common_fileio_info(struct target *target,
91 struct gdb_fileio_info *fileio_info);
92 static int semihosting_common_fileio_end(struct target *target, int result,
93 int fileio_errno, bool ctrl_c);
94
95 /* Attempts to include gdb_server.h failed. */
96 extern int gdb_actual_connections;
97
98 /**
99 * Initialize common semihosting support.
100 *
101 * @param target Pointer to the target to initialize.
102 * @param setup
103 * @param post_result
104 * @return An error status if there is a problem during initialization.
105 */
106 int semihosting_common_init(struct target *target, void *setup,
107 void *post_result)
108 {
109 LOG_DEBUG(" ");
110
111 target->fileio_info = malloc(sizeof(*target->fileio_info));
112 if (!target->fileio_info) {
113 LOG_ERROR("out of memory");
114 return ERROR_FAIL;
115 }
116 memset(target->fileio_info, 0, sizeof(*target->fileio_info));
117
118 struct semihosting *semihosting;
119 semihosting = malloc(sizeof(*target->semihosting));
120 if (!semihosting) {
121 LOG_ERROR("out of memory");
122 return ERROR_FAIL;
123 }
124
125 semihosting->is_active = false;
126 semihosting->redirect_cfg = SEMIHOSTING_REDIRECT_CFG_NONE;
127 semihosting->tcp_connection = NULL;
128 semihosting->stdin_fd = -1;
129 semihosting->stdout_fd = -1;
130 semihosting->stderr_fd = -1;
131 semihosting->is_fileio = false;
132 semihosting->hit_fileio = false;
133 semihosting->is_resumable = false;
134 semihosting->has_resumable_exit = false;
135 semihosting->word_size_bytes = 0;
136 semihosting->op = -1;
137 semihosting->param = 0;
138 semihosting->result = -1;
139 semihosting->sys_errno = -1;
140 semihosting->cmdline = NULL;
141 semihosting->basedir = NULL;
142
143 /* If possible, update it in setup(). */
144 semihosting->setup_time = clock();
145
146 semihosting->setup = setup;
147 semihosting->post_result = post_result;
148 semihosting->user_command_extension = NULL;
149
150 target->semihosting = semihosting;
151
152 target->type->get_gdb_fileio_info = semihosting_common_fileio_info;
153 target->type->gdb_fileio_end = semihosting_common_fileio_end;
154
155 return ERROR_OK;
156 }
157
158 struct semihosting_tcp_service {
159 struct semihosting *semihosting;
160 char *name;
161 int error;
162 };
163
164 static bool semihosting_is_redirected(struct semihosting *semihosting, int fd)
165 {
166 if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_NONE)
167 return false;
168
169 bool is_read_op = false;
170
171 switch (semihosting->op) {
172 /* check debug semihosting operations: READC, WRITEC and WRITE0 */
173 case SEMIHOSTING_SYS_READC:
174 is_read_op = true;
175 /* fall through */
176 case SEMIHOSTING_SYS_WRITEC:
177 case SEMIHOSTING_SYS_WRITE0:
178 /* debug operations are redirected when CFG is either DEBUG or ALL */
179 if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_STDIO)
180 return false;
181 break;
182
183 /* check stdio semihosting operations: READ and WRITE */
184 case SEMIHOSTING_SYS_READ:
185 is_read_op = true;
186 /* fall through */
187 case SEMIHOSTING_SYS_WRITE:
188 /* stdio operations are redirected when CFG is either STDIO or ALL */
189 if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_DEBUG)
190 return false;
191 break;
192
193 default:
194 return false;
195 }
196
197 if (is_read_op)
198 return fd == semihosting->stdin_fd;
199
200 /* write operation */
201 return fd == semihosting->stdout_fd || fd == semihosting->stderr_fd;
202 }
203
204 static ssize_t semihosting_redirect_write(struct semihosting *semihosting, void *buf, int size)
205 {
206 if (!semihosting->tcp_connection) {
207 LOG_ERROR("No connected TCP client for semihosting");
208 semihosting->sys_errno = EBADF; /* Bad file number */
209 return -1;
210 }
211
212 struct semihosting_tcp_service *service = semihosting->tcp_connection->service->priv;
213
214 int retval = connection_write(semihosting->tcp_connection, buf, size);
215
216 if (retval < 0)
217 log_socket_error(service->name);
218
219 return retval;
220 }
221
222 static ssize_t semihosting_write(struct semihosting *semihosting, int fd, void *buf, int size)
223 {
224 if (semihosting_is_redirected(semihosting, fd))
225 return semihosting_redirect_write(semihosting, buf, size);
226
227 /* default write */
228 return write(fd, buf, size);
229 }
230
231 static ssize_t semihosting_redirect_read(struct semihosting *semihosting, void *buf, int size)
232 {
233 if (!semihosting->tcp_connection) {
234 LOG_ERROR("No connected TCP client for semihosting");
235 semihosting->sys_errno = EBADF; /* Bad file number */
236 return -1;
237 }
238
239 struct semihosting_tcp_service *service = semihosting->tcp_connection->service->priv;
240
241 service->error = ERROR_OK;
242 semihosting->tcp_connection->input_pending = true;
243
244 int retval = connection_read(semihosting->tcp_connection, buf, size);
245
246 if (retval <= 0)
247 service->error = ERROR_SERVER_REMOTE_CLOSED;
248
249 if (retval < 0)
250 log_socket_error(service->name);
251
252 semihosting->tcp_connection->input_pending = false;
253
254 return retval;
255 }
256
257 static inline int semihosting_putchar(struct semihosting *semihosting, int fd, int c)
258 {
259 if (semihosting_is_redirected(semihosting, fd))
260 return semihosting_redirect_write(semihosting, &c, 1);
261
262 /* default putchar */
263 return putchar(c);
264 }
265
266 static inline ssize_t semihosting_read(struct semihosting *semihosting, int fd, void *buf, int size)
267 {
268 if (semihosting_is_redirected(semihosting, fd))
269 return semihosting_redirect_read(semihosting, buf, size);
270
271 /* default read */
272 ssize_t result = read(fd, buf, size);
273 semihosting->sys_errno = errno;
274
275 return result;
276 }
277
278 static inline int semihosting_getchar(struct semihosting *semihosting, int fd)
279 {
280 if (semihosting_is_redirected(semihosting, fd)) {
281 unsigned char c;
282
283 if (semihosting_redirect_read(semihosting, &c, 1) > 0)
284 return c;
285
286 return EOF;
287 }
288
289 /* default getchar */
290 return getchar();
291 }
292
293 /**
294 * User operation parameter string storage buffer. Contains valid data when the
295 * TARGET_EVENT_SEMIHOSTING_USER_CMD_xxxxx event callbacks are running.
296 */
297 static char *semihosting_user_op_params;
298
299 /**
300 * Portable implementation of ARM semihosting calls.
301 * Performs the currently pending semihosting operation
302 * encoded in target->semihosting.
303 */
304 int semihosting_common(struct target *target)
305 {
306 struct semihosting *semihosting = target->semihosting;
307 if (!semihosting) {
308 /* Silently ignore if the semihosting field was not set. */
309 return ERROR_OK;
310 }
311
312 struct gdb_fileio_info *fileio_info = target->fileio_info;
313
314 /*
315 * By default return an error.
316 * The actual result must be set by each function
317 */
318 semihosting->result = -1;
319
320 /* Most operations are resumable, except the two exit calls. */
321 semihosting->is_resumable = true;
322
323 int retval;
324
325 /* Enough space to hold 4 long words. */
326 uint8_t fields[4*8];
327
328 LOG_DEBUG("op=0x%x, param=0x%" PRIx64, semihosting->op,
329 semihosting->param);
330
331 switch (semihosting->op) {
332
333 case SEMIHOSTING_SYS_CLOCK: /* 0x10 */
334 /*
335 * Returns the number of centiseconds (hundredths of a second)
336 * since the execution started.
337 *
338 * Values returned can be of limited use for some benchmarking
339 * purposes because of communication overhead or other
340 * agent-specific factors. For example, with a debug hardware
341 * unit the request is passed back to the host for execution.
342 * This can lead to unpredictable delays in transmission and
343 * process scheduling.
344 *
345 * Use this function to calculate time intervals, by calculating
346 * differences between intervals with and without the code
347 * sequence to be timed.
348 *
349 * Entry
350 * The PARAMETER REGISTER must contain 0. There are no other
351 * parameters.
352 *
353 * Return
354 * On exit, the RETURN REGISTER contains:
355 * - The number of centiseconds since some arbitrary start
356 * point, if the call is successful.
357 * - –1 if the call is not successful. For example, because
358 * of a communications error.
359 */
360 {
361 clock_t delta = clock() - semihosting->setup_time;
362
363 semihosting->result = delta / (CLOCKS_PER_SEC / 100);
364 }
365 break;
366
367 case SEMIHOSTING_SYS_CLOSE: /* 0x02 */
368 /*
369 * Closes a file on the host system. The handle must reference
370 * a file that was opened with SYS_OPEN.
371 *
372 * Entry
373 * On entry, the PARAMETER REGISTER contains a pointer to a
374 * one-field argument block:
375 * - field 1 Contains a handle for an open file.
376 *
377 * Return
378 * On exit, the RETURN REGISTER contains:
379 * - 0 if the call is successful
380 * - –1 if the call is not successful.
381 */
382 retval = semihosting_read_fields(target, 1, fields);
383 if (retval != ERROR_OK)
384 return retval;
385 else {
386 int fd = semihosting_get_field(target, 0, fields);
387 /* Do not allow to close OpenOCD's own standard streams */
388 if (fd == 0 || fd == 1 || fd == 2) {
389 LOG_DEBUG("ignoring semihosting attempt to close %s",
390 (fd == 0) ? "stdin" :
391 (fd == 1) ? "stdout" : "stderr");
392 /* Just pretend success */
393 if (semihosting->is_fileio) {
394 semihosting->result = 0;
395 } else {
396 semihosting->result = 0;
397 semihosting->sys_errno = 0;
398 }
399 break;
400 }
401 /* Close the descriptor */
402 if (semihosting->is_fileio) {
403 semihosting->hit_fileio = true;
404 fileio_info->identifier = "close";
405 fileio_info->param_1 = fd;
406 } else {
407 semihosting->result = close(fd);
408 semihosting->sys_errno = errno;
409 LOG_DEBUG("close(%d)=%d", fd, (int)semihosting->result);
410 }
411 }
412 break;
413
414 case SEMIHOSTING_SYS_ERRNO: /* 0x13 */
415 /*
416 * Returns the value of the C library errno variable that is
417 * associated with the semihosting implementation. The errno
418 * variable can be set by a number of C library semihosted
419 * functions, including:
420 * - SYS_REMOVE
421 * - SYS_OPEN
422 * - SYS_CLOSE
423 * - SYS_READ
424 * - SYS_WRITE
425 * - SYS_SEEK.
426 *
427 * Whether errno is set or not, and to what value, is entirely
428 * host-specific, except where the ISO C standard defines the
429 * behavior.
430 *
431 * Entry
432 * There are no parameters. The PARAMETER REGISTER must be 0.
433 *
434 * Return
435 * On exit, the RETURN REGISTER contains the value of the C
436 * library errno variable.
437 */
438 semihosting->result = semihosting->sys_errno;
439 break;
440
441 case SEMIHOSTING_SYS_EXIT: /* 0x18 */
442 /*
443 * Note: SYS_EXIT was called angel_SWIreason_ReportException in
444 * previous versions of the documentation.
445 *
446 * An application calls this operation to report an exception
447 * to the debugger directly. The most common use is to report
448 * that execution has completed, using ADP_Stopped_ApplicationExit.
449 *
450 * Note: This semihosting operation provides no means for 32-bit
451 * callers to indicate an application exit with a specified exit
452 * code. Semihosting callers may prefer to check for the presence
453 * of the SH_EXT_EXTENDED_REPORT_EXCEPTION extension and use
454 * the SYS_REPORT_EXCEPTION_EXTENDED operation instead, if it
455 * is available.
456 *
457 * Entry (32-bit)
458 * On entry, the PARAMETER register is set to a reason code
459 * describing the cause of the trap. Not all semihosting client
460 * implementations will necessarily trap every corresponding
461 * event. Important reason codes are:
462 *
463 * - ADP_Stopped_ApplicationExit 0x20026
464 * - ADP_Stopped_RunTimeErrorUnknown 0x20023
465 *
466 * Entry (64-bit)
467 * On entry, the PARAMETER REGISTER contains a pointer to a
468 * two-field argument block:
469 * - field 1 The exception type, which is one of the set of
470 * reason codes in the above tables.
471 * - field 2 A subcode, whose meaning depends on the reason
472 * code in field 1.
473 * In particular, if field 1 is ADP_Stopped_ApplicationExit
474 * then field 2 is an exit status code, as passed to the C
475 * standard library exit() function. A simulator receiving
476 * this request must notify a connected debugger, if present,
477 * and then exit with the specified status.
478 *
479 * Return
480 * No return is expected from these calls. However, it is
481 * possible for the debugger to request that the application
482 * continues by performing an RDI_Execute request or equivalent.
483 * In this case, execution continues with the registers as they
484 * were on entry to the operation, or as subsequently modified
485 * by the debugger.
486 */
487 if (semihosting->word_size_bytes == 8) {
488 retval = semihosting_read_fields(target, 2, fields);
489 if (retval != ERROR_OK)
490 return retval;
491 else {
492 int type = semihosting_get_field(target, 0, fields);
493 int code = semihosting_get_field(target, 1, fields);
494
495 if (type == ADP_STOPPED_APPLICATION_EXIT) {
496 if (!gdb_actual_connections)
497 exit(code);
498 else {
499 fprintf(stderr,
500 "semihosting: *** application exited with %d ***\n",
501 code);
502 }
503 } else {
504 fprintf(stderr,
505 "semihosting: application exception %#x\n",
506 type);
507 }
508 }
509 } else {
510 if (semihosting->param == ADP_STOPPED_APPLICATION_EXIT) {
511 if (!gdb_actual_connections)
512 exit(0);
513 else {
514 fprintf(stderr,
515 "semihosting: *** application exited normally ***\n");
516 }
517 } else if (semihosting->param == ADP_STOPPED_RUN_TIME_ERROR) {
518 /* Chosen more or less arbitrarily to have a nicer message,
519 * otherwise all other return the same exit code 1. */
520 if (!gdb_actual_connections)
521 exit(1);
522 else {
523 fprintf(stderr,
524 "semihosting: *** application exited with error ***\n");
525 }
526 } else {
527 if (!gdb_actual_connections)
528 exit(1);
529 else {
530 fprintf(stderr,
531 "semihosting: application exception %#x\n",
532 (unsigned) semihosting->param);
533 }
534 }
535 }
536 if (!semihosting->has_resumable_exit) {
537 semihosting->is_resumable = false;
538 return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
539 }
540 break;
541
542 case SEMIHOSTING_SYS_EXIT_EXTENDED: /* 0x20 */
543 /*
544 * This operation is only supported if the semihosting extension
545 * SH_EXT_EXIT_EXTENDED is implemented. SH_EXT_EXIT_EXTENDED is
546 * reported using feature byte 0, bit 0. If this extension is
547 * supported, then the implementation provides a means to
548 * report a normal exit with a nonzero exit status in both 32-bit
549 * and 64-bit semihosting APIs.
550 *
551 * The implementation must provide the semihosting call
552 * SYS_EXIT_EXTENDED for both A64 and A32/T32 semihosting APIs.
553 *
554 * SYS_EXIT_EXTENDED is used by an application to report an
555 * exception or exit to the debugger directly. The most common
556 * use is to report that execution has completed, using
557 * ADP_Stopped_ApplicationExit.
558 *
559 * Entry
560 * On entry, the PARAMETER REGISTER contains a pointer to a
561 * two-field argument block:
562 * - field 1 The exception type, which should be one of the set
563 * of reason codes that are documented for the SYS_EXIT
564 * (0x18) call. For example, ADP_Stopped_ApplicationExit.
565 * - field 2 A subcode, whose meaning depends on the reason
566 * code in field 1. In particular, if field 1 is
567 * ADP_Stopped_ApplicationExit then field 2 is an exit status
568 * code, as passed to the C standard library exit() function.
569 * A simulator receiving this request must notify a connected
570 * debugger, if present, and then exit with the specified status.
571 *
572 * Return
573 * No return is expected from these calls.
574 *
575 * For the A64 API, this call is identical to the behavior of
576 * the mandatory SYS_EXIT (0x18) call. If this extension is
577 * supported, then both calls must be implemented.
578 */
579 retval = semihosting_read_fields(target, 2, fields);
580 if (retval != ERROR_OK)
581 return retval;
582 else {
583 int type = semihosting_get_field(target, 0, fields);
584 int code = semihosting_get_field(target, 1, fields);
585
586 if (type == ADP_STOPPED_APPLICATION_EXIT) {
587 if (!gdb_actual_connections)
588 exit(code);
589 else {
590 fprintf(stderr,
591 "semihosting: *** application exited with %d ***\n",
592 code);
593 }
594 } else {
595 fprintf(stderr, "semihosting: exception %#x\n",
596 type);
597 }
598 }
599 if (!semihosting->has_resumable_exit) {
600 semihosting->is_resumable = false;
601 return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
602 }
603 break;
604
605 case SEMIHOSTING_SYS_FLEN: /* 0x0C */
606 /*
607 * Returns the length of a specified file.
608 *
609 * Entry
610 * On entry, the PARAMETER REGISTER contains a pointer to a
611 * one-field argument block:
612 * - field 1 A handle for a previously opened, seekable file
613 * object.
614 *
615 * Return
616 * On exit, the RETURN REGISTER contains:
617 * - The current length of the file object, if the call is
618 * successful.
619 * - –1 if an error occurs.
620 */
621 if (semihosting->is_fileio) {
622 semihosting->result = -1;
623 semihosting->sys_errno = EINVAL;
624 }
625 retval = semihosting_read_fields(target, 1, fields);
626 if (retval != ERROR_OK)
627 return retval;
628 else {
629 int fd = semihosting_get_field(target, 0, fields);
630 struct stat buf;
631 semihosting->result = fstat(fd, &buf);
632 if (semihosting->result == -1) {
633 semihosting->sys_errno = errno;
634 LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result);
635 break;
636 }
637 LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result);
638 semihosting->result = buf.st_size;
639 }
640 break;
641
642 case SEMIHOSTING_SYS_GET_CMDLINE: /* 0x15 */
643 /*
644 * Returns the command line that is used for the call to the
645 * executable, that is, argc and argv.
646 *
647 * Entry
648 * On entry, the PARAMETER REGISTER points to a two-field data
649 * block to be used for returning the command string and its length:
650 * - field 1 A pointer to a buffer of at least the size that is
651 * specified in field 2.
652 * - field 2 The length of the buffer in bytes.
653 *
654 * Return
655 * On exit:
656 * If the call is successful, then the RETURN REGISTER contains 0,
657 * the PARAMETER REGISTER is unchanged, and the data block is
658 * updated as follows:
659 * - field 1 A pointer to a null-terminated string of the command
660 * line.
661 * - field 2 The length of the string in bytes.
662 * If the call is not successful, then the RETURN REGISTER
663 * contains -1.
664 *
665 * Note: The semihosting implementation might impose limits on
666 * the maximum length of the string that can be transferred.
667 * However, the implementation must be able to support a
668 * command-line length of at least 80 bytes.
669 */
670 retval = semihosting_read_fields(target, 2, fields);
671 if (retval != ERROR_OK)
672 return retval;
673 else {
674 uint64_t addr = semihosting_get_field(target, 0, fields);
675 size_t size = semihosting_get_field(target, 1, fields);
676
677 char *arg = semihosting->cmdline ?
678 semihosting->cmdline : "";
679 uint32_t len = strlen(arg) + 1;
680 if (len > size)
681 semihosting->result = -1;
682 else {
683 semihosting_set_field(target, len, 1, fields);
684 retval = target_write_buffer(target, addr, len,
685 (uint8_t *)arg);
686 if (retval != ERROR_OK)
687 return retval;
688 semihosting->result = 0;
689
690 retval = semihosting_write_fields(target, 2, fields);
691 if (retval != ERROR_OK)
692 return retval;
693 }
694 LOG_DEBUG("SYS_GET_CMDLINE=[%s],%d", arg,
695 (int)semihosting->result);
696 }
697 break;
698
699 case SEMIHOSTING_SYS_HEAPINFO: /* 0x16 */
700 /*
701 * Returns the system stack and heap parameters.
702 *
703 * Entry
704 * On entry, the PARAMETER REGISTER contains the address of a
705 * pointer to a four-field data block. The contents of the data
706 * block are filled by the function. The following C-like
707 * pseudocode describes the layout of the block:
708 * struct block {
709 * void* heap_base;
710 * void* heap_limit;
711 * void* stack_base;
712 * void* stack_limit;
713 * };
714 *
715 * Return
716 * On exit, the PARAMETER REGISTER is unchanged and the data
717 * block has been updated.
718 */
719 retval = semihosting_read_fields(target, 1, fields);
720 if (retval != ERROR_OK)
721 return retval;
722 else {
723 uint64_t addr = semihosting_get_field(target, 0, fields);
724 /* tell the remote we have no idea */
725 memset(fields, 0, 4 * semihosting->word_size_bytes);
726 retval = target_write_memory(target, addr, 4,
727 semihosting->word_size_bytes,
728 fields);
729 if (retval != ERROR_OK)
730 return retval;
731 semihosting->result = 0;
732 }
733 break;
734
735 case SEMIHOSTING_SYS_ISERROR: /* 0x08 */
736 /*
737 * Determines whether the return code from another semihosting
738 * call is an error status or not.
739 *
740 * This call is passed a parameter block containing the error
741 * code to examine.
742 *
743 * Entry
744 * On entry, the PARAMETER REGISTER contains a pointer to a
745 * one-field data block:
746 * - field 1 The required status word to check.
747 *
748 * Return
749 * On exit, the RETURN REGISTER contains:
750 * - 0 if the status field is not an error indication
751 * - A nonzero value if the status field is an error indication.
752 */
753 retval = semihosting_read_fields(target, 1, fields);
754 if (retval != ERROR_OK)
755 return retval;
756
757 uint64_t code = semihosting_get_field(target, 0, fields);
758 semihosting->result = (code != 0);
759 break;
760
761 case SEMIHOSTING_SYS_ISTTY: /* 0x09 */
762 /*
763 * Checks whether a file is connected to an interactive device.
764 *
765 * Entry
766 * On entry, the PARAMETER REGISTER contains a pointer to a
767 * one-field argument block:
768 * field 1 A handle for a previously opened file object.
769 *
770 * Return
771 * On exit, the RETURN REGISTER contains:
772 * - 1 if the handle identifies an interactive device.
773 * - 0 if the handle identifies a file.
774 * - A value other than 1 or 0 if an error occurs.
775 */
776 if (semihosting->is_fileio) {
777 semihosting->hit_fileio = true;
778 fileio_info->identifier = "isatty";
779 fileio_info->param_1 = semihosting->param;
780 } else {
781 retval = semihosting_read_fields(target, 1, fields);
782 if (retval != ERROR_OK)
783 return retval;
784 int fd = semihosting_get_field(target, 0, fields);
785 semihosting->result = isatty(fd);
786 semihosting->sys_errno = errno;
787 LOG_DEBUG("isatty(%d)=%d", fd, (int)semihosting->result);
788 }
789 break;
790
791 case SEMIHOSTING_SYS_OPEN: /* 0x01 */
792 /*
793 * Opens a file on the host system.
794 *
795 * The file path is specified either as relative to the current
796 * directory of the host process, or absolute, using the path
797 * conventions of the host operating system.
798 *
799 * Semihosting implementations must support opening the special
800 * path name :semihosting-features as part of the semihosting
801 * extensions reporting mechanism.
802 *
803 * ARM targets interpret the special path name :tt as meaning
804 * the console input stream, for an open-read or the console
805 * output stream, for an open-write. Opening these streams is
806 * performed as part of the standard startup code for those
807 * applications that reference the C stdio streams. The
808 * semihosting extension SH_EXT_STDOUT_STDERR allows the
809 * semihosting caller to open separate output streams
810 * corresponding to stdout and stderr. This extension is
811 * reported using feature byte 0, bit 1. Use SYS_OPEN with
812 * the special path name :semihosting-features to access the
813 * feature bits.
814 *
815 * If this extension is supported, the implementation must
816 * support the following additional semantics to SYS_OPEN:
817 * - If the special path name :tt is opened with an fopen
818 * mode requesting write access (w, wb, w+, or w+b), then
819 * this is a request to open stdout.
820 * - If the special path name :tt is opened with a mode
821 * requesting append access (a, ab, a+, or a+b), then this is
822 * a request to open stderr.
823 *
824 * Entry
825 * On entry, the PARAMETER REGISTER contains a pointer to a
826 * three-field argument block:
827 * - field 1 A pointer to a null-terminated string containing
828 * a file or device name.
829 * - field 2 An integer that specifies the file opening mode.
830 * - field 3 An integer that gives the length of the string
831 * pointed to by field 1.
832 *
833 * The length does not include the terminating null character
834 * that must be present.
835 *
836 * Return
837 * On exit, the RETURN REGISTER contains:
838 * - A nonzero handle if the call is successful.
839 * - –1 if the call is not successful.
840 */
841 retval = semihosting_read_fields(target, 3, fields);
842 if (retval != ERROR_OK)
843 return retval;
844 else {
845 uint64_t addr = semihosting_get_field(target, 0, fields);
846 uint32_t mode = semihosting_get_field(target, 1, fields);
847 size_t len = semihosting_get_field(target, 2, fields);
848
849 if (mode > 11) {
850 semihosting->result = -1;
851 semihosting->sys_errno = EINVAL;
852 break;
853 }
854 size_t basedir_len = semihosting->basedir ? strlen(semihosting->basedir) : 0;
855 uint8_t *fn = malloc(basedir_len + len + 2);
856 if (!fn) {
857 semihosting->result = -1;
858 semihosting->sys_errno = ENOMEM;
859 } else {
860 if (basedir_len > 0) {
861 strcpy((char *)fn, semihosting->basedir);
862 if (fn[basedir_len - 1] != '/')
863 fn[basedir_len++] = '/';
864 }
865 retval = target_read_memory(target, addr, 1, len, fn + basedir_len);
866 if (retval != ERROR_OK) {
867 free(fn);
868 return retval;
869 }
870 fn[basedir_len + len] = 0;
871 /* TODO: implement the :semihosting-features special file.
872 * */
873 if (semihosting->is_fileio) {
874 if (strcmp((char *)fn, ":semihosting-features") == 0) {
875 semihosting->result = -1;
876 semihosting->sys_errno = EINVAL;
877 } else if (strcmp((char *)fn, ":tt") == 0) {
878 if (mode == 0)
879 semihosting->result = 0;
880 else if (mode == 4)
881 semihosting->result = 1;
882 else if (mode == 8)
883 semihosting->result = 2;
884 else
885 semihosting->result = -1;
886 } else {
887 semihosting->hit_fileio = true;
888 fileio_info->identifier = "open";
889 fileio_info->param_1 = addr;
890 fileio_info->param_2 = len;
891 fileio_info->param_3 = open_gdb_modeflags[mode];
892 fileio_info->param_4 = 0644;
893 }
894 } else {
895 if (strcmp((char *)fn, ":tt") == 0) {
896 /* Mode is:
897 * - 0-3 ("r") for stdin,
898 * - 4-7 ("w") for stdout,
899 * - 8-11 ("a") for stderr */
900 if (mode < 4) {
901 int fd = dup(STDIN_FILENO);
902 semihosting->result = fd;
903 semihosting->stdin_fd = fd;
904 semihosting->sys_errno = errno;
905 LOG_DEBUG("dup(STDIN)=%d",
906 (int)semihosting->result);
907 } else if (mode < 8) {
908 int fd = dup(STDOUT_FILENO);
909 semihosting->result = fd;
910 semihosting->stdout_fd = fd;
911 semihosting->sys_errno = errno;
912 LOG_DEBUG("dup(STDOUT)=%d",
913 (int)semihosting->result);
914 } else {
915 int fd = dup(STDERR_FILENO);
916 semihosting->result = fd;
917 semihosting->stderr_fd = fd;
918 semihosting->sys_errno = errno;
919 LOG_DEBUG("dup(STDERR)=%d",
920 (int)semihosting->result);
921 }
922 } else {
923 /* cygwin requires the permission setting
924 * otherwise it will fail to reopen a previously
925 * written file */
926 semihosting->result = open((char *)fn,
927 open_host_modeflags[mode],
928 0644);
929 semihosting->sys_errno = errno;
930 LOG_DEBUG("open('%s')=%d", fn,
931 (int)semihosting->result);
932 }
933 }
934 free(fn);
935 }
936 }
937 break;
938
939 case SEMIHOSTING_SYS_READ: /* 0x06 */
940 /*
941 * Reads the contents of a file into a buffer. The file position
942 * is specified either:
943 * - Explicitly by a SYS_SEEK.
944 * - Implicitly one byte beyond the previous SYS_READ or
945 * SYS_WRITE request.
946 *
947 * The file position is at the start of the file when it is
948 * opened, and is lost when the file is closed. Perform the
949 * file operation as a single action whenever possible. For
950 * example, do not split a read of 16KB into four 4KB chunks
951 * unless there is no alternative.
952 *
953 * Entry
954 * On entry, the PARAMETER REGISTER contains a pointer to a
955 * three-field data block:
956 * - field 1 Contains a handle for a file previously opened
957 * with SYS_OPEN.
958 * - field 2 Points to a buffer.
959 * - field 3 Contains the number of bytes to read to the buffer
960 * from the file.
961 *
962 * Return
963 * On exit, the RETURN REGISTER contains the number of bytes not
964 * filled in the buffer (buffer_length - bytes_read) as follows:
965 * - If the RETURN REGISTER is 0, the entire buffer was
966 * successfully filled.
967 * - If the RETURN REGISTER is the same as field 3, no bytes
968 * were read (EOF can be assumed).
969 * - If the RETURN REGISTER contains a value smaller than
970 * field 3, the read succeeded but the buffer was only partly
971 * filled. For interactive devices, this is the most common
972 * return value.
973 */
974 retval = semihosting_read_fields(target, 3, fields);
975 if (retval != ERROR_OK)
976 return retval;
977 else {
978 int fd = semihosting_get_field(target, 0, fields);
979 uint64_t addr = semihosting_get_field(target, 1, fields);
980 size_t len = semihosting_get_field(target, 2, fields);
981 if (semihosting->is_fileio) {
982 semihosting->hit_fileio = true;
983 fileio_info->identifier = "read";
984 fileio_info->param_1 = fd;
985 fileio_info->param_2 = addr;
986 fileio_info->param_3 = len;
987 } else {
988 uint8_t *buf = malloc(len);
989 if (!buf) {
990 semihosting->result = -1;
991 semihosting->sys_errno = ENOMEM;
992 } else {
993 semihosting->result = semihosting_read(semihosting, fd, buf, len);
994 LOG_DEBUG("read(%d, 0x%" PRIx64 ", %zu)=%d",
995 fd,
996 addr,
997 len,
998 (int)semihosting->result);
999 if (semihosting->result >= 0) {
1000 retval = target_write_buffer(target, addr,
1001 semihosting->result,
1002 buf);
1003 if (retval != ERROR_OK) {
1004 free(buf);
1005 return retval;
1006 }
1007 /* the number of bytes NOT filled in */
1008 semihosting->result = len -
1009 semihosting->result;
1010 }
1011 free(buf);
1012 }
1013 }
1014 }
1015 break;
1016
1017 case SEMIHOSTING_SYS_READC: /* 0x07 */
1018 /*
1019 * Reads a byte from the console.
1020 *
1021 * Entry
1022 * The PARAMETER REGISTER must contain 0. There are no other
1023 * parameters or values possible.
1024 *
1025 * Return
1026 * On exit, the RETURN REGISTER contains the byte read from
1027 * the console.
1028 */
1029 if (semihosting->is_fileio) {
1030 LOG_ERROR("SYS_READC not supported by semihosting fileio");
1031 return ERROR_FAIL;
1032 }
1033 semihosting->result = semihosting_getchar(semihosting, semihosting->stdin_fd);
1034 LOG_DEBUG("getchar()=%d", (int)semihosting->result);
1035 break;
1036
1037 case SEMIHOSTING_SYS_REMOVE: /* 0x0E */
1038 /*
1039 * Deletes a specified file on the host filing system.
1040 *
1041 * Entry
1042 * On entry, the PARAMETER REGISTER contains a pointer to a
1043 * two-field argument block:
1044 * - field 1 Points to a null-terminated string that gives the
1045 * path name of the file to be deleted.
1046 * - field 2 The length of the string.
1047 *
1048 * Return
1049 * On exit, the RETURN REGISTER contains:
1050 * - 0 if the delete is successful
1051 * - A nonzero, host-specific error code if the delete fails.
1052 */
1053 retval = semihosting_read_fields(target, 2, fields);
1054 if (retval != ERROR_OK)
1055 return retval;
1056 else {
1057 uint64_t addr = semihosting_get_field(target, 0, fields);
1058 size_t len = semihosting_get_field(target, 1, fields);
1059 if (semihosting->is_fileio) {
1060 semihosting->hit_fileio = true;
1061 fileio_info->identifier = "unlink";
1062 fileio_info->param_1 = addr;
1063 fileio_info->param_2 = len;
1064 } else {
1065 uint8_t *fn = malloc(len+1);
1066 if (!fn) {
1067 semihosting->result = -1;
1068 semihosting->sys_errno = ENOMEM;
1069 } else {
1070 retval =
1071 target_read_memory(target, addr, 1, len,
1072 fn);
1073 if (retval != ERROR_OK) {
1074 free(fn);
1075 return retval;
1076 }
1077 fn[len] = 0;
1078 semihosting->result = remove((char *)fn);
1079 semihosting->sys_errno = errno;
1080 LOG_DEBUG("remove('%s')=%d", fn,
1081 (int)semihosting->result);
1082
1083 free(fn);
1084 }
1085 }
1086 }
1087 break;
1088
1089 case SEMIHOSTING_SYS_RENAME: /* 0x0F */
1090 /*
1091 * Renames a specified file.
1092 *
1093 * Entry
1094 * On entry, the PARAMETER REGISTER contains a pointer to a
1095 * four-field data block:
1096 * - field 1 A pointer to the name of the old file.
1097 * - field 2 The length of the old filename.
1098 * - field 3 A pointer to the new filename.
1099 * - field 4 The length of the new filename. Both strings are
1100 * null-terminated.
1101 *
1102 * Return
1103 * On exit, the RETURN REGISTER contains:
1104 * - 0 if the rename is successful.
1105 * - A nonzero, host-specific error code if the rename fails.
1106 */
1107 retval = semihosting_read_fields(target, 4, fields);
1108 if (retval != ERROR_OK)
1109 return retval;
1110 else {
1111 uint64_t addr1 = semihosting_get_field(target, 0, fields);
1112 size_t len1 = semihosting_get_field(target, 1, fields);
1113 uint64_t addr2 = semihosting_get_field(target, 2, fields);
1114 size_t len2 = semihosting_get_field(target, 3, fields);
1115 if (semihosting->is_fileio) {
1116 semihosting->hit_fileio = true;
1117 fileio_info->identifier = "rename";
1118 fileio_info->param_1 = addr1;
1119 fileio_info->param_2 = len1;
1120 fileio_info->param_3 = addr2;
1121 fileio_info->param_4 = len2;
1122 } else {
1123 uint8_t *fn1 = malloc(len1+1);
1124 uint8_t *fn2 = malloc(len2+1);
1125 if (!fn1 || !fn2) {
1126 free(fn1);
1127 free(fn2);
1128 semihosting->result = -1;
1129 semihosting->sys_errno = ENOMEM;
1130 } else {
1131 retval = target_read_memory(target, addr1, 1, len1,
1132 fn1);
1133 if (retval != ERROR_OK) {
1134 free(fn1);
1135 free(fn2);
1136 return retval;
1137 }
1138 retval = target_read_memory(target, addr2, 1, len2,
1139 fn2);
1140 if (retval != ERROR_OK) {
1141 free(fn1);
1142 free(fn2);
1143 return retval;
1144 }
1145 fn1[len1] = 0;
1146 fn2[len2] = 0;
1147 semihosting->result = rename((char *)fn1,
1148 (char *)fn2);
1149 semihosting->sys_errno = errno;
1150 LOG_DEBUG("rename('%s', '%s')=%d", fn1, fn2,
1151 (int)semihosting->result);
1152
1153 free(fn1);
1154 free(fn2);
1155 }
1156 }
1157 }
1158 break;
1159
1160 case SEMIHOSTING_SYS_SEEK: /* 0x0A */
1161 /*
1162 * Seeks to a specified position in a file using an offset
1163 * specified from the start of the file. The file is assumed
1164 * to be a byte array and the offset is given in bytes.
1165 *
1166 * Entry
1167 * On entry, the PARAMETER REGISTER contains a pointer to a
1168 * two-field data block:
1169 * - field 1 A handle for a seekable file object.
1170 * - field 2 The absolute byte position to seek to.
1171 *
1172 * Return
1173 * On exit, the RETURN REGISTER contains:
1174 * - 0 if the request is successful.
1175 * - A negative value if the request is not successful.
1176 * Use SYS_ERRNO to read the value of the host errno variable
1177 * describing the error.
1178 *
1179 * Note: The effect of seeking outside the current extent of
1180 * the file object is undefined.
1181 */
1182 retval = semihosting_read_fields(target, 2, fields);
1183 if (retval != ERROR_OK)
1184 return retval;
1185 else {
1186 int fd = semihosting_get_field(target, 0, fields);
1187 off_t pos = semihosting_get_field(target, 1, fields);
1188 if (semihosting->is_fileio) {
1189 semihosting->hit_fileio = true;
1190 fileio_info->identifier = "lseek";
1191 fileio_info->param_1 = fd;
1192 fileio_info->param_2 = pos;
1193 fileio_info->param_3 = SEEK_SET;
1194 } else {
1195 semihosting->result = lseek(fd, pos, SEEK_SET);
1196 semihosting->sys_errno = errno;
1197 LOG_DEBUG("lseek(%d, %d)=%d", fd, (int)pos,
1198 (int)semihosting->result);
1199 if (semihosting->result == pos)
1200 semihosting->result = 0;
1201 }
1202 }
1203 break;
1204
1205 case SEMIHOSTING_SYS_SYSTEM: /* 0x12 */
1206 /*
1207 * Passes a command to the host command-line interpreter.
1208 * This enables you to execute a system command such as dir,
1209 * ls, or pwd. The terminal I/O is on the host, and is not
1210 * visible to the target.
1211 *
1212 * Entry
1213 * On entry, the PARAMETER REGISTER contains a pointer to a
1214 * two-field argument block:
1215 * - field 1 Points to a string to be passed to the host
1216 * command-line interpreter.
1217 * - field 2 The length of the string.
1218 *
1219 * Return
1220 * On exit, the RETURN REGISTER contains the return status.
1221 */
1222
1223 /* Provide SYS_SYSTEM functionality. Uses the
1224 * libc system command, there may be a reason *NOT*
1225 * to use this, but as I can't think of one, I
1226 * implemented it this way.
1227 */
1228 retval = semihosting_read_fields(target, 2, fields);
1229 if (retval != ERROR_OK)
1230 return retval;
1231 else {
1232 uint64_t addr = semihosting_get_field(target, 0, fields);
1233 size_t len = semihosting_get_field(target, 1, fields);
1234 if (semihosting->is_fileio) {
1235 semihosting->hit_fileio = true;
1236 fileio_info->identifier = "system";
1237 fileio_info->param_1 = addr;
1238 fileio_info->param_2 = len;
1239 } else {
1240 uint8_t *cmd = malloc(len+1);
1241 if (!cmd) {
1242 semihosting->result = -1;
1243 semihosting->sys_errno = ENOMEM;
1244 } else {
1245 retval = target_read_memory(target,
1246 addr,
1247 1,
1248 len,
1249 cmd);
1250 if (retval != ERROR_OK) {
1251 free(cmd);
1252 return retval;
1253 } else {
1254 cmd[len] = 0;
1255 semihosting->result = system(
1256 (const char *)cmd);
1257 LOG_DEBUG("system('%s')=%d",
1258 cmd,
1259 (int)semihosting->result);
1260 }
1261
1262 free(cmd);
1263 }
1264 }
1265 }
1266 break;
1267
1268 case SEMIHOSTING_SYS_TIME: /* 0x11 */
1269 /*
1270 * Returns the number of seconds since 00:00 January 1, 1970.
1271 * This value is real-world time, regardless of any debug agent
1272 * configuration.
1273 *
1274 * Entry
1275 * There are no parameters.
1276 *
1277 * Return
1278 * On exit, the RETURN REGISTER contains the number of seconds.
1279 */
1280 semihosting->result = time(NULL);
1281 break;
1282
1283 case SEMIHOSTING_SYS_WRITE: /* 0x05 */
1284 /*
1285 * Writes the contents of a buffer to a specified file at the
1286 * current file position. The file position is specified either:
1287 * - Explicitly, by a SYS_SEEK.
1288 * - Implicitly as one byte beyond the previous SYS_READ or
1289 * SYS_WRITE request.
1290 *
1291 * The file position is at the start of the file when the file
1292 * is opened, and is lost when the file is closed.
1293 *
1294 * Perform the file operation as a single action whenever
1295 * possible. For example, do not split a write of 16KB into
1296 * four 4KB chunks unless there is no alternative.
1297 *
1298 * Entry
1299 * On entry, the PARAMETER REGISTER contains a pointer to a
1300 * three-field data block:
1301 * - field 1 Contains a handle for a file previously opened
1302 * with SYS_OPEN.
1303 * - field 2 Points to the memory containing the data to be written.
1304 * - field 3 Contains the number of bytes to be written from
1305 * the buffer to the file.
1306 *
1307 * Return
1308 * On exit, the RETURN REGISTER contains:
1309 * - 0 if the call is successful.
1310 * - The number of bytes that are not written, if there is an error.
1311 */
1312 retval = semihosting_read_fields(target, 3, fields);
1313 if (retval != ERROR_OK)
1314 return retval;
1315 else {
1316 int fd = semihosting_get_field(target, 0, fields);
1317 uint64_t addr = semihosting_get_field(target, 1, fields);
1318 size_t len = semihosting_get_field(target, 2, fields);
1319 if (semihosting->is_fileio) {
1320 semihosting->hit_fileio = true;
1321 fileio_info->identifier = "write";
1322 fileio_info->param_1 = fd;
1323 fileio_info->param_2 = addr;
1324 fileio_info->param_3 = len;
1325 } else {
1326 uint8_t *buf = malloc(len);
1327 if (!buf) {
1328 semihosting->result = -1;
1329 semihosting->sys_errno = ENOMEM;
1330 } else {
1331 retval = target_read_buffer(target, addr, len, buf);
1332 if (retval != ERROR_OK) {
1333 free(buf);
1334 return retval;
1335 }
1336 semihosting->result = semihosting_write(semihosting, fd, buf, len);
1337 semihosting->sys_errno = errno;
1338 LOG_DEBUG("write(%d, 0x%" PRIx64 ", %zu)=%d",
1339 fd,
1340 addr,
1341 len,
1342 (int)semihosting->result);
1343 if (semihosting->result >= 0) {
1344 /* The number of bytes that are NOT written.
1345 * */
1346 semihosting->result = len -
1347 semihosting->result;
1348 }
1349
1350 free(buf);
1351 }
1352 }
1353 }
1354 break;
1355
1356 case SEMIHOSTING_SYS_WRITEC: /* 0x03 */
1357 /*
1358 * Writes a character byte, pointed to by the PARAMETER REGISTER,
1359 * to the debug channel. When executed under a semihosting
1360 * debugger, the character appears on the host debugger console.
1361 *
1362 * Entry
1363 * On entry, the PARAMETER REGISTER contains a pointer to the
1364 * character.
1365 *
1366 * Return
1367 * None. The RETURN REGISTER is corrupted.
1368 */
1369 if (semihosting->is_fileio) {
1370 semihosting->hit_fileio = true;
1371 fileio_info->identifier = "write";
1372 fileio_info->param_1 = 1;
1373 fileio_info->param_2 = semihosting->param;
1374 fileio_info->param_3 = 1;
1375 } else {
1376 uint64_t addr = semihosting->param;
1377 unsigned char c;
1378 retval = target_read_memory(target, addr, 1, 1, &c);
1379 if (retval != ERROR_OK)
1380 return retval;
1381 semihosting_putchar(semihosting, semihosting->stdout_fd, c);
1382 semihosting->result = 0;
1383 }
1384 break;
1385
1386 case SEMIHOSTING_SYS_WRITE0: /* 0x04 */
1387 /*
1388 * Writes a null-terminated string to the debug channel.
1389 * When executed under a semihosting debugger, the characters
1390 * appear on the host debugger console.
1391 *
1392 * Entry
1393 * On entry, the PARAMETER REGISTER contains a pointer to the
1394 * first byte of the string.
1395 *
1396 * Return
1397 * None. The RETURN REGISTER is corrupted.
1398 */
1399 if (semihosting->is_fileio) {
1400 size_t count = 0;
1401 uint64_t addr = semihosting->param;
1402 for (;; addr++) {
1403 unsigned char c;
1404 retval = target_read_memory(target, addr, 1, 1, &c);
1405 if (retval != ERROR_OK)
1406 return retval;
1407 if (c == '\0')
1408 break;
1409 count++;
1410 }
1411 semihosting->hit_fileio = true;
1412 fileio_info->identifier = "write";
1413 fileio_info->param_1 = 1;
1414 fileio_info->param_2 = semihosting->param;
1415 fileio_info->param_3 = count;
1416 } else {
1417 uint64_t addr = semihosting->param;
1418 do {
1419 unsigned char c;
1420 retval = target_read_memory(target, addr++, 1, 1, &c);
1421 if (retval != ERROR_OK)
1422 return retval;
1423 if (!c)
1424 break;
1425 semihosting_putchar(semihosting, semihosting->stdout_fd, c);
1426 } while (1);
1427 semihosting->result = 0;
1428 }
1429 break;
1430
1431 case SEMIHOSTING_USER_CMD_0x100 ... SEMIHOSTING_USER_CMD_0x107:
1432 /**
1433 * This is a user defined operation (while user cmds 0x100-0x1ff
1434 * are possible, only 0x100-0x107 are currently implemented).
1435 *
1436 * Reads the user operation parameters from target, then fires the
1437 * corresponding target event. When the target callbacks returned,
1438 * cleans up the command parameter buffer.
1439 *
1440 * Entry
1441 * On entry, the PARAMETER REGISTER contains a pointer to a
1442 * two-field data block:
1443 * - field 1 Contains a pointer to the bound command parameter
1444 * string
1445 * - field 2 Contains the command parameter string length
1446 *
1447 * Return
1448 * On exit, the RETURN REGISTER contains the return status.
1449 */
1450 if (semihosting->user_command_extension) {
1451 retval = semihosting->user_command_extension(target);
1452 if (retval != ERROR_NOT_IMPLEMENTED)
1453 break;
1454 /* If custom user command not handled, we are looking for the TCL handler */
1455 }
1456
1457 assert(!semihosting_user_op_params);
1458 retval = semihosting_read_fields(target, 2, fields);
1459 if (retval != ERROR_OK) {
1460 LOG_ERROR("Failed to read fields for user defined command"
1461 " op=0x%x", semihosting->op);
1462 return retval;
1463 }
1464
1465 uint64_t addr = semihosting_get_field(target, 0, fields);
1466
1467 size_t len = semihosting_get_field(target, 1, fields);
1468 if (len > SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH) {
1469 LOG_ERROR("The maximum length for user defined command "
1470 "parameter is %u, received length is %zu (op=0x%x)",
1471 SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH,
1472 len,
1473 semihosting->op);
1474 return ERROR_FAIL;
1475 }
1476
1477 semihosting_user_op_params = malloc(len + 1);
1478 if (!semihosting_user_op_params)
1479 return ERROR_FAIL;
1480 semihosting_user_op_params[len] = 0;
1481
1482 retval = target_read_buffer(target, addr, len,
1483 (uint8_t *)(semihosting_user_op_params));
1484 if (retval != ERROR_OK) {
1485 LOG_ERROR("Failed to read from target, semihosting op=0x%x",
1486 semihosting->op);
1487 free(semihosting_user_op_params);
1488 semihosting_user_op_params = NULL;
1489 return retval;
1490 }
1491
1492 target_handle_event(target, semihosting->op);
1493 free(semihosting_user_op_params);
1494 semihosting_user_op_params = NULL;
1495 semihosting->result = 0;
1496 break;
1497
1498 case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */
1499 /*
1500 * Returns the number of elapsed target ticks since execution
1501 * started.
1502 * Use SYS_TICKFREQ to determine the tick frequency.
1503 *
1504 * Entry (32-bit)
1505 * On entry, the PARAMETER REGISTER points to a two-field data
1506 * block to be used for returning the number of elapsed ticks:
1507 * - field 1 The least significant field and is at the low address.
1508 * - field 2 The most significant field and is at the high address.
1509 *
1510 * Entry (64-bit)
1511 * On entry the PARAMETER REGISTER points to a one-field data
1512 * block to be used for returning the number of elapsed ticks:
1513 * - field 1 The number of elapsed ticks as a 64-bit value.
1514 *
1515 * Return
1516 * On exit:
1517 * - On success, the RETURN REGISTER contains 0, the PARAMETER
1518 * REGISTER is unchanged, and the data block pointed to by the
1519 * PARAMETER REGISTER is filled in with the number of elapsed
1520 * ticks.
1521 * - On failure, the RETURN REGISTER contains -1, and the
1522 * PARAMETER REGISTER contains -1.
1523 *
1524 * Note: Some semihosting implementations might not support this
1525 * semihosting operation, and they always return -1 in the
1526 * RETURN REGISTER.
1527 */
1528
1529 case SEMIHOSTING_SYS_TICKFREQ: /* 0x31 */
1530 /*
1531 * Returns the tick frequency.
1532 *
1533 * Entry
1534 * The PARAMETER REGISTER must contain 0 on entry to this routine.
1535 *
1536 * Return
1537 * On exit, the RETURN REGISTER contains either:
1538 * - The number of ticks per second.
1539 * - –1 if the target does not know the value of one tick.
1540 *
1541 * Note: Some semihosting implementations might not support
1542 * this semihosting operation, and they always return -1 in the
1543 * RETURN REGISTER.
1544 */
1545
1546 case SEMIHOSTING_SYS_TMPNAM: /* 0x0D */
1547 /*
1548 * Returns a temporary name for a file identified by a system
1549 * file identifier.
1550 *
1551 * Entry
1552 * On entry, the PARAMETER REGISTER contains a pointer to a
1553 * three-word argument block:
1554 * - field 1 A pointer to a buffer.
1555 * - field 2 A target identifier for this filename. Its value
1556 * must be an integer in the range 0-255.
1557 * - field 3 Contains the length of the buffer. The length must
1558 * be at least the value of L_tmpnam on the host system.
1559 *
1560 * Return
1561 * On exit, the RETURN REGISTER contains:
1562 * - 0 if the call is successful.
1563 * - –1 if an error occurs.
1564 *
1565 * The buffer pointed to by the PARAMETER REGISTER contains
1566 * the filename, prefixed with a suitable directory name.
1567 * If you use the same target identifier again, the same
1568 * filename is returned.
1569 *
1570 * Note: The returned string must be null-terminated.
1571 */
1572
1573 default:
1574 fprintf(stderr, "semihosting: unsupported call %#x\n",
1575 (unsigned) semihosting->op);
1576 semihosting->result = -1;
1577 semihosting->sys_errno = ENOTSUP;
1578 }
1579
1580 if (!semihosting->hit_fileio) {
1581 retval = semihosting->post_result(target);
1582 if (retval != ERROR_OK) {
1583 LOG_ERROR("Failed to post semihosting result");
1584 return retval;
1585 }
1586 }
1587
1588 return ERROR_OK;
1589 }
1590
1591 /* -------------------------------------------------------------------------
1592 * Local functions. */
1593
1594 static int semihosting_common_fileio_info(struct target *target,
1595 struct gdb_fileio_info *fileio_info)
1596 {
1597 struct semihosting *semihosting = target->semihosting;
1598 if (!semihosting)
1599 return ERROR_FAIL;
1600
1601 /*
1602 * To avoid unnecessary duplication, semihosting prepares the
1603 * fileio_info structure out-of-band when the target halts. See
1604 * do_semihosting for more detail.
1605 */
1606 if (!semihosting->is_fileio || !semihosting->hit_fileio)
1607 return ERROR_FAIL;
1608
1609 return ERROR_OK;
1610 }
1611
1612 static int semihosting_common_fileio_end(struct target *target, int result,
1613 int fileio_errno, bool ctrl_c)
1614 {
1615 struct gdb_fileio_info *fileio_info = target->fileio_info;
1616 struct semihosting *semihosting = target->semihosting;
1617 if (!semihosting)
1618 return ERROR_FAIL;
1619
1620 /* clear pending status */
1621 semihosting->hit_fileio = false;
1622
1623 semihosting->result = result;
1624 semihosting->sys_errno = fileio_errno;
1625
1626 /*
1627 * Some fileio results do not match up with what the semihosting
1628 * operation expects; for these operations, we munge the results
1629 * below:
1630 */
1631 switch (semihosting->op) {
1632 case SEMIHOSTING_SYS_WRITE: /* 0x05 */
1633 case SEMIHOSTING_SYS_READ: /* 0x06 */
1634 if (result < 0)
1635 semihosting->result = fileio_info->param_3; /* Zero bytes read/written. */
1636 else
1637 semihosting->result = (int64_t)fileio_info->param_3 - result;
1638 break;
1639
1640 case SEMIHOSTING_SYS_SEEK: /* 0x0a */
1641 if (result > 0)
1642 semihosting->result = 0;
1643 break;
1644 }
1645
1646 return semihosting->post_result(target);
1647 }
1648
1649 /* -------------------------------------------------------------------------
1650 * Utility functions. */
1651
1652 /**
1653 * Read all fields of a command from target to buffer.
1654 */
1655 int semihosting_read_fields(struct target *target, size_t number,
1656 uint8_t *fields)
1657 {
1658 struct semihosting *semihosting = target->semihosting;
1659 /* Use 4-byte multiples to trigger fast memory access. */
1660 return target_read_memory(target, semihosting->param, 4,
1661 number * (semihosting->word_size_bytes / 4), fields);
1662 }
1663
1664 /**
1665 * Write all fields of a command from buffer to target.
1666 */
1667 int semihosting_write_fields(struct target *target, size_t number,
1668 uint8_t *fields)
1669 {
1670 struct semihosting *semihosting = target->semihosting;
1671 /* Use 4-byte multiples to trigger fast memory access. */
1672 return target_write_memory(target, semihosting->param, 4,
1673 number * (semihosting->word_size_bytes / 4), fields);
1674 }
1675
1676 /**
1677 * Extract a field from the buffer, considering register size and endianness.
1678 */
1679 uint64_t semihosting_get_field(struct target *target, size_t index,
1680 uint8_t *fields)
1681 {
1682 struct semihosting *semihosting = target->semihosting;
1683 if (semihosting->word_size_bytes == 8)
1684 return target_buffer_get_u64(target, fields + (index * 8));
1685 else
1686 return target_buffer_get_u32(target, fields + (index * 4));
1687 }
1688
1689 /**
1690 * Store a field in the buffer, considering register size and endianness.
1691 */
1692 void semihosting_set_field(struct target *target, uint64_t value,
1693 size_t index,
1694 uint8_t *fields)
1695 {
1696 struct semihosting *semihosting = target->semihosting;
1697 if (semihosting->word_size_bytes == 8)
1698 target_buffer_set_u64(target, fields + (index * 8), value);
1699 else
1700 target_buffer_set_u32(target, fields + (index * 4), value);
1701 }
1702
1703 /* -------------------------------------------------------------------------
1704 * Semihosting redirect over TCP structs and functions */
1705
1706 static int semihosting_service_new_connection_handler(struct connection *connection)
1707 {
1708 struct semihosting_tcp_service *service = connection->service->priv;
1709 service->semihosting->tcp_connection = connection;
1710
1711 return ERROR_OK;
1712 }
1713
1714 static int semihosting_service_input_handler(struct connection *connection)
1715 {
1716 struct semihosting_tcp_service *service = connection->service->priv;
1717
1718 if (!connection->input_pending) {
1719 /* consume received data, not for semihosting IO */
1720 const int buf_len = 100;
1721 char buf[buf_len];
1722 int bytes_read = connection_read(connection, buf, buf_len);
1723
1724 if (bytes_read == 0) {
1725 return ERROR_SERVER_REMOTE_CLOSED;
1726 } else if (bytes_read == -1) {
1727 LOG_ERROR("error during read: %s", strerror(errno));
1728 return ERROR_SERVER_REMOTE_CLOSED;
1729 }
1730 } else if (service->error != ERROR_OK) {
1731 return ERROR_SERVER_REMOTE_CLOSED;
1732 }
1733
1734 return ERROR_OK;
1735 }
1736
1737 static int semihosting_service_connection_closed_handler(struct connection *connection)
1738 {
1739 struct semihosting_tcp_service *service = connection->service->priv;
1740 if (service) {
1741 free(service->name);
1742 free(service);
1743 }
1744
1745 return ERROR_OK;
1746 }
1747
1748 static void semihosting_tcp_close_cnx(struct semihosting *semihosting)
1749 {
1750 if (!semihosting->tcp_connection)
1751 return;
1752
1753 struct service *service = semihosting->tcp_connection->service;
1754 remove_service(service->name, service->port);
1755 semihosting->tcp_connection = NULL;
1756
1757 }
1758
1759 static const struct service_driver semihosting_service_driver = {
1760 .name = "semihosting",
1761 .new_connection_during_keep_alive_handler = NULL,
1762 .new_connection_handler = semihosting_service_new_connection_handler,
1763 .input_handler = semihosting_service_input_handler,
1764 .connection_closed_handler = semihosting_service_connection_closed_handler,
1765 .keep_client_alive_handler = NULL,
1766 };
1767
1768 /* -------------------------------------------------------------------------
1769 * Common semihosting commands handlers. */
1770
1771 COMMAND_HANDLER(handle_common_semihosting_command)
1772 {
1773 struct target *target = get_current_target(CMD_CTX);
1774
1775 if (!target) {
1776 LOG_ERROR("No target selected");
1777 return ERROR_FAIL;
1778 }
1779
1780 struct semihosting *semihosting = target->semihosting;
1781 if (!semihosting) {
1782 command_print(CMD, "semihosting not supported for current target");
1783 return ERROR_FAIL;
1784 }
1785
1786 if (CMD_ARGC > 0) {
1787 int is_active;
1788
1789 COMMAND_PARSE_ENABLE(CMD_ARGV[0], is_active);
1790
1791 if (!target_was_examined(target)) {
1792 LOG_ERROR("Target not examined yet");
1793 return ERROR_FAIL;
1794 }
1795
1796 if (semihosting && semihosting->setup(target, is_active) != ERROR_OK) {
1797 LOG_ERROR("Failed to Configure semihosting");
1798 return ERROR_FAIL;
1799 }
1800
1801 /* FIXME never let that "catch" be dropped! (???) */
1802 semihosting->is_active = is_active;
1803 }
1804
1805 command_print(CMD, "semihosting is %s",
1806 semihosting->is_active
1807 ? "enabled" : "disabled");
1808
1809 return ERROR_OK;
1810 }
1811
1812 COMMAND_HANDLER(handle_common_semihosting_redirect_command)
1813 {
1814 struct target *target = get_current_target(CMD_CTX);
1815
1816 if (!target) {
1817 LOG_ERROR("No target selected");
1818 return ERROR_FAIL;
1819 }
1820
1821 struct semihosting *semihosting = target->semihosting;
1822 if (!semihosting) {
1823 command_print(CMD, "semihosting not supported for current target");
1824 return ERROR_FAIL;
1825 }
1826
1827 if (!semihosting->is_active) {
1828 command_print(CMD, "semihosting not yet enabled for current target");
1829 return ERROR_FAIL;
1830 }
1831
1832 enum semihosting_redirect_config cfg;
1833 const char *port;
1834
1835 if (CMD_ARGC < 1)
1836 return ERROR_COMMAND_SYNTAX_ERROR;
1837
1838 if (strcmp(CMD_ARGV[0], "disable") == 0) {
1839 cfg = SEMIHOSTING_REDIRECT_CFG_NONE;
1840 if (CMD_ARGC > 1)
1841 return ERROR_COMMAND_SYNTAX_ERROR;
1842 } else if (strcmp(CMD_ARGV[0], "tcp") == 0) {
1843 if (CMD_ARGC < 2 || CMD_ARGC > 3)
1844 return ERROR_COMMAND_SYNTAX_ERROR;
1845
1846 port = CMD_ARGV[1];
1847
1848 cfg = SEMIHOSTING_REDIRECT_CFG_ALL;
1849 if (CMD_ARGC == 3) {
1850 if (strcmp(CMD_ARGV[2], "debug") == 0)
1851 cfg = SEMIHOSTING_REDIRECT_CFG_DEBUG;
1852 else if (strcmp(CMD_ARGV[2], "stdio") == 0)
1853 cfg = SEMIHOSTING_REDIRECT_CFG_STDIO;
1854 else if (strcmp(CMD_ARGV[2], "all") != 0)
1855 return ERROR_COMMAND_SYNTAX_ERROR;
1856 }
1857 } else {
1858 return ERROR_COMMAND_SYNTAX_ERROR;
1859 }
1860
1861 semihosting_tcp_close_cnx(semihosting);
1862 semihosting->redirect_cfg = SEMIHOSTING_REDIRECT_CFG_NONE;
1863
1864 if (cfg != SEMIHOSTING_REDIRECT_CFG_NONE) {
1865 struct semihosting_tcp_service *service =
1866 calloc(1, sizeof(struct semihosting_tcp_service));
1867 if (!service) {
1868 LOG_ERROR("Failed to allocate semihosting TCP service.");
1869 return ERROR_FAIL;
1870 }
1871
1872 service->semihosting = semihosting;
1873
1874 service->name = alloc_printf("%s semihosting service", target_name(target));
1875 if (!service->name) {
1876 LOG_ERROR("Out of memory");
1877 free(service);
1878 return ERROR_FAIL;
1879 }
1880
1881 int ret = add_service(&semihosting_service_driver,
1882 port, 1, service);
1883
1884 if (ret != ERROR_OK) {
1885 LOG_ERROR("failed to initialize %s", service->name);
1886 free(service->name);
1887 free(service);
1888 return ERROR_FAIL;
1889 }
1890 }
1891
1892 semihosting->redirect_cfg = cfg;
1893
1894 return ERROR_OK;
1895 }
1896
1897 COMMAND_HANDLER(handle_common_semihosting_fileio_command)
1898 {
1899 struct target *target = get_current_target(CMD_CTX);
1900
1901 if (!target) {
1902 LOG_ERROR("No target selected");
1903 return ERROR_FAIL;
1904 }
1905
1906 struct semihosting *semihosting = target->semihosting;
1907 if (!semihosting) {
1908 command_print(CMD, "semihosting not supported for current target");
1909 return ERROR_FAIL;
1910 }
1911
1912 if (!semihosting->is_active) {
1913 command_print(CMD, "semihosting not yet enabled for current target");
1914 return ERROR_FAIL;
1915 }
1916
1917 if (CMD_ARGC > 0)
1918 COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->is_fileio);
1919
1920 command_print(CMD, "semihosting fileio is %s",
1921 semihosting->is_fileio
1922 ? "enabled" : "disabled");
1923
1924 return ERROR_OK;
1925 }
1926
1927 COMMAND_HANDLER(handle_common_semihosting_cmdline)
1928 {
1929 struct target *target = get_current_target(CMD_CTX);
1930 unsigned int i;
1931
1932 if (!target) {
1933 LOG_ERROR("No target selected");
1934 return ERROR_FAIL;
1935 }
1936
1937 struct semihosting *semihosting = target->semihosting;
1938 if (!semihosting) {
1939 command_print(CMD, "semihosting not supported for current target");
1940 return ERROR_FAIL;
1941 }
1942
1943 free(semihosting->cmdline);
1944 semihosting->cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
1945
1946 for (i = 1; i < CMD_ARGC; i++) {
1947 char *cmdline = alloc_printf("%s %s", semihosting->cmdline, CMD_ARGV[i]);
1948 if (!cmdline)
1949 break;
1950 free(semihosting->cmdline);
1951 semihosting->cmdline = cmdline;
1952 }
1953
1954 command_print(CMD, "semihosting command line is [%s]",
1955 semihosting->cmdline);
1956
1957 return ERROR_OK;
1958 }
1959
1960 COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
1961 {
1962 struct target *target = get_current_target(CMD_CTX);
1963
1964 if (!target) {
1965 LOG_ERROR("No target selected");
1966 return ERROR_FAIL;
1967 }
1968
1969 struct semihosting *semihosting = target->semihosting;
1970 if (!semihosting) {
1971 command_print(CMD, "semihosting not supported for current target");
1972 return ERROR_FAIL;
1973 }
1974
1975 if (!semihosting->is_active) {
1976 command_print(CMD, "semihosting not yet enabled for current target");
1977 return ERROR_FAIL;
1978 }
1979
1980 if (CMD_ARGC > 0)
1981 COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->has_resumable_exit);
1982
1983 command_print(CMD, "semihosting resumable exit is %s",
1984 semihosting->has_resumable_exit
1985 ? "enabled" : "disabled");
1986
1987 return ERROR_OK;
1988 }
1989
1990 COMMAND_HANDLER(handle_common_semihosting_read_user_param_command)
1991 {
1992 struct target *target = get_current_target(CMD_CTX);
1993 struct semihosting *semihosting = target->semihosting;
1994
1995 if (CMD_ARGC)
1996 return ERROR_COMMAND_SYNTAX_ERROR;
1997
1998 if (!semihosting->is_active) {
1999 LOG_ERROR("semihosting not yet enabled for current target");
2000 return ERROR_FAIL;
2001 }
2002
2003 if (!semihosting_user_op_params) {
2004 LOG_ERROR("This command is usable only from a registered user "
2005 "semihosting event callback.");
2006 return ERROR_FAIL;
2007 }
2008
2009 command_print_sameline(CMD, "%s", semihosting_user_op_params);
2010
2011 return ERROR_OK;
2012 }
2013
2014 COMMAND_HANDLER(handle_common_semihosting_basedir_command)
2015 {
2016 struct target *target = get_current_target(CMD_CTX);
2017
2018 if (CMD_ARGC > 1)
2019 return ERROR_COMMAND_SYNTAX_ERROR;
2020
2021 if (!target) {
2022 LOG_ERROR("No target selected");
2023 return ERROR_FAIL;
2024 }
2025
2026 struct semihosting *semihosting = target->semihosting;
2027 if (!semihosting) {
2028 command_print(CMD, "semihosting not supported for current target");
2029 return ERROR_FAIL;
2030 }
2031
2032 if (!semihosting->is_active) {
2033 command_print(CMD, "semihosting not yet enabled for current target");
2034 return ERROR_FAIL;
2035 }
2036
2037 if (CMD_ARGC > 0) {
2038 free(semihosting->basedir);
2039 semihosting->basedir = strdup(CMD_ARGV[0]);
2040 if (!semihosting->basedir) {
2041 command_print(CMD, "semihosting failed to allocate memory for basedir!");
2042 return ERROR_FAIL;
2043 }
2044 }
2045
2046 command_print(CMD, "semihosting base dir: %s",
2047 semihosting->basedir ? semihosting->basedir : "");
2048
2049 return ERROR_OK;
2050 }
2051
2052 const struct command_registration semihosting_common_handlers[] = {
2053 {
2054 .name = "semihosting",
2055 .handler = handle_common_semihosting_command,
2056 .mode = COMMAND_EXEC,
2057 .usage = "['enable'|'disable']",
2058 .help = "activate support for semihosting operations",
2059 },
2060 {
2061 .name = "semihosting_redirect",
2062 .handler = handle_common_semihosting_redirect_command,
2063 .mode = COMMAND_EXEC,
2064 .usage = "(disable | tcp <port> ['debug'|'stdio'|'all'])",
2065 .help = "redirect semihosting IO",
2066 },
2067 {
2068 .name = "semihosting_cmdline",
2069 .handler = handle_common_semihosting_cmdline,
2070 .mode = COMMAND_EXEC,
2071 .usage = "arguments",
2072 .help = "command line arguments to be passed to program",
2073 },
2074 {
2075 .name = "semihosting_fileio",
2076 .handler = handle_common_semihosting_fileio_command,
2077 .mode = COMMAND_EXEC,
2078 .usage = "['enable'|'disable']",
2079 .help = "activate support for semihosting fileio operations",
2080 },
2081 {
2082 .name = "semihosting_resexit",
2083 .handler = handle_common_semihosting_resumable_exit_command,
2084 .mode = COMMAND_EXEC,
2085 .usage = "['enable'|'disable']",
2086 .help = "activate support for semihosting resumable exit",
2087 },
2088 {
2089 .name = "semihosting_read_user_param",
2090 .handler = handle_common_semihosting_read_user_param_command,
2091 .mode = COMMAND_EXEC,
2092 .usage = "",
2093 .help = "read parameters in semihosting-user-cmd-0x10X callbacks",
2094 },
2095 {
2096 .name = "semihosting_basedir",
2097 .handler = handle_common_semihosting_basedir_command,
2098 .mode = COMMAND_EXEC,
2099 .usage = "[dir]",
2100 .help = "set the base directory for semihosting I/O operations",
2101 },
2102 COMMAND_REGISTRATION_DONE
2103 };

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)