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