target/semihosting: drop type casting from semihosting->result
[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)=%" PRId64, fd, 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)=%" PRId64, fd, semihosting->result);
635 break;
636 }
637 LOG_DEBUG("fstat(%d)=%" PRId64, fd, 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], %" PRId64, arg, semihosting->result);
695 }
696 break;
697
698 case SEMIHOSTING_SYS_HEAPINFO: /* 0x16 */
699 /*
700 * Returns the system stack and heap parameters.
701 *
702 * Entry
703 * On entry, the PARAMETER REGISTER contains the address of a
704 * pointer to a four-field data block. The contents of the data
705 * block are filled by the function. The following C-like
706 * pseudocode describes the layout of the block:
707 * struct block {
708 * void* heap_base;
709 * void* heap_limit;
710 * void* stack_base;
711 * void* stack_limit;
712 * };
713 *
714 * Return
715 * On exit, the PARAMETER REGISTER is unchanged and the data
716 * block has been updated.
717 */
718 retval = semihosting_read_fields(target, 1, fields);
719 if (retval != ERROR_OK)
720 return retval;
721 else {
722 uint64_t addr = semihosting_get_field(target, 0, fields);
723 /* tell the remote we have no idea */
724 memset(fields, 0, 4 * semihosting->word_size_bytes);
725 retval = target_write_memory(target, addr, 4,
726 semihosting->word_size_bytes,
727 fields);
728 if (retval != ERROR_OK)
729 return retval;
730 semihosting->result = 0;
731 }
732 break;
733
734 case SEMIHOSTING_SYS_ISERROR: /* 0x08 */
735 /*
736 * Determines whether the return code from another semihosting
737 * call is an error status or not.
738 *
739 * This call is passed a parameter block containing the error
740 * code to examine.
741 *
742 * Entry
743 * On entry, the PARAMETER REGISTER contains a pointer to a
744 * one-field data block:
745 * - field 1 The required status word to check.
746 *
747 * Return
748 * On exit, the RETURN REGISTER contains:
749 * - 0 if the status field is not an error indication
750 * - A nonzero value if the status field is an error indication.
751 */
752 retval = semihosting_read_fields(target, 1, fields);
753 if (retval != ERROR_OK)
754 return retval;
755
756 uint64_t code = semihosting_get_field(target, 0, fields);
757 semihosting->result = (code != 0);
758 break;
759
760 case SEMIHOSTING_SYS_ISTTY: /* 0x09 */
761 /*
762 * Checks whether a file is connected to an interactive device.
763 *
764 * Entry
765 * On entry, the PARAMETER REGISTER contains a pointer to a
766 * one-field argument block:
767 * field 1 A handle for a previously opened file object.
768 *
769 * Return
770 * On exit, the RETURN REGISTER contains:
771 * - 1 if the handle identifies an interactive device.
772 * - 0 if the handle identifies a file.
773 * - A value other than 1 or 0 if an error occurs.
774 */
775 if (semihosting->is_fileio) {
776 semihosting->hit_fileio = true;
777 fileio_info->identifier = "isatty";
778 fileio_info->param_1 = semihosting->param;
779 } else {
780 retval = semihosting_read_fields(target, 1, fields);
781 if (retval != ERROR_OK)
782 return retval;
783 int fd = semihosting_get_field(target, 0, fields);
784 semihosting->result = isatty(fd);
785 semihosting->sys_errno = errno;
786 LOG_DEBUG("isatty(%d)=%" PRId64, fd, semihosting->result);
787 }
788 break;
789
790 case SEMIHOSTING_SYS_OPEN: /* 0x01 */
791 /*
792 * Opens a file on the host system.
793 *
794 * The file path is specified either as relative to the current
795 * directory of the host process, or absolute, using the path
796 * conventions of the host operating system.
797 *
798 * Semihosting implementations must support opening the special
799 * path name :semihosting-features as part of the semihosting
800 * extensions reporting mechanism.
801 *
802 * ARM targets interpret the special path name :tt as meaning
803 * the console input stream, for an open-read or the console
804 * output stream, for an open-write. Opening these streams is
805 * performed as part of the standard startup code for those
806 * applications that reference the C stdio streams. The
807 * semihosting extension SH_EXT_STDOUT_STDERR allows the
808 * semihosting caller to open separate output streams
809 * corresponding to stdout and stderr. This extension is
810 * reported using feature byte 0, bit 1. Use SYS_OPEN with
811 * the special path name :semihosting-features to access the
812 * feature bits.
813 *
814 * If this extension is supported, the implementation must
815 * support the following additional semantics to SYS_OPEN:
816 * - If the special path name :tt is opened with an fopen
817 * mode requesting write access (w, wb, w+, or w+b), then
818 * this is a request to open stdout.
819 * - If the special path name :tt is opened with a mode
820 * requesting append access (a, ab, a+, or a+b), then this is
821 * a request to open stderr.
822 *
823 * Entry
824 * On entry, the PARAMETER REGISTER contains a pointer to a
825 * three-field argument block:
826 * - field 1 A pointer to a null-terminated string containing
827 * a file or device name.
828 * - field 2 An integer that specifies the file opening mode.
829 * - field 3 An integer that gives the length of the string
830 * pointed to by field 1.
831 *
832 * The length does not include the terminating null character
833 * that must be present.
834 *
835 * Return
836 * On exit, the RETURN REGISTER contains:
837 * - A nonzero handle if the call is successful.
838 * - –1 if the call is not successful.
839 */
840 retval = semihosting_read_fields(target, 3, fields);
841 if (retval != ERROR_OK)
842 return retval;
843 else {
844 uint64_t addr = semihosting_get_field(target, 0, fields);
845 uint32_t mode = semihosting_get_field(target, 1, fields);
846 size_t len = semihosting_get_field(target, 2, fields);
847
848 if (mode > 11) {
849 semihosting->result = -1;
850 semihosting->sys_errno = EINVAL;
851 break;
852 }
853 size_t basedir_len = semihosting->basedir ? strlen(semihosting->basedir) : 0;
854 uint8_t *fn = malloc(basedir_len + len + 2);
855 if (!fn) {
856 semihosting->result = -1;
857 semihosting->sys_errno = ENOMEM;
858 } else {
859 if (basedir_len > 0) {
860 strcpy((char *)fn, semihosting->basedir);
861 if (fn[basedir_len - 1] != '/')
862 fn[basedir_len++] = '/';
863 }
864 retval = target_read_memory(target, addr, 1, len, fn + basedir_len);
865 if (retval != ERROR_OK) {
866 free(fn);
867 return retval;
868 }
869 fn[basedir_len + len] = 0;
870 /* TODO: implement the :semihosting-features special file.
871 * */
872 if (semihosting->is_fileio) {
873 if (strcmp((char *)fn, ":semihosting-features") == 0) {
874 semihosting->result = -1;
875 semihosting->sys_errno = EINVAL;
876 } else if (strcmp((char *)fn, ":tt") == 0) {
877 if (mode == 0)
878 semihosting->result = 0;
879 else if (mode == 4)
880 semihosting->result = 1;
881 else if (mode == 8)
882 semihosting->result = 2;
883 else
884 semihosting->result = -1;
885 } else {
886 semihosting->hit_fileio = true;
887 fileio_info->identifier = "open";
888 fileio_info->param_1 = addr;
889 fileio_info->param_2 = len;
890 fileio_info->param_3 = open_gdb_modeflags[mode];
891 fileio_info->param_4 = 0644;
892 }
893 } else {
894 if (strcmp((char *)fn, ":tt") == 0) {
895 /* Mode is:
896 * - 0-3 ("r") for stdin,
897 * - 4-7 ("w") for stdout,
898 * - 8-11 ("a") for stderr */
899 if (mode < 4) {
900 int fd = dup(STDIN_FILENO);
901 semihosting->result = fd;
902 semihosting->stdin_fd = fd;
903 semihosting->sys_errno = errno;
904 LOG_DEBUG("dup(STDIN)=%" PRId64, semihosting->result);
905 } else if (mode < 8) {
906 int fd = dup(STDOUT_FILENO);
907 semihosting->result = fd;
908 semihosting->stdout_fd = fd;
909 semihosting->sys_errno = errno;
910 LOG_DEBUG("dup(STDOUT)=%" PRId64, semihosting->result);
911 } else {
912 int fd = dup(STDERR_FILENO);
913 semihosting->result = fd;
914 semihosting->stderr_fd = fd;
915 semihosting->sys_errno = errno;
916 LOG_DEBUG("dup(STDERR)=%" PRId64, semihosting->result);
917 }
918 } else {
919 /* cygwin requires the permission setting
920 * otherwise it will fail to reopen a previously
921 * written file */
922 semihosting->result = open((char *)fn,
923 open_host_modeflags[mode],
924 0644);
925 semihosting->sys_errno = errno;
926 LOG_DEBUG("open('%s')=%" PRId64, fn, semihosting->result);
927 }
928 }
929 free(fn);
930 }
931 }
932 break;
933
934 case SEMIHOSTING_SYS_READ: /* 0x06 */
935 /*
936 * Reads the contents of a file into a buffer. The file position
937 * is specified either:
938 * - Explicitly by a SYS_SEEK.
939 * - Implicitly one byte beyond the previous SYS_READ or
940 * SYS_WRITE request.
941 *
942 * The file position is at the start of the file when it is
943 * opened, and is lost when the file is closed. Perform the
944 * file operation as a single action whenever possible. For
945 * example, do not split a read of 16KB into four 4KB chunks
946 * unless there is no alternative.
947 *
948 * Entry
949 * On entry, the PARAMETER REGISTER contains a pointer to a
950 * three-field data block:
951 * - field 1 Contains a handle for a file previously opened
952 * with SYS_OPEN.
953 * - field 2 Points to a buffer.
954 * - field 3 Contains the number of bytes to read to the buffer
955 * from the file.
956 *
957 * Return
958 * On exit, the RETURN REGISTER contains the number of bytes not
959 * filled in the buffer (buffer_length - bytes_read) as follows:
960 * - If the RETURN REGISTER is 0, the entire buffer was
961 * successfully filled.
962 * - If the RETURN REGISTER is the same as field 3, no bytes
963 * were read (EOF can be assumed).
964 * - If the RETURN REGISTER contains a value smaller than
965 * field 3, the read succeeded but the buffer was only partly
966 * filled. For interactive devices, this is the most common
967 * return value.
968 */
969 retval = semihosting_read_fields(target, 3, fields);
970 if (retval != ERROR_OK)
971 return retval;
972 else {
973 int fd = semihosting_get_field(target, 0, fields);
974 uint64_t addr = semihosting_get_field(target, 1, fields);
975 size_t len = semihosting_get_field(target, 2, fields);
976 if (semihosting->is_fileio) {
977 semihosting->hit_fileio = true;
978 fileio_info->identifier = "read";
979 fileio_info->param_1 = fd;
980 fileio_info->param_2 = addr;
981 fileio_info->param_3 = len;
982 } else {
983 uint8_t *buf = malloc(len);
984 if (!buf) {
985 semihosting->result = -1;
986 semihosting->sys_errno = ENOMEM;
987 } else {
988 semihosting->result = semihosting_read(semihosting, fd, buf, len);
989 LOG_DEBUG("read(%d, 0x%" PRIx64 ", %zu)=%" PRId64,
990 fd,
991 addr,
992 len,
993 semihosting->result);
994 if (semihosting->result >= 0) {
995 retval = target_write_buffer(target, addr,
996 semihosting->result,
997 buf);
998 if (retval != ERROR_OK) {
999 free(buf);
1000 return retval;
1001 }
1002 /* the number of bytes NOT filled in */
1003 semihosting->result = len -
1004 semihosting->result;
1005 }
1006 free(buf);
1007 }
1008 }
1009 }
1010 break;
1011
1012 case SEMIHOSTING_SYS_READC: /* 0x07 */
1013 /*
1014 * Reads a byte from the console.
1015 *
1016 * Entry
1017 * The PARAMETER REGISTER must contain 0. There are no other
1018 * parameters or values possible.
1019 *
1020 * Return
1021 * On exit, the RETURN REGISTER contains the byte read from
1022 * the console.
1023 */
1024 if (semihosting->is_fileio) {
1025 LOG_ERROR("SYS_READC not supported by semihosting fileio");
1026 return ERROR_FAIL;
1027 }
1028 semihosting->result = semihosting_getchar(semihosting, semihosting->stdin_fd);
1029 LOG_DEBUG("getchar()=%" PRId64, semihosting->result);
1030 break;
1031
1032 case SEMIHOSTING_SYS_REMOVE: /* 0x0E */
1033 /*
1034 * Deletes a specified file on the host filing system.
1035 *
1036 * Entry
1037 * On entry, the PARAMETER REGISTER contains a pointer to a
1038 * two-field argument block:
1039 * - field 1 Points to a null-terminated string that gives the
1040 * path name of the file to be deleted.
1041 * - field 2 The length of the string.
1042 *
1043 * Return
1044 * On exit, the RETURN REGISTER contains:
1045 * - 0 if the delete is successful
1046 * - A nonzero, host-specific error code if the delete fails.
1047 */
1048 retval = semihosting_read_fields(target, 2, fields);
1049 if (retval != ERROR_OK)
1050 return retval;
1051 else {
1052 uint64_t addr = semihosting_get_field(target, 0, fields);
1053 size_t len = semihosting_get_field(target, 1, fields);
1054 if (semihosting->is_fileio) {
1055 semihosting->hit_fileio = true;
1056 fileio_info->identifier = "unlink";
1057 fileio_info->param_1 = addr;
1058 fileio_info->param_2 = len;
1059 } else {
1060 uint8_t *fn = malloc(len+1);
1061 if (!fn) {
1062 semihosting->result = -1;
1063 semihosting->sys_errno = ENOMEM;
1064 } else {
1065 retval =
1066 target_read_memory(target, addr, 1, len,
1067 fn);
1068 if (retval != ERROR_OK) {
1069 free(fn);
1070 return retval;
1071 }
1072 fn[len] = 0;
1073 semihosting->result = remove((char *)fn);
1074 semihosting->sys_errno = errno;
1075 LOG_DEBUG("remove('%s')=%" PRId64, fn, semihosting->result);
1076
1077 free(fn);
1078 }
1079 }
1080 }
1081 break;
1082
1083 case SEMIHOSTING_SYS_RENAME: /* 0x0F */
1084 /*
1085 * Renames a specified file.
1086 *
1087 * Entry
1088 * On entry, the PARAMETER REGISTER contains a pointer to a
1089 * four-field data block:
1090 * - field 1 A pointer to the name of the old file.
1091 * - field 2 The length of the old filename.
1092 * - field 3 A pointer to the new filename.
1093 * - field 4 The length of the new filename. Both strings are
1094 * null-terminated.
1095 *
1096 * Return
1097 * On exit, the RETURN REGISTER contains:
1098 * - 0 if the rename is successful.
1099 * - A nonzero, host-specific error code if the rename fails.
1100 */
1101 retval = semihosting_read_fields(target, 4, fields);
1102 if (retval != ERROR_OK)
1103 return retval;
1104 else {
1105 uint64_t addr1 = semihosting_get_field(target, 0, fields);
1106 size_t len1 = semihosting_get_field(target, 1, fields);
1107 uint64_t addr2 = semihosting_get_field(target, 2, fields);
1108 size_t len2 = semihosting_get_field(target, 3, fields);
1109 if (semihosting->is_fileio) {
1110 semihosting->hit_fileio = true;
1111 fileio_info->identifier = "rename";
1112 fileio_info->param_1 = addr1;
1113 fileio_info->param_2 = len1;
1114 fileio_info->param_3 = addr2;
1115 fileio_info->param_4 = len2;
1116 } else {
1117 uint8_t *fn1 = malloc(len1+1);
1118 uint8_t *fn2 = malloc(len2+1);
1119 if (!fn1 || !fn2) {
1120 free(fn1);
1121 free(fn2);
1122 semihosting->result = -1;
1123 semihosting->sys_errno = ENOMEM;
1124 } else {
1125 retval = target_read_memory(target, addr1, 1, len1,
1126 fn1);
1127 if (retval != ERROR_OK) {
1128 free(fn1);
1129 free(fn2);
1130 return retval;
1131 }
1132 retval = target_read_memory(target, addr2, 1, len2,
1133 fn2);
1134 if (retval != ERROR_OK) {
1135 free(fn1);
1136 free(fn2);
1137 return retval;
1138 }
1139 fn1[len1] = 0;
1140 fn2[len2] = 0;
1141 semihosting->result = rename((char *)fn1,
1142 (char *)fn2);
1143 semihosting->sys_errno = errno;
1144 LOG_DEBUG("rename('%s', '%s')=%" PRId64 " %d", fn1, fn2, semihosting->result, errno);
1145 free(fn1);
1146 free(fn2);
1147 }
1148 }
1149 }
1150 break;
1151
1152 case SEMIHOSTING_SYS_SEEK: /* 0x0A */
1153 /*
1154 * Seeks to a specified position in a file using an offset
1155 * specified from the start of the file. The file is assumed
1156 * to be a byte array and the offset is given in bytes.
1157 *
1158 * Entry
1159 * On entry, the PARAMETER REGISTER contains a pointer to a
1160 * two-field data block:
1161 * - field 1 A handle for a seekable file object.
1162 * - field 2 The absolute byte position to seek to.
1163 *
1164 * Return
1165 * On exit, the RETURN REGISTER contains:
1166 * - 0 if the request is successful.
1167 * - A negative value if the request is not successful.
1168 * Use SYS_ERRNO to read the value of the host errno variable
1169 * describing the error.
1170 *
1171 * Note: The effect of seeking outside the current extent of
1172 * the file object is undefined.
1173 */
1174 retval = semihosting_read_fields(target, 2, fields);
1175 if (retval != ERROR_OK)
1176 return retval;
1177 else {
1178 int fd = semihosting_get_field(target, 0, fields);
1179 off_t pos = semihosting_get_field(target, 1, fields);
1180 if (semihosting->is_fileio) {
1181 semihosting->hit_fileio = true;
1182 fileio_info->identifier = "lseek";
1183 fileio_info->param_1 = fd;
1184 fileio_info->param_2 = pos;
1185 fileio_info->param_3 = SEEK_SET;
1186 } else {
1187 semihosting->result = lseek(fd, pos, SEEK_SET);
1188 semihosting->sys_errno = errno;
1189 LOG_DEBUG("lseek(%d, %d)=%" PRId64, fd, (int)pos, semihosting->result);
1190 if (semihosting->result == pos)
1191 semihosting->result = 0;
1192 }
1193 }
1194 break;
1195
1196 case SEMIHOSTING_SYS_SYSTEM: /* 0x12 */
1197 /*
1198 * Passes a command to the host command-line interpreter.
1199 * This enables you to execute a system command such as dir,
1200 * ls, or pwd. The terminal I/O is on the host, and is not
1201 * visible to the target.
1202 *
1203 * Entry
1204 * On entry, the PARAMETER REGISTER contains a pointer to a
1205 * two-field argument block:
1206 * - field 1 Points to a string to be passed to the host
1207 * command-line interpreter.
1208 * - field 2 The length of the string.
1209 *
1210 * Return
1211 * On exit, the RETURN REGISTER contains the return status.
1212 */
1213
1214 /* Provide SYS_SYSTEM functionality. Uses the
1215 * libc system command, there may be a reason *NOT*
1216 * to use this, but as I can't think of one, I
1217 * implemented it this way.
1218 */
1219 retval = semihosting_read_fields(target, 2, fields);
1220 if (retval != ERROR_OK)
1221 return retval;
1222 else {
1223 uint64_t addr = semihosting_get_field(target, 0, fields);
1224 size_t len = semihosting_get_field(target, 1, fields);
1225 if (semihosting->is_fileio) {
1226 semihosting->hit_fileio = true;
1227 fileio_info->identifier = "system";
1228 fileio_info->param_1 = addr;
1229 fileio_info->param_2 = len;
1230 } else {
1231 uint8_t *cmd = malloc(len+1);
1232 if (!cmd) {
1233 semihosting->result = -1;
1234 semihosting->sys_errno = ENOMEM;
1235 } else {
1236 retval = target_read_memory(target,
1237 addr,
1238 1,
1239 len,
1240 cmd);
1241 if (retval != ERROR_OK) {
1242 free(cmd);
1243 return retval;
1244 } else {
1245 cmd[len] = 0;
1246 semihosting->result = system(
1247 (const char *)cmd);
1248 LOG_DEBUG("system('%s')=%" PRId64, cmd, semihosting->result);
1249 }
1250
1251 free(cmd);
1252 }
1253 }
1254 }
1255 break;
1256
1257 case SEMIHOSTING_SYS_TIME: /* 0x11 */
1258 /*
1259 * Returns the number of seconds since 00:00 January 1, 1970.
1260 * This value is real-world time, regardless of any debug agent
1261 * configuration.
1262 *
1263 * Entry
1264 * There are no parameters.
1265 *
1266 * Return
1267 * On exit, the RETURN REGISTER contains the number of seconds.
1268 */
1269 semihosting->result = time(NULL);
1270 break;
1271
1272 case SEMIHOSTING_SYS_WRITE: /* 0x05 */
1273 /*
1274 * Writes the contents of a buffer to a specified file at the
1275 * current file position. The file position is specified either:
1276 * - Explicitly, by a SYS_SEEK.
1277 * - Implicitly as one byte beyond the previous SYS_READ or
1278 * SYS_WRITE request.
1279 *
1280 * The file position is at the start of the file when the file
1281 * is opened, and is lost when the file is closed.
1282 *
1283 * Perform the file operation as a single action whenever
1284 * possible. For example, do not split a write of 16KB into
1285 * four 4KB chunks unless there is no alternative.
1286 *
1287 * Entry
1288 * On entry, the PARAMETER REGISTER contains a pointer to a
1289 * three-field data block:
1290 * - field 1 Contains a handle for a file previously opened
1291 * with SYS_OPEN.
1292 * - field 2 Points to the memory containing the data to be written.
1293 * - field 3 Contains the number of bytes to be written from
1294 * the buffer to the file.
1295 *
1296 * Return
1297 * On exit, the RETURN REGISTER contains:
1298 * - 0 if the call is successful.
1299 * - The number of bytes that are not written, if there is an error.
1300 */
1301 retval = semihosting_read_fields(target, 3, fields);
1302 if (retval != ERROR_OK)
1303 return retval;
1304 else {
1305 int fd = semihosting_get_field(target, 0, fields);
1306 uint64_t addr = semihosting_get_field(target, 1, fields);
1307 size_t len = semihosting_get_field(target, 2, fields);
1308 if (semihosting->is_fileio) {
1309 semihosting->hit_fileio = true;
1310 fileio_info->identifier = "write";
1311 fileio_info->param_1 = fd;
1312 fileio_info->param_2 = addr;
1313 fileio_info->param_3 = len;
1314 } else {
1315 uint8_t *buf = malloc(len);
1316 if (!buf) {
1317 semihosting->result = -1;
1318 semihosting->sys_errno = ENOMEM;
1319 } else {
1320 retval = target_read_buffer(target, addr, len, buf);
1321 if (retval != ERROR_OK) {
1322 free(buf);
1323 return retval;
1324 }
1325 semihosting->result = semihosting_write(semihosting, fd, buf, len);
1326 semihosting->sys_errno = errno;
1327 LOG_DEBUG("write(%d, 0x%" PRIx64 ", %zu)=%" PRId64,
1328 fd,
1329 addr,
1330 len,
1331 semihosting->result);
1332 if (semihosting->result >= 0) {
1333 /* The number of bytes that are NOT written.
1334 * */
1335 semihosting->result = len -
1336 semihosting->result;
1337 }
1338
1339 free(buf);
1340 }
1341 }
1342 }
1343 break;
1344
1345 case SEMIHOSTING_SYS_WRITEC: /* 0x03 */
1346 /*
1347 * Writes a character byte, pointed to by the PARAMETER REGISTER,
1348 * to the debug channel. When executed under a semihosting
1349 * debugger, the character appears on the host debugger console.
1350 *
1351 * Entry
1352 * On entry, the PARAMETER REGISTER contains a pointer to the
1353 * character.
1354 *
1355 * Return
1356 * None. The RETURN REGISTER is corrupted.
1357 */
1358 if (semihosting->is_fileio) {
1359 semihosting->hit_fileio = true;
1360 fileio_info->identifier = "write";
1361 fileio_info->param_1 = 1;
1362 fileio_info->param_2 = semihosting->param;
1363 fileio_info->param_3 = 1;
1364 } else {
1365 uint64_t addr = semihosting->param;
1366 unsigned char c;
1367 retval = target_read_memory(target, addr, 1, 1, &c);
1368 if (retval != ERROR_OK)
1369 return retval;
1370 semihosting_putchar(semihosting, semihosting->stdout_fd, c);
1371 semihosting->result = 0;
1372 }
1373 break;
1374
1375 case SEMIHOSTING_SYS_WRITE0: /* 0x04 */
1376 /*
1377 * Writes a null-terminated string to the debug channel.
1378 * When executed under a semihosting debugger, the characters
1379 * appear on the host debugger console.
1380 *
1381 * Entry
1382 * On entry, the PARAMETER REGISTER contains a pointer to the
1383 * first byte of the string.
1384 *
1385 * Return
1386 * None. The RETURN REGISTER is corrupted.
1387 */
1388 if (semihosting->is_fileio) {
1389 size_t count = 0;
1390 uint64_t addr = semihosting->param;
1391 for (;; addr++) {
1392 unsigned char c;
1393 retval = target_read_memory(target, addr, 1, 1, &c);
1394 if (retval != ERROR_OK)
1395 return retval;
1396 if (c == '\0')
1397 break;
1398 count++;
1399 }
1400 semihosting->hit_fileio = true;
1401 fileio_info->identifier = "write";
1402 fileio_info->param_1 = 1;
1403 fileio_info->param_2 = semihosting->param;
1404 fileio_info->param_3 = count;
1405 } else {
1406 uint64_t addr = semihosting->param;
1407 do {
1408 unsigned char c;
1409 retval = target_read_memory(target, addr++, 1, 1, &c);
1410 if (retval != ERROR_OK)
1411 return retval;
1412 if (!c)
1413 break;
1414 semihosting_putchar(semihosting, semihosting->stdout_fd, c);
1415 } while (1);
1416 semihosting->result = 0;
1417 }
1418 break;
1419
1420 case SEMIHOSTING_USER_CMD_0x100 ... SEMIHOSTING_USER_CMD_0x107:
1421 /**
1422 * This is a user defined operation (while user cmds 0x100-0x1ff
1423 * are possible, only 0x100-0x107 are currently implemented).
1424 *
1425 * Reads the user operation parameters from target, then fires the
1426 * corresponding target event. When the target callbacks returned,
1427 * cleans up the command parameter buffer.
1428 *
1429 * Entry
1430 * On entry, the PARAMETER REGISTER contains a pointer to a
1431 * two-field data block:
1432 * - field 1 Contains a pointer to the bound command parameter
1433 * string
1434 * - field 2 Contains the command parameter string length
1435 *
1436 * Return
1437 * On exit, the RETURN REGISTER contains the return status.
1438 */
1439 if (semihosting->user_command_extension) {
1440 retval = semihosting->user_command_extension(target);
1441 if (retval != ERROR_NOT_IMPLEMENTED)
1442 break;
1443 /* If custom user command not handled, we are looking for the TCL handler */
1444 }
1445
1446 assert(!semihosting_user_op_params);
1447 retval = semihosting_read_fields(target, 2, fields);
1448 if (retval != ERROR_OK) {
1449 LOG_ERROR("Failed to read fields for user defined command"
1450 " op=0x%x", semihosting->op);
1451 return retval;
1452 }
1453
1454 uint64_t addr = semihosting_get_field(target, 0, fields);
1455
1456 size_t len = semihosting_get_field(target, 1, fields);
1457 if (len > SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH) {
1458 LOG_ERROR("The maximum length for user defined command "
1459 "parameter is %u, received length is %zu (op=0x%x)",
1460 SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH,
1461 len,
1462 semihosting->op);
1463 return ERROR_FAIL;
1464 }
1465
1466 semihosting_user_op_params = malloc(len + 1);
1467 if (!semihosting_user_op_params)
1468 return ERROR_FAIL;
1469 semihosting_user_op_params[len] = 0;
1470
1471 retval = target_read_buffer(target, addr, len,
1472 (uint8_t *)(semihosting_user_op_params));
1473 if (retval != ERROR_OK) {
1474 LOG_ERROR("Failed to read from target, semihosting op=0x%x",
1475 semihosting->op);
1476 free(semihosting_user_op_params);
1477 semihosting_user_op_params = NULL;
1478 return retval;
1479 }
1480
1481 target_handle_event(target, semihosting->op);
1482 free(semihosting_user_op_params);
1483 semihosting_user_op_params = NULL;
1484 semihosting->result = 0;
1485 break;
1486
1487 case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */
1488 /*
1489 * Returns the number of elapsed target ticks since execution
1490 * started.
1491 * Use SYS_TICKFREQ to determine the tick frequency.
1492 *
1493 * Entry (32-bit)
1494 * On entry, the PARAMETER REGISTER points to a two-field data
1495 * block to be used for returning the number of elapsed ticks:
1496 * - field 1 The least significant field and is at the low address.
1497 * - field 2 The most significant field and is at the high address.
1498 *
1499 * Entry (64-bit)
1500 * On entry the PARAMETER REGISTER points to a one-field data
1501 * block to be used for returning the number of elapsed ticks:
1502 * - field 1 The number of elapsed ticks as a 64-bit value.
1503 *
1504 * Return
1505 * On exit:
1506 * - On success, the RETURN REGISTER contains 0, the PARAMETER
1507 * REGISTER is unchanged, and the data block pointed to by the
1508 * PARAMETER REGISTER is filled in with the number of elapsed
1509 * ticks.
1510 * - On failure, the RETURN REGISTER contains -1, and the
1511 * PARAMETER REGISTER contains -1.
1512 *
1513 * Note: Some semihosting implementations might not support this
1514 * semihosting operation, and they always return -1 in the
1515 * RETURN REGISTER.
1516 */
1517
1518 case SEMIHOSTING_SYS_TICKFREQ: /* 0x31 */
1519 /*
1520 * Returns the tick frequency.
1521 *
1522 * Entry
1523 * The PARAMETER REGISTER must contain 0 on entry to this routine.
1524 *
1525 * Return
1526 * On exit, the RETURN REGISTER contains either:
1527 * - The number of ticks per second.
1528 * - –1 if the target does not know the value of one tick.
1529 *
1530 * Note: Some semihosting implementations might not support
1531 * this semihosting operation, and they always return -1 in the
1532 * RETURN REGISTER.
1533 */
1534
1535 case SEMIHOSTING_SYS_TMPNAM: /* 0x0D */
1536 /*
1537 * Returns a temporary name for a file identified by a system
1538 * file identifier.
1539 *
1540 * Entry
1541 * On entry, the PARAMETER REGISTER contains a pointer to a
1542 * three-word argument block:
1543 * - field 1 A pointer to a buffer.
1544 * - field 2 A target identifier for this filename. Its value
1545 * must be an integer in the range 0-255.
1546 * - field 3 Contains the length of the buffer. The length must
1547 * be at least the value of L_tmpnam on the host system.
1548 *
1549 * Return
1550 * On exit, the RETURN REGISTER contains:
1551 * - 0 if the call is successful.
1552 * - –1 if an error occurs.
1553 *
1554 * The buffer pointed to by the PARAMETER REGISTER contains
1555 * the filename, prefixed with a suitable directory name.
1556 * If you use the same target identifier again, the same
1557 * filename is returned.
1558 *
1559 * Note: The returned string must be null-terminated.
1560 */
1561
1562 default:
1563 fprintf(stderr, "semihosting: unsupported call %#x\n",
1564 (unsigned) semihosting->op);
1565 semihosting->result = -1;
1566 semihosting->sys_errno = ENOTSUP;
1567 }
1568
1569 if (!semihosting->hit_fileio) {
1570 retval = semihosting->post_result(target);
1571 if (retval != ERROR_OK) {
1572 LOG_ERROR("Failed to post semihosting result");
1573 return retval;
1574 }
1575 }
1576
1577 return ERROR_OK;
1578 }
1579
1580 /* -------------------------------------------------------------------------
1581 * Local functions. */
1582
1583 static int semihosting_common_fileio_info(struct target *target,
1584 struct gdb_fileio_info *fileio_info)
1585 {
1586 struct semihosting *semihosting = target->semihosting;
1587 if (!semihosting)
1588 return ERROR_FAIL;
1589
1590 /*
1591 * To avoid unnecessary duplication, semihosting prepares the
1592 * fileio_info structure out-of-band when the target halts. See
1593 * do_semihosting for more detail.
1594 */
1595 if (!semihosting->is_fileio || !semihosting->hit_fileio)
1596 return ERROR_FAIL;
1597
1598 return ERROR_OK;
1599 }
1600
1601 static int semihosting_common_fileio_end(struct target *target, int result,
1602 int fileio_errno, bool ctrl_c)
1603 {
1604 struct gdb_fileio_info *fileio_info = target->fileio_info;
1605 struct semihosting *semihosting = target->semihosting;
1606 if (!semihosting)
1607 return ERROR_FAIL;
1608
1609 /* clear pending status */
1610 semihosting->hit_fileio = false;
1611
1612 semihosting->result = result;
1613 semihosting->sys_errno = fileio_errno;
1614
1615 /*
1616 * Some fileio results do not match up with what the semihosting
1617 * operation expects; for these operations, we munge the results
1618 * below:
1619 */
1620 switch (semihosting->op) {
1621 case SEMIHOSTING_SYS_WRITE: /* 0x05 */
1622 case SEMIHOSTING_SYS_READ: /* 0x06 */
1623 if (result < 0)
1624 semihosting->result = fileio_info->param_3; /* Zero bytes read/written. */
1625 else
1626 semihosting->result = (int64_t)fileio_info->param_3 - result;
1627 break;
1628
1629 case SEMIHOSTING_SYS_SEEK: /* 0x0a */
1630 if (result > 0)
1631 semihosting->result = 0;
1632 break;
1633 }
1634
1635 return semihosting->post_result(target);
1636 }
1637
1638 /* -------------------------------------------------------------------------
1639 * Utility functions. */
1640
1641 /**
1642 * Read all fields of a command from target to buffer.
1643 */
1644 int semihosting_read_fields(struct target *target, size_t number,
1645 uint8_t *fields)
1646 {
1647 struct semihosting *semihosting = target->semihosting;
1648 /* Use 4-byte multiples to trigger fast memory access. */
1649 return target_read_memory(target, semihosting->param, 4,
1650 number * (semihosting->word_size_bytes / 4), fields);
1651 }
1652
1653 /**
1654 * Write all fields of a command from buffer to target.
1655 */
1656 int semihosting_write_fields(struct target *target, size_t number,
1657 uint8_t *fields)
1658 {
1659 struct semihosting *semihosting = target->semihosting;
1660 /* Use 4-byte multiples to trigger fast memory access. */
1661 return target_write_memory(target, semihosting->param, 4,
1662 number * (semihosting->word_size_bytes / 4), fields);
1663 }
1664
1665 /**
1666 * Extract a field from the buffer, considering register size and endianness.
1667 */
1668 uint64_t semihosting_get_field(struct target *target, size_t index,
1669 uint8_t *fields)
1670 {
1671 struct semihosting *semihosting = target->semihosting;
1672 if (semihosting->word_size_bytes == 8)
1673 return target_buffer_get_u64(target, fields + (index * 8));
1674 else
1675 return target_buffer_get_u32(target, fields + (index * 4));
1676 }
1677
1678 /**
1679 * Store a field in the buffer, considering register size and endianness.
1680 */
1681 void semihosting_set_field(struct target *target, uint64_t value,
1682 size_t index,
1683 uint8_t *fields)
1684 {
1685 struct semihosting *semihosting = target->semihosting;
1686 if (semihosting->word_size_bytes == 8)
1687 target_buffer_set_u64(target, fields + (index * 8), value);
1688 else
1689 target_buffer_set_u32(target, fields + (index * 4), value);
1690 }
1691
1692 /* -------------------------------------------------------------------------
1693 * Semihosting redirect over TCP structs and functions */
1694
1695 static int semihosting_service_new_connection_handler(struct connection *connection)
1696 {
1697 struct semihosting_tcp_service *service = connection->service->priv;
1698 service->semihosting->tcp_connection = connection;
1699
1700 return ERROR_OK;
1701 }
1702
1703 static int semihosting_service_input_handler(struct connection *connection)
1704 {
1705 struct semihosting_tcp_service *service = connection->service->priv;
1706
1707 if (!connection->input_pending) {
1708 /* consume received data, not for semihosting IO */
1709 const int buf_len = 100;
1710 char buf[buf_len];
1711 int bytes_read = connection_read(connection, buf, buf_len);
1712
1713 if (bytes_read == 0) {
1714 return ERROR_SERVER_REMOTE_CLOSED;
1715 } else if (bytes_read == -1) {
1716 LOG_ERROR("error during read: %s", strerror(errno));
1717 return ERROR_SERVER_REMOTE_CLOSED;
1718 }
1719 } else if (service->error != ERROR_OK) {
1720 return ERROR_SERVER_REMOTE_CLOSED;
1721 }
1722
1723 return ERROR_OK;
1724 }
1725
1726 static int semihosting_service_connection_closed_handler(struct connection *connection)
1727 {
1728 struct semihosting_tcp_service *service = connection->service->priv;
1729 if (service) {
1730 free(service->name);
1731 free(service);
1732 }
1733
1734 return ERROR_OK;
1735 }
1736
1737 static void semihosting_tcp_close_cnx(struct semihosting *semihosting)
1738 {
1739 if (!semihosting->tcp_connection)
1740 return;
1741
1742 struct service *service = semihosting->tcp_connection->service;
1743 remove_service(service->name, service->port);
1744 semihosting->tcp_connection = NULL;
1745
1746 }
1747
1748 static const struct service_driver semihosting_service_driver = {
1749 .name = "semihosting",
1750 .new_connection_during_keep_alive_handler = NULL,
1751 .new_connection_handler = semihosting_service_new_connection_handler,
1752 .input_handler = semihosting_service_input_handler,
1753 .connection_closed_handler = semihosting_service_connection_closed_handler,
1754 .keep_client_alive_handler = NULL,
1755 };
1756
1757 /* -------------------------------------------------------------------------
1758 * Common semihosting commands handlers. */
1759
1760 COMMAND_HANDLER(handle_common_semihosting_command)
1761 {
1762 struct target *target = get_current_target(CMD_CTX);
1763
1764 if (!target) {
1765 LOG_ERROR("No target selected");
1766 return ERROR_FAIL;
1767 }
1768
1769 struct semihosting *semihosting = target->semihosting;
1770 if (!semihosting) {
1771 command_print(CMD, "semihosting not supported for current target");
1772 return ERROR_FAIL;
1773 }
1774
1775 if (CMD_ARGC > 0) {
1776 int is_active;
1777
1778 COMMAND_PARSE_ENABLE(CMD_ARGV[0], is_active);
1779
1780 if (!target_was_examined(target)) {
1781 LOG_ERROR("Target not examined yet");
1782 return ERROR_FAIL;
1783 }
1784
1785 if (semihosting && semihosting->setup(target, is_active) != ERROR_OK) {
1786 LOG_ERROR("Failed to Configure semihosting");
1787 return ERROR_FAIL;
1788 }
1789
1790 /* FIXME never let that "catch" be dropped! (???) */
1791 semihosting->is_active = is_active;
1792 }
1793
1794 command_print(CMD, "semihosting is %s",
1795 semihosting->is_active
1796 ? "enabled" : "disabled");
1797
1798 return ERROR_OK;
1799 }
1800
1801 COMMAND_HANDLER(handle_common_semihosting_redirect_command)
1802 {
1803 struct target *target = get_current_target(CMD_CTX);
1804
1805 if (!target) {
1806 LOG_ERROR("No target selected");
1807 return ERROR_FAIL;
1808 }
1809
1810 struct semihosting *semihosting = target->semihosting;
1811 if (!semihosting) {
1812 command_print(CMD, "semihosting not supported for current target");
1813 return ERROR_FAIL;
1814 }
1815
1816 if (!semihosting->is_active) {
1817 command_print(CMD, "semihosting not yet enabled for current target");
1818 return ERROR_FAIL;
1819 }
1820
1821 enum semihosting_redirect_config cfg;
1822 const char *port;
1823
1824 if (CMD_ARGC < 1)
1825 return ERROR_COMMAND_SYNTAX_ERROR;
1826
1827 if (strcmp(CMD_ARGV[0], "disable") == 0) {
1828 cfg = SEMIHOSTING_REDIRECT_CFG_NONE;
1829 if (CMD_ARGC > 1)
1830 return ERROR_COMMAND_SYNTAX_ERROR;
1831 } else if (strcmp(CMD_ARGV[0], "tcp") == 0) {
1832 if (CMD_ARGC < 2 || CMD_ARGC > 3)
1833 return ERROR_COMMAND_SYNTAX_ERROR;
1834
1835 port = CMD_ARGV[1];
1836
1837 cfg = SEMIHOSTING_REDIRECT_CFG_ALL;
1838 if (CMD_ARGC == 3) {
1839 if (strcmp(CMD_ARGV[2], "debug") == 0)
1840 cfg = SEMIHOSTING_REDIRECT_CFG_DEBUG;
1841 else if (strcmp(CMD_ARGV[2], "stdio") == 0)
1842 cfg = SEMIHOSTING_REDIRECT_CFG_STDIO;
1843 else if (strcmp(CMD_ARGV[2], "all") != 0)
1844 return ERROR_COMMAND_SYNTAX_ERROR;
1845 }
1846 } else {
1847 return ERROR_COMMAND_SYNTAX_ERROR;
1848 }
1849
1850 semihosting_tcp_close_cnx(semihosting);
1851 semihosting->redirect_cfg = SEMIHOSTING_REDIRECT_CFG_NONE;
1852
1853 if (cfg != SEMIHOSTING_REDIRECT_CFG_NONE) {
1854 struct semihosting_tcp_service *service =
1855 calloc(1, sizeof(struct semihosting_tcp_service));
1856 if (!service) {
1857 LOG_ERROR("Failed to allocate semihosting TCP service.");
1858 return ERROR_FAIL;
1859 }
1860
1861 service->semihosting = semihosting;
1862
1863 service->name = alloc_printf("%s semihosting service", target_name(target));
1864 if (!service->name) {
1865 LOG_ERROR("Out of memory");
1866 free(service);
1867 return ERROR_FAIL;
1868 }
1869
1870 int ret = add_service(&semihosting_service_driver,
1871 port, 1, service);
1872
1873 if (ret != ERROR_OK) {
1874 LOG_ERROR("failed to initialize %s", service->name);
1875 free(service->name);
1876 free(service);
1877 return ERROR_FAIL;
1878 }
1879 }
1880
1881 semihosting->redirect_cfg = cfg;
1882
1883 return ERROR_OK;
1884 }
1885
1886 COMMAND_HANDLER(handle_common_semihosting_fileio_command)
1887 {
1888 struct target *target = get_current_target(CMD_CTX);
1889
1890 if (!target) {
1891 LOG_ERROR("No target selected");
1892 return ERROR_FAIL;
1893 }
1894
1895 struct semihosting *semihosting = target->semihosting;
1896 if (!semihosting) {
1897 command_print(CMD, "semihosting not supported for current target");
1898 return ERROR_FAIL;
1899 }
1900
1901 if (!semihosting->is_active) {
1902 command_print(CMD, "semihosting not yet enabled for current target");
1903 return ERROR_FAIL;
1904 }
1905
1906 if (CMD_ARGC > 0)
1907 COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->is_fileio);
1908
1909 command_print(CMD, "semihosting fileio is %s",
1910 semihosting->is_fileio
1911 ? "enabled" : "disabled");
1912
1913 return ERROR_OK;
1914 }
1915
1916 COMMAND_HANDLER(handle_common_semihosting_cmdline)
1917 {
1918 struct target *target = get_current_target(CMD_CTX);
1919 unsigned int i;
1920
1921 if (!target) {
1922 LOG_ERROR("No target selected");
1923 return ERROR_FAIL;
1924 }
1925
1926 struct semihosting *semihosting = target->semihosting;
1927 if (!semihosting) {
1928 command_print(CMD, "semihosting not supported for current target");
1929 return ERROR_FAIL;
1930 }
1931
1932 free(semihosting->cmdline);
1933 semihosting->cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
1934
1935 for (i = 1; i < CMD_ARGC; i++) {
1936 char *cmdline = alloc_printf("%s %s", semihosting->cmdline, CMD_ARGV[i]);
1937 if (!cmdline)
1938 break;
1939 free(semihosting->cmdline);
1940 semihosting->cmdline = cmdline;
1941 }
1942
1943 command_print(CMD, "semihosting command line is [%s]",
1944 semihosting->cmdline);
1945
1946 return ERROR_OK;
1947 }
1948
1949 COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
1950 {
1951 struct target *target = get_current_target(CMD_CTX);
1952
1953 if (!target) {
1954 LOG_ERROR("No target selected");
1955 return ERROR_FAIL;
1956 }
1957
1958 struct semihosting *semihosting = target->semihosting;
1959 if (!semihosting) {
1960 command_print(CMD, "semihosting not supported for current target");
1961 return ERROR_FAIL;
1962 }
1963
1964 if (!semihosting->is_active) {
1965 command_print(CMD, "semihosting not yet enabled for current target");
1966 return ERROR_FAIL;
1967 }
1968
1969 if (CMD_ARGC > 0)
1970 COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->has_resumable_exit);
1971
1972 command_print(CMD, "semihosting resumable exit is %s",
1973 semihosting->has_resumable_exit
1974 ? "enabled" : "disabled");
1975
1976 return ERROR_OK;
1977 }
1978
1979 COMMAND_HANDLER(handle_common_semihosting_read_user_param_command)
1980 {
1981 struct target *target = get_current_target(CMD_CTX);
1982 struct semihosting *semihosting = target->semihosting;
1983
1984 if (CMD_ARGC)
1985 return ERROR_COMMAND_SYNTAX_ERROR;
1986
1987 if (!semihosting->is_active) {
1988 LOG_ERROR("semihosting not yet enabled for current target");
1989 return ERROR_FAIL;
1990 }
1991
1992 if (!semihosting_user_op_params) {
1993 LOG_ERROR("This command is usable only from a registered user "
1994 "semihosting event callback.");
1995 return ERROR_FAIL;
1996 }
1997
1998 command_print_sameline(CMD, "%s", semihosting_user_op_params);
1999
2000 return ERROR_OK;
2001 }
2002
2003 COMMAND_HANDLER(handle_common_semihosting_basedir_command)
2004 {
2005 struct target *target = get_current_target(CMD_CTX);
2006
2007 if (CMD_ARGC > 1)
2008 return ERROR_COMMAND_SYNTAX_ERROR;
2009
2010 if (!target) {
2011 LOG_ERROR("No target selected");
2012 return ERROR_FAIL;
2013 }
2014
2015 struct semihosting *semihosting = target->semihosting;
2016 if (!semihosting) {
2017 command_print(CMD, "semihosting not supported for current target");
2018 return ERROR_FAIL;
2019 }
2020
2021 if (!semihosting->is_active) {
2022 command_print(CMD, "semihosting not yet enabled for current target");
2023 return ERROR_FAIL;
2024 }
2025
2026 if (CMD_ARGC > 0) {
2027 free(semihosting->basedir);
2028 semihosting->basedir = strdup(CMD_ARGV[0]);
2029 if (!semihosting->basedir) {
2030 command_print(CMD, "semihosting failed to allocate memory for basedir!");
2031 return ERROR_FAIL;
2032 }
2033 }
2034
2035 command_print(CMD, "semihosting base dir: %s",
2036 semihosting->basedir ? semihosting->basedir : "");
2037
2038 return ERROR_OK;
2039 }
2040
2041 const struct command_registration semihosting_common_handlers[] = {
2042 {
2043 .name = "semihosting",
2044 .handler = handle_common_semihosting_command,
2045 .mode = COMMAND_EXEC,
2046 .usage = "['enable'|'disable']",
2047 .help = "activate support for semihosting operations",
2048 },
2049 {
2050 .name = "semihosting_redirect",
2051 .handler = handle_common_semihosting_redirect_command,
2052 .mode = COMMAND_EXEC,
2053 .usage = "(disable | tcp <port> ['debug'|'stdio'|'all'])",
2054 .help = "redirect semihosting IO",
2055 },
2056 {
2057 .name = "semihosting_cmdline",
2058 .handler = handle_common_semihosting_cmdline,
2059 .mode = COMMAND_EXEC,
2060 .usage = "arguments",
2061 .help = "command line arguments to be passed to program",
2062 },
2063 {
2064 .name = "semihosting_fileio",
2065 .handler = handle_common_semihosting_fileio_command,
2066 .mode = COMMAND_EXEC,
2067 .usage = "['enable'|'disable']",
2068 .help = "activate support for semihosting fileio operations",
2069 },
2070 {
2071 .name = "semihosting_resexit",
2072 .handler = handle_common_semihosting_resumable_exit_command,
2073 .mode = COMMAND_EXEC,
2074 .usage = "['enable'|'disable']",
2075 .help = "activate support for semihosting resumable exit",
2076 },
2077 {
2078 .name = "semihosting_read_user_param",
2079 .handler = handle_common_semihosting_read_user_param_command,
2080 .mode = COMMAND_EXEC,
2081 .usage = "",
2082 .help = "read parameters in semihosting-user-cmd-0x10X callbacks",
2083 },
2084 {
2085 .name = "semihosting_basedir",
2086 .handler = handle_common_semihosting_basedir_command,
2087 .mode = COMMAND_EXEC,
2088 .usage = "[dir]",
2089 .help = "set the base directory for semihosting I/O operations",
2090 },
2091 COMMAND_REGISTRATION_DONE
2092 };

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)