openocd: manually fix Yoda conditions
[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 "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[12];
89
90         /* Read the current instruction, including the bracketing */
91         *retval = target_read_memory(target, pc - 4, 2, 6, tmp);
92         if (*retval != ERROR_OK)
93                 return SEMI_ERROR;
94
95         /*
96          * The instructions that trigger a semihosting call,
97          * always uncompressed, should look like:
98          *
99          * 01f01013              slli    zero,zero,0x1f
100          * 00100073              ebreak
101          * 40705013              srai    zero,zero,0x7
102          */
103         uint32_t pre = target_buffer_get_u32(target, tmp);
104         uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
105         uint32_t post = target_buffer_get_u32(target, tmp + 8);
106         LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
107
108         if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
109                 /* Not the magic sequence defining semihosting. */
110                 LOG_DEBUG("   -> NONE (no magic)");
111                 return SEMI_NONE;
112         }
113
114         /*
115          * Perform semihosting call if we are not waiting on a fileio
116          * operation to complete.
117          */
118         if (!semihosting->hit_fileio) {
119                 /* RISC-V uses A0 and A1 to pass function arguments */
120                 riscv_reg_t r0;
121                 riscv_reg_t r1;
122
123                 result = riscv_get_register(target, &r0, GDB_REGNO_A0);
124                 if (result != ERROR_OK) {
125                         LOG_DEBUG("   -> ERROR (couldn't read a0)");
126                         return SEMI_ERROR;
127                 }
128
129                 result = riscv_get_register(target, &r1, GDB_REGNO_A1);
130                 if (result != ERROR_OK) {
131                         LOG_DEBUG("   -> ERROR (couldn't read a1)");
132                         return SEMI_ERROR;
133                 }
134
135                 semihosting->op = r0;
136                 semihosting->param = r1;
137                 semihosting->word_size_bytes = riscv_xlen(target) / 8;
138
139                 /* Check for ARM operation numbers. */
140                 if (semihosting->op >= 0 && semihosting->op <= 0x31) {
141                         *retval = semihosting_common(target);
142                         if (*retval != ERROR_OK) {
143                                 LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
144                                 return SEMI_ERROR;
145                         }
146                 } else {
147                         /* Unknown operation number, not a semihosting call. */
148                         LOG_DEBUG("   -> NONE (unknown operation number)");
149                         return SEMI_NONE;
150                 }
151         }
152
153         /*
154          * Resume target if we are not waiting on a fileio
155          * operation to complete.
156          */
157         if (semihosting->is_resumable && !semihosting->hit_fileio) {
158                 /* Resume right after the EBREAK 4 bytes instruction. */
159                 *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
160                 if (*retval != ERROR_OK)
161                         return SEMI_ERROR;
162
163                 LOG_DEBUG("   -> HANDLED");
164                 return SEMI_HANDLED;
165         }
166
167         LOG_DEBUG("   -> WAITING");
168         return SEMI_WAITING;
169 }
170
171 /* -------------------------------------------------------------------------
172  * Local functions. */
173
174 /**
175  * Called via semihosting->setup() later, after the target is known,
176  * usually on the first semihosting command.
177  */
178 static int riscv_semihosting_setup(struct target *target, int enable)
179 {
180         LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
181
182         struct semihosting *semihosting = target->semihosting;
183         if (semihosting)
184                 semihosting->setup_time = clock();
185
186         return ERROR_OK;
187 }
188
189 static int riscv_semihosting_post_result(struct target *target)
190 {
191         struct semihosting *semihosting = target->semihosting;
192         if (!semihosting) {
193                 /* If not enabled, silently ignored. */
194                 return 0;
195         }
196
197         LOG_DEBUG("0x%" PRIx64, semihosting->result);
198         riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
199         return 0;
200 }