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