openocd: fix SPDX tag format for files .c
[fw/openocd] / src / target / riscv / riscv_semihosting.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) 2009 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  * Hold RISC-V semihosting support.
20  *
21  * The RISC-V code is inspired from ARM semihosting.
22  *
23  * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
24  * from ARM Ltd.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <helper/log.h>
32
33 #include "target/target.h"
34 #include "riscv.h"
35
36 static int riscv_semihosting_setup(struct target *target, int enable);
37 static int riscv_semihosting_post_result(struct target *target);
38
39 /**
40  * Initialize RISC-V semihosting. Use common ARM code.
41  */
42 void riscv_semihosting_init(struct target *target)
43 {
44         semihosting_common_init(target, riscv_semihosting_setup,
45                 riscv_semihosting_post_result);
46 }
47
48 /**
49  * Check for and process a semihosting request using the ARM protocol). This
50  * is meant to be called when the target is stopped due to a debug mode entry.
51  *
52  * @param target Pointer to the target to process.
53  * @param retval Pointer to a location where the return code will be stored
54  * @return non-zero value if a request was processed or an error encountered
55  */
56 enum semihosting_result riscv_semihosting(struct target *target, int *retval)
57 {
58         struct semihosting *semihosting = target->semihosting;
59         if (!semihosting) {
60                 LOG_DEBUG("   -> NONE (!semihosting)");
61                 return SEMIHOSTING_NONE;
62         }
63
64         if (!semihosting->is_active) {
65                 LOG_DEBUG("   -> NONE (!semihosting->is_active)");
66                 return SEMIHOSTING_NONE;
67         }
68
69         riscv_reg_t pc;
70         int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
71         if (result != ERROR_OK)
72                 return SEMIHOSTING_ERROR;
73
74         uint8_t tmp_buf[12];
75
76         /* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
77         for (int i = 0; i < 3; i++) {
78                 /* Instruction memories may not support arbitrary read size. Use any size that will work. */
79                 *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i);
80                 if (*retval != ERROR_OK)
81                         return SEMIHOSTING_ERROR;
82         }
83
84         /*
85          * The instructions that trigger a semihosting call,
86          * always uncompressed, should look like:
87          *
88          * 01f01013              slli    zero,zero,0x1f
89          * 00100073              ebreak
90          * 40705013              srai    zero,zero,0x7
91          */
92         uint32_t pre = target_buffer_get_u32(target, tmp_buf);
93         uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4);
94         uint32_t post = target_buffer_get_u32(target, tmp_buf + 8);
95         LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
96
97         if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
98                 /* Not the magic sequence defining semihosting. */
99                 LOG_DEBUG("   -> NONE (no magic)");
100                 return SEMIHOSTING_NONE;
101         }
102
103         /*
104          * Perform semihosting call if we are not waiting on a fileio
105          * operation to complete.
106          */
107         if (!semihosting->hit_fileio) {
108                 /* RISC-V uses A0 and A1 to pass function arguments */
109                 riscv_reg_t r0;
110                 riscv_reg_t r1;
111
112                 result = riscv_get_register(target, &r0, GDB_REGNO_A0);
113                 if (result != ERROR_OK) {
114                         LOG_DEBUG("   -> ERROR (couldn't read a0)");
115                         return SEMIHOSTING_ERROR;
116                 }
117
118                 result = riscv_get_register(target, &r1, GDB_REGNO_A1);
119                 if (result != ERROR_OK) {
120                         LOG_DEBUG("   -> ERROR (couldn't read a1)");
121                         return SEMIHOSTING_ERROR;
122                 }
123
124                 semihosting->op = r0;
125                 semihosting->param = r1;
126                 semihosting->word_size_bytes = riscv_xlen(target) / 8;
127
128                 /* Check for ARM operation numbers. */
129                 if ((semihosting->op >= 0 && semihosting->op <= 0x31) ||
130                         (semihosting->op >= 0x100 && semihosting->op <= 0x107)) {
131
132                         *retval = semihosting_common(target);
133                         if (*retval != ERROR_OK) {
134                                 LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
135                                 return SEMIHOSTING_ERROR;
136                         }
137                 } else {
138                         /* Unknown operation number, not a semihosting call. */
139                         LOG_DEBUG("   -> NONE (unknown operation number)");
140                         return SEMIHOSTING_NONE;
141                 }
142         }
143
144         /*
145          * Resume target if we are not waiting on a fileio
146          * operation to complete.
147          */
148         if (semihosting->is_resumable && !semihosting->hit_fileio) {
149                 /* Resume right after the EBREAK 4 bytes instruction. */
150                 *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
151                 if (*retval != ERROR_OK)
152                         return SEMIHOSTING_ERROR;
153
154                 LOG_DEBUG("   -> HANDLED");
155                 return SEMIHOSTING_HANDLED;
156         }
157
158         LOG_DEBUG("   -> WAITING");
159         return SEMIHOSTING_WAITING;
160 }
161
162 /* -------------------------------------------------------------------------
163  * Local functions. */
164
165 /**
166  * Called via semihosting->setup() later, after the target is known,
167  * usually on the first semihosting command.
168  */
169 static int riscv_semihosting_setup(struct target *target, int enable)
170 {
171         LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
172
173         struct semihosting *semihosting = target->semihosting;
174         if (semihosting)
175                 semihosting->setup_time = clock();
176
177         return ERROR_OK;
178 }
179
180 static int riscv_semihosting_post_result(struct target *target)
181 {
182         struct semihosting *semihosting = target->semihosting;
183         if (!semihosting) {
184                 /* If not enabled, silently ignored. */
185                 return 0;
186         }
187
188         LOG_DEBUG("0x%" PRIx64, semihosting->result);
189         riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
190         return 0;
191 }