1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2005-2020 IBM Corporation.
4  *
5  * Includes code from librtas (https://github.com/ibm-power-utilities/librtas/)
6  */
7 
8 #include <byteswap.h>
9 #include <stdint.h>
10 #include <inttypes.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/syscall.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include "utils.h"
21 
22 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
23 #define cpu_to_be32(x)		bswap_32(x)
24 #define be32_to_cpu(x)		bswap_32(x)
25 #else
26 #define cpu_to_be32(x)		(x)
27 #define be32_to_cpu(x)		(x)
28 #endif
29 
30 #define RTAS_IO_ASSERT	-1098	/* Unexpected I/O Error */
31 #define RTAS_UNKNOWN_OP -1099	/* No Firmware Implementation of Function */
32 #define BLOCK_SIZE 4096
33 #define PAGE_SIZE 4096
34 #define MAX_PAGES 64
35 
36 static const char *ofdt_rtas_path = "/proc/device-tree/rtas";
37 
38 typedef __be32 uint32_t;
39 struct rtas_args {
40 	__be32 token;
41 	__be32 nargs;
42 	__be32 nret;
43 	__be32 args[16];
44 	__be32 *rets;	  /* Pointer to return values in args[]. */
45 };
46 
47 struct region {
48 	uint64_t addr;
49 	uint32_t size;
50 	struct region *next;
51 };
52 
read_entire_file(int fd,char ** buf,size_t * len)53 int read_entire_file(int fd, char **buf, size_t *len)
54 {
55 	size_t buf_size = 0;
56 	size_t off = 0;
57 	int rc;
58 
59 	*buf = NULL;
60 	do {
61 		buf_size += BLOCK_SIZE;
62 		if (*buf == NULL)
63 			*buf = malloc(buf_size);
64 		else
65 			*buf = realloc(*buf, buf_size);
66 
67 		if (*buf == NULL)
68 			return -ENOMEM;
69 
70 		rc = read(fd, *buf + off, BLOCK_SIZE);
71 		if (rc < 0)
72 			return -EIO;
73 
74 		off += rc;
75 	} while (rc == BLOCK_SIZE);
76 
77 	if (len)
78 		*len = off;
79 
80 	return 0;
81 }
82 
open_prop_file(const char * prop_path,const char * prop_name,int * fd)83 static int open_prop_file(const char *prop_path, const char *prop_name, int *fd)
84 {
85 	char *path;
86 	int len;
87 
88 	/* allocate enough for two string, a slash and trailing NULL */
89 	len = strlen(prop_path) + strlen(prop_name) + 1 + 1;
90 	path = malloc(len);
91 	if (path == NULL)
92 		return -ENOMEM;
93 
94 	snprintf(path, len, "%s/%s", prop_path, prop_name);
95 
96 	*fd = open(path, O_RDONLY);
97 	free(path);
98 	if (*fd < 0)
99 		return -errno;
100 
101 	return 0;
102 }
103 
get_property(const char * prop_path,const char * prop_name,char ** prop_val,size_t * prop_len)104 static int get_property(const char *prop_path, const char *prop_name,
105 			char **prop_val, size_t *prop_len)
106 {
107 	int rc, fd;
108 
109 	rc = open_prop_file(prop_path, prop_name, &fd);
110 	if (rc)
111 		return rc;
112 
113 	rc = read_entire_file(fd, prop_val, prop_len);
114 	close(fd);
115 
116 	return rc;
117 }
118 
rtas_token(const char * call_name)119 int rtas_token(const char *call_name)
120 {
121 	char *prop_buf = NULL;
122 	size_t len;
123 	int rc;
124 
125 	rc = get_property(ofdt_rtas_path, call_name, &prop_buf, &len);
126 	if (rc < 0) {
127 		rc = RTAS_UNKNOWN_OP;
128 		goto err;
129 	}
130 
131 	rc = be32_to_cpu(*(int *)prop_buf);
132 
133 err:
134 	free(prop_buf);
135 	return rc;
136 }
137 
read_kregion_bounds(struct region * kregion)138 static int read_kregion_bounds(struct region *kregion)
139 {
140 	char *buf;
141 	int fd;
142 	int rc;
143 
144 	fd = open("/proc/ppc64/rtas/rmo_buffer", O_RDONLY);
145 	if (fd < 0) {
146 		printf("Could not open rmo_buffer file\n");
147 		return RTAS_IO_ASSERT;
148 	}
149 
150 	rc = read_entire_file(fd, &buf, NULL);
151 	close(fd);
152 	if (rc) {
153 		free(buf);
154 		return rc;
155 	}
156 
157 	sscanf(buf, "%" SCNx64 " %x", &kregion->addr, &kregion->size);
158 	free(buf);
159 
160 	if (!(kregion->size && kregion->addr) ||
161 	    (kregion->size > (PAGE_SIZE * MAX_PAGES))) {
162 		printf("Unexpected kregion bounds\n");
163 		return RTAS_IO_ASSERT;
164 	}
165 
166 	return 0;
167 }
168 
rtas_call(const char * name,int nargs,int nrets,...)169 static int rtas_call(const char *name, int nargs,
170 		     int nrets, ...)
171 {
172 	struct rtas_args args;
173 	__be32 *rets[16];
174 	int i, rc, token;
175 	va_list ap;
176 
177 	va_start(ap, nrets);
178 
179 	token = rtas_token(name);
180 	if (token == RTAS_UNKNOWN_OP) {
181 		// We don't care if the call doesn't exist
182 		printf("call '%s' not available, skipping...", name);
183 		rc = RTAS_UNKNOWN_OP;
184 		goto err;
185 	}
186 
187 	args.token = cpu_to_be32(token);
188 	args.nargs = cpu_to_be32(nargs);
189 	args.nret = cpu_to_be32(nrets);
190 
191 	for (i = 0; i < nargs; i++)
192 		args.args[i] = (__be32) va_arg(ap, unsigned long);
193 
194 	for (i = 0; i < nrets; i++)
195 		rets[i] = (__be32 *) va_arg(ap, unsigned long);
196 
197 	rc = syscall(__NR_rtas, &args);
198 	if (rc) {
199 		rc = -errno;
200 		goto err;
201 	}
202 
203 	if (nrets) {
204 		*(rets[0]) = be32_to_cpu(args.args[nargs]);
205 
206 		for (i = 1; i < nrets; i++) {
207 			*(rets[i]) = args.args[nargs + i];
208 		}
209 	}
210 
211 err:
212 	va_end(ap);
213 	return rc;
214 }
215 
test(void)216 static int test(void)
217 {
218 	struct region rmo_region;
219 	uint32_t rmo_start;
220 	uint32_t rmo_end;
221 	__be32 rets[1];
222 	int rc;
223 
224 	// Test a legitimate harmless call
225 	// Expected: call succeeds
226 	printf("Test a permitted call, no parameters... ");
227 	rc = rtas_call("get-time-of-day", 0, 1, rets);
228 	printf("rc: %d\n", rc);
229 	FAIL_IF(rc != 0 && rc != RTAS_UNKNOWN_OP);
230 
231 	// Test a prohibited call
232 	// Expected: call returns -EINVAL
233 	printf("Test a prohibited call... ");
234 	rc = rtas_call("nvram-fetch", 0, 1, rets);
235 	printf("rc: %d\n", rc);
236 	FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP);
237 
238 	// Get RMO
239 	rc = read_kregion_bounds(&rmo_region);
240 	if (rc) {
241 		printf("Couldn't read RMO region bounds, skipping remaining cases\n");
242 		return 0;
243 	}
244 	rmo_start = rmo_region.addr;
245 	rmo_end = rmo_start + rmo_region.size - 1;
246 	printf("RMO range: %08x - %08x\n", rmo_start, rmo_end);
247 
248 	// Test a permitted call, user-supplied size, buffer inside RMO
249 	// Expected: call succeeds
250 	printf("Test a permitted call, user-supplied size, buffer inside RMO... ");
251 	rc = rtas_call("ibm,get-system-parameter", 3, 1, 0, cpu_to_be32(rmo_start),
252 		       cpu_to_be32(rmo_end - rmo_start + 1), rets);
253 	printf("rc: %d\n", rc);
254 	FAIL_IF(rc != 0 && rc != RTAS_UNKNOWN_OP);
255 
256 	// Test a permitted call, user-supplied size, buffer start outside RMO
257 	// Expected: call returns -EINVAL
258 	printf("Test a permitted call, user-supplied size, buffer start outside RMO... ");
259 	rc = rtas_call("ibm,get-system-parameter", 3, 1, 0, cpu_to_be32(rmo_end + 1),
260 		       cpu_to_be32(4000), rets);
261 	printf("rc: %d\n", rc);
262 	FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP);
263 
264 	// Test a permitted call, user-supplied size, buffer end outside RMO
265 	// Expected: call returns -EINVAL
266 	printf("Test a permitted call, user-supplied size, buffer end outside RMO... ");
267 	rc = rtas_call("ibm,get-system-parameter", 3, 1, 0, cpu_to_be32(rmo_start),
268 		       cpu_to_be32(rmo_end - rmo_start + 2), rets);
269 	printf("rc: %d\n", rc);
270 	FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP);
271 
272 	// Test a permitted call, fixed size, buffer end outside RMO
273 	// Expected: call returns -EINVAL
274 	printf("Test a permitted call, fixed size, buffer end outside RMO... ");
275 	rc = rtas_call("ibm,configure-connector", 2, 1, cpu_to_be32(rmo_end - 4000), 0, rets);
276 	printf("rc: %d\n", rc);
277 	FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP);
278 
279 	return 0;
280 }
281 
main(int argc,char * argv[])282 int main(int argc, char *argv[])
283 {
284 	return test_harness(test, "rtas_filter");
285 }
286