Add RISC-V support.
[fw/openocd] / src / target / riscv / riscv_semihosting.c
1 /***************************************************************************
2  *   Copyright (C) 2018 by Liviu Ionescu                                   *
3  *   ilg@livius.net                                                        *
4  *                                                                         *
5  *   Copyright (C) 2009 by Marvell Technology Group Ltd.                   *
6  *   Written by Nicolas Pitre <nico@marvell.com>                           *
7  *                                                                         *
8  *   Copyright (C) 2010 by Spencer Oliver                                  *
9  *   spen@spen-soft.co.uk                                                  *
10  *                                                                         *
11  *   Copyright (C) 2016 by Square, Inc.                                    *
12  *   Steven Stallion <stallion@squareup.com>                               *
13  *                                                                         *
14  *   This program is free software; you can redistribute it and/or modify  *
15  *   it under the terms of the GNU General Public License as published by  *
16  *   the Free Software Foundation; either version 2 of the License, or     *
17  *   (at your option) any later version.                                   *
18  *                                                                         *
19  *   This program is distributed in the hope that it will be useful,       *
20  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
21  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
22  *   GNU General Public License for more details.                          *
23  *                                                                         *
24  *   You should have received a copy of the GNU General Public License     *
25  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
26  ***************************************************************************/
27
28 /**
29  * @file
30  * Hold RISC-V semihosting support.
31  *
32  * The RISC-V code is inspired from ARM semihosting.
33  *
34  * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
35  * from ARM Ltd.
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include "log.h"
43
44 #include "target/target.h"
45 #include "target/semihosting_common.h"
46 #include "riscv.h"
47
48 static int riscv_semihosting_setup(struct target *target, int enable);
49 static int riscv_semihosting_post_result(struct target *target);
50
51 /**
52  * Initialize RISC-V semihosting. Use common ARM code.
53  */
54 void riscv_semihosting_init(struct target *target)
55 {
56         semihosting_common_init(target, riscv_semihosting_setup,
57                 riscv_semihosting_post_result);
58 }
59
60 /**
61  * Check for and process a semihosting request using the ARM protocol). This
62  * is meant to be called when the target is stopped due to a debug mode entry.
63  * If the value 0 is returned then there was nothing to process. A non-zero
64  * return value signifies that a request was processed and the target resumed,
65  * or an error was encountered, in which case the caller must return
66  * immediately.
67  *
68  * @param target Pointer to the target to process.
69  * @param retval Pointer to a location where the return code will be stored
70  * @return non-zero value if a request was processed or an error encountered
71  */
72 int riscv_semihosting(struct target *target, int *retval)
73 {
74         struct semihosting *semihosting = target->semihosting;
75         if (!semihosting)
76                 return 0;
77
78         if (!semihosting->is_active)
79                 return 0;
80
81         riscv_reg_t dpc;
82         int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
83         if (result != ERROR_OK)
84                 return 0;
85
86         uint8_t tmp[12];
87
88         /* Read the current instruction, including the bracketing */
89         *retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
90         if (*retval != ERROR_OK)
91                 return 0;
92
93         /*
94          * The instructions that trigger a semihosting call,
95          * always uncompressed, should look like:
96          *
97          * 01f01013              slli    zero,zero,0x1f
98          * 00100073              ebreak
99          * 40705013              srai    zero,zero,0x7
100          */
101         uint32_t pre = target_buffer_get_u32(target, tmp);
102         uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
103         uint32_t post = target_buffer_get_u32(target, tmp + 8);
104         LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
105
106         if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
107
108                 /* Not the magic sequence defining semihosting. */
109                 return 0;
110         }
111
112         /*
113          * Perform semihosting call if we are not waiting on a fileio
114          * operation to complete.
115          */
116         if (!semihosting->hit_fileio) {
117
118                 /* RISC-V uses A0 and A1 to pass function arguments */
119                 riscv_reg_t r0;
120                 riscv_reg_t r1;
121
122                 result = riscv_get_register(target, &r0, GDB_REGNO_A0);
123                 if (result != ERROR_OK)
124                         return 0;
125
126                 result = riscv_get_register(target, &r1, GDB_REGNO_A1);
127                 if (result != ERROR_OK)
128                         return 0;
129
130                 semihosting->op = r0;
131                 semihosting->param = r1;
132                 semihosting->word_size_bytes = riscv_xlen(target) / 8;
133
134                 /* Check for ARM operation numbers. */
135                 if (0 <= semihosting->op && semihosting->op <= 0x31) {
136                         *retval = semihosting_common(target);
137                         if (*retval != ERROR_OK) {
138                                 LOG_ERROR("Failed semihosting operation");
139                                 return 0;
140                         }
141                 } else {
142                         /* Unknown operation number, not a semihosting call. */
143                         return 0;
144                 }
145         }
146
147         /*
148          * Resume target if we are not waiting on a fileio
149          * operation to complete.
150          */
151         if (semihosting->is_resumable && !semihosting->hit_fileio) {
152                 /* Resume right after the EBREAK 4 bytes instruction. */
153                 *retval = target_resume(target, 0, dpc+4, 0, 0);
154                 if (*retval != ERROR_OK) {
155                         LOG_ERROR("Failed to resume target");
156                         return 0;
157                 }
158
159                 return 1;
160         }
161
162         return 0;
163 }
164
165 /* -------------------------------------------------------------------------
166  * Local functions. */
167
168 /**
169  * Called via semihosting->setup() later, after the target is known,
170  * usually on the first semihosting command.
171  */
172 static int riscv_semihosting_setup(struct target *target, int enable)
173 {
174         LOG_DEBUG("enable=%d", enable);
175
176         struct semihosting *semihosting = target->semihosting;
177         if (semihosting)
178                 semihosting->setup_time = clock();
179
180         return ERROR_OK;
181 }
182
183 static int riscv_semihosting_post_result(struct target *target)
184 {
185         struct semihosting *semihosting = target->semihosting;
186         if (!semihosting) {
187                 /* If not enabled, silently ignored. */
188                 return 0;
189         }
190
191         LOG_DEBUG("0x%" PRIx64, semihosting->result);
192         riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
193         return 0;
194 }