blob: 3dee7d51b363f66f6b7ce110d62cfb3fb410c90a [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Darwin Rambo261d2762014-06-09 11:12:59 -07002/*
Sean Anderson12a05b32022-03-22 16:59:18 -04003 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
Darwin Rambo261d2762014-06-09 11:12:59 -07004 * Copyright 2014 Broadcom Corporation
Darwin Rambo261d2762014-06-09 11:12:59 -07005 */
6
Darwin Rambo261d2762014-06-09 11:12:59 -07007#include <common.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -06008#include <log.h>
Sean Andersonb10f7242022-03-22 16:59:14 -04009#include <semihosting.h>
Darwin Rambo261d2762014-06-09 11:12:59 -070010
11#define SYSOPEN 0x01
12#define SYSCLOSE 0x02
Sean Anderson3ea744e2022-03-22 16:59:23 -040013#define SYSWRITEC 0x03
14#define SYSWRITE0 0x04
Sean Anderson12a05b32022-03-22 16:59:18 -040015#define SYSWRITE 0x05
Darwin Rambo261d2762014-06-09 11:12:59 -070016#define SYSREAD 0x06
Sean Anderson3ea744e2022-03-22 16:59:23 -040017#define SYSREADC 0x07
Sean Anderson385d69d2022-03-22 16:59:30 -040018#define SYSISERROR 0x08
Sean Anderson12a05b32022-03-22 16:59:18 -040019#define SYSSEEK 0x0A
Darwin Rambo261d2762014-06-09 11:12:59 -070020#define SYSFLEN 0x0C
Sean Anderson80e62cc2022-03-22 16:59:16 -040021#define SYSERRNO 0x13
Darwin Rambo261d2762014-06-09 11:12:59 -070022
Andre Przywarad660a822022-10-05 17:38:48 +010023/*
24 * Macro to force the compiler to *populate* memory (for an array or struct)
25 * before passing the pointer to an inline assembly call.
26 */
27#define USE_PTR(ptr) *(const char (*)[]) (ptr)
28
Andre Przywara30b315b2022-10-05 17:38:47 +010029#if defined(CONFIG_ARM64)
30 #define SMH_TRAP "hlt #0xf000"
31#elif defined(CONFIG_CPU_V7M)
32 #define SMH_TRAP "bkpt #0xAB"
33#elif defined(CONFIG_SYS_THUMB_BUILD)
34 #define SMH_TRAP "svc #0xab"
35#else
36 #define SMH_TRAP "svc #0x123456"
37#endif
38
Darwin Rambo261d2762014-06-09 11:12:59 -070039/*
40 * Call the handler
41 */
Linus Walleije769f682015-03-23 11:06:10 +010042static noinline long smh_trap(unsigned int sysnum, void *addr)
Darwin Rambo261d2762014-06-09 11:12:59 -070043{
Linus Walleij4e1ef152014-12-15 11:05:56 +010044 register long result asm("r0");
Andre Przywara30b315b2022-10-05 17:38:47 +010045
Andre Przywarad660a822022-10-05 17:38:48 +010046 /*
47 * We need a memory clobber (aka compiler barrier) for two reasons:
48 * - The compiler needs to populate any data structures pointed to
49 * by "addr" *before* the trap instruction is called.
50 * - At least the SYSREAD function puts the result into memory pointed
51 * to by "addr", so the compiler must not use a cached version of
52 * the previous content, after the call has finished.
53 */
Andre Przywara30b315b2022-10-05 17:38:47 +010054 asm volatile (SMH_TRAP
55 : "=r" (result)
Andre Przywarad660a822022-10-05 17:38:48 +010056 : "0"(sysnum), "r"(USE_PTR(addr))
Andre Przywara30b315b2022-10-05 17:38:47 +010057 : "memory");
58
Darwin Rambo261d2762014-06-09 11:12:59 -070059 return result;
60}
61
Sean Anderson385d69d2022-03-22 16:59:30 -040062#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
63static bool _semihosting_enabled = true;
64static bool try_semihosting = true;
65
66bool semihosting_enabled(void)
67{
68 if (try_semihosting) {
69 smh_trap(SYSERRNO, NULL);
70 try_semihosting = false;
71 }
72
73 return _semihosting_enabled;
74}
75
76void disable_semihosting(void)
77{
78 _semihosting_enabled = false;
79}
80#endif
81
Sean Anderson80e62cc2022-03-22 16:59:16 -040082/**
83 * smh_errno() - Read the host's errno
84 *
85 * This gets the value of the host's errno and negates it. The host's errno may
86 * or may not be set, so only call this function if a previous semihosting call
87 * has failed.
88 *
89 * Return: a negative error value
Linus Walleij9be5c662014-12-15 11:06:05 +010090 */
Sean Anderson80e62cc2022-03-22 16:59:16 -040091static int smh_errno(void)
92{
93 long ret = smh_trap(SYSERRNO, NULL);
94
95 if (ret > 0 && ret < INT_MAX)
96 return -ret;
97 return -EIO;
98}
99
Sean Andersoneff77c32022-03-22 16:59:15 -0400100long smh_open(const char *fname, enum smh_open_mode mode)
Linus Walleij9be5c662014-12-15 11:06:05 +0100101{
102 long fd;
Linus Walleij9be5c662014-12-15 11:06:05 +0100103 struct smh_open_s {
104 const char *fname;
105 unsigned long mode;
106 size_t len;
107 } open;
108
Sean Andersoneff77c32022-03-22 16:59:15 -0400109 debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
Linus Walleij9be5c662014-12-15 11:06:05 +0100110
111 open.fname = fname;
112 open.len = strlen(fname);
113 open.mode = mode;
114
115 /* Open the file on the host */
116 fd = smh_trap(SYSOPEN, &open);
117 if (fd == -1)
Sean Anderson80e62cc2022-03-22 16:59:16 -0400118 return smh_errno();
Linus Walleij9be5c662014-12-15 11:06:05 +0100119 return fd;
120}
121
Sean Anderson12a05b32022-03-22 16:59:18 -0400122/**
123 * struct smg_rdwr_s - Arguments for read and write
124 * @fd: A file descriptor returned from smh_open()
125 * @memp: Pointer to a buffer of memory of at least @len bytes
126 * @len: The number of bytes to read or write
127 */
128struct smh_rdwr_s {
129 long fd;
130 void *memp;
131 size_t len;
132};
133
Sean Andersonb10f7242022-03-22 16:59:14 -0400134long smh_read(long fd, void *memp, size_t len)
Linus Walleij9be5c662014-12-15 11:06:05 +0100135{
136 long ret;
Sean Anderson12a05b32022-03-22 16:59:18 -0400137 struct smh_rdwr_s read;
Linus Walleij9be5c662014-12-15 11:06:05 +0100138
Vadzim Dambrouski7bdf75c2015-10-19 19:40:15 +0300139 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
Linus Walleij9be5c662014-12-15 11:06:05 +0100140
141 read.fd = fd;
142 read.memp = memp;
143 read.len = len;
144
145 ret = smh_trap(SYSREAD, &read);
Sean Anderson80e62cc2022-03-22 16:59:16 -0400146 if (ret < 0)
147 return smh_errno();
148 return len - ret;
Linus Walleij9be5c662014-12-15 11:06:05 +0100149}
150
Sean Anderson12a05b32022-03-22 16:59:18 -0400151long smh_write(long fd, const void *memp, size_t len, ulong *written)
152{
153 long ret;
154 struct smh_rdwr_s write;
155
156 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
157
158 write.fd = fd;
159 write.memp = (void *)memp;
160 write.len = len;
161
162 ret = smh_trap(SYSWRITE, &write);
163 *written = len - ret;
164 if (ret)
165 return smh_errno();
166 return 0;
167}
168
Sean Andersonb10f7242022-03-22 16:59:14 -0400169long smh_close(long fd)
Linus Walleij9be5c662014-12-15 11:06:05 +0100170{
171 long ret;
172
173 debug("%s: fd %ld\n", __func__, fd);
174
175 ret = smh_trap(SYSCLOSE, &fd);
176 if (ret == -1)
Sean Anderson80e62cc2022-03-22 16:59:16 -0400177 return smh_errno();
178 return 0;
Linus Walleij9be5c662014-12-15 11:06:05 +0100179}
180
Sean Andersonb10f7242022-03-22 16:59:14 -0400181long smh_flen(long fd)
Linus Walleij9be5c662014-12-15 11:06:05 +0100182{
183 long ret;
184
185 debug("%s: fd %ld\n", __func__, fd);
186
187 ret = smh_trap(SYSFLEN, &fd);
188 if (ret == -1)
Sean Anderson80e62cc2022-03-22 16:59:16 -0400189 return smh_errno();
Linus Walleij9be5c662014-12-15 11:06:05 +0100190 return ret;
191}
192
Sean Anderson12a05b32022-03-22 16:59:18 -0400193long smh_seek(long fd, long pos)
194{
195 long ret;
196 struct smh_seek_s {
197 long fd;
198 long pos;
199 } seek;
200
201 debug("%s: fd %ld pos %ld\n", __func__, fd, pos);
202
203 seek.fd = fd;
204 seek.pos = pos;
205
206 ret = smh_trap(SYSSEEK, &seek);
207 if (ret)
208 return smh_errno();
209 return 0;
210}
Sean Anderson3ea744e2022-03-22 16:59:23 -0400211
212int smh_getc(void)
213{
214 return smh_trap(SYSREADC, NULL);
215}
216
217void smh_putc(char ch)
218{
219 smh_trap(SYSWRITEC, &ch);
220}
221
222void smh_puts(const char *s)
223{
224 smh_trap(SYSWRITE0, (char *)s);
225}