1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
4 * Copyright 2014 Broadcom Corporation
5 */
6
7#include <log.h>
8#include <semihosting.h>
9#include <linux/errno.h>
10#include <linux/string.h>
11
12#define SYSOPEN		0x01
13#define SYSCLOSE	0x02
14#define SYSWRITEC	0x03
15#define SYSWRITE0	0x04
16#define SYSWRITE	0x05
17#define SYSREAD		0x06
18#define SYSREADC	0x07
19#define SYSISERROR	0x08
20#define SYSSEEK		0x0A
21#define SYSFLEN		0x0C
22#define SYSERRNO	0x13
23
24#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
25static bool _semihosting_enabled = true;
26static bool try_semihosting = true;
27
28bool semihosting_enabled(void)
29{
30	if (try_semihosting) {
31		smh_trap(SYSERRNO, NULL);
32		try_semihosting = false;
33	}
34
35	return _semihosting_enabled;
36}
37
38void disable_semihosting(void)
39{
40	_semihosting_enabled = false;
41}
42#endif
43
44/**
45 * smh_errno() - Read the host's errno
46 *
47 * This gets the value of the host's errno and negates it. The host's errno may
48 * or may not be set, so only call this function if a previous semihosting call
49 * has failed.
50 *
51 * Return: a negative error value
52 */
53static int smh_errno(void)
54{
55	long ret = smh_trap(SYSERRNO, NULL);
56
57	if (ret > 0 && ret < INT_MAX)
58		return -ret;
59	return -EIO;
60}
61
62long smh_open(const char *fname, enum smh_open_mode mode)
63{
64	long fd;
65	struct smh_open_s {
66		const char *fname;
67		unsigned long mode;
68		size_t len;
69	} open;
70
71	debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
72
73	open.fname = fname;
74	open.len = strlen(fname);
75	open.mode = mode;
76
77	/* Open the file on the host */
78	fd = smh_trap(SYSOPEN, &open);
79	if (fd == -1)
80		return smh_errno();
81	return fd;
82}
83
84/**
85 * struct smg_rdwr_s - Arguments for read and write
86 * @fd: A file descriptor returned from smh_open()
87 * @memp: Pointer to a buffer of memory of at least @len bytes
88 * @len: The number of bytes to read or write
89 */
90struct smh_rdwr_s {
91	long fd;
92	void *memp;
93	size_t len;
94};
95
96long smh_read(long fd, void *memp, size_t len)
97{
98	long ret;
99	struct smh_rdwr_s read;
100
101	debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
102
103	read.fd = fd;
104	read.memp = memp;
105	read.len = len;
106
107	ret = smh_trap(SYSREAD, &read);
108	if (ret < 0)
109		return smh_errno();
110	return len - ret;
111}
112
113long smh_write(long fd, const void *memp, size_t len, ulong *written)
114{
115	long ret;
116	struct smh_rdwr_s write;
117
118	debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
119
120	write.fd = fd;
121	write.memp = (void *)memp;
122	write.len = len;
123
124	ret = smh_trap(SYSWRITE, &write);
125	*written = len - ret;
126	if (ret)
127		return smh_errno();
128	return 0;
129}
130
131long smh_close(long fd)
132{
133	long ret;
134
135	debug("%s: fd %ld\n", __func__, fd);
136
137	ret = smh_trap(SYSCLOSE, &fd);
138	if (ret == -1)
139		return smh_errno();
140	return 0;
141}
142
143long smh_flen(long fd)
144{
145	long ret;
146
147	debug("%s: fd %ld\n", __func__, fd);
148
149	ret = smh_trap(SYSFLEN, &fd);
150	if (ret == -1)
151		return smh_errno();
152	return ret;
153}
154
155long smh_seek(long fd, long pos)
156{
157	long ret;
158	struct smh_seek_s {
159		long fd;
160		long pos;
161	} seek;
162
163	debug("%s: fd %ld pos %ld\n", __func__, fd, pos);
164
165	seek.fd = fd;
166	seek.pos = pos;
167
168	ret = smh_trap(SYSSEEK, &seek);
169	if (ret)
170		return smh_errno();
171	return 0;
172}
173
174int smh_getc(void)
175{
176	return smh_trap(SYSREADC, NULL);
177}
178
179void smh_putc(char ch)
180{
181	smh_trap(SYSWRITEC, &ch);
182}
183
184void smh_puts(const char *s)
185{
186	smh_trap(SYSWRITE0, (char *)s);
187}
188