Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Add a rtas_filter selftest

Add a selftest to test the basic functionality of CONFIG_RTAS_FILTER.

Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
[mpe: Change rmo_start/end to 32-bit to avoid build errors on ppc64]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200820044512.7543-2-ajd@linux.ibm.com

authored by

Andrew Donnellan and committed by
Michael Ellerman
dc9af82e bd59380c

+286 -1
+1 -1
tools/testing/selftests/powerpc/syscalls/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - TEST_GEN_PROGS := ipc_unmuxed 2 + TEST_GEN_PROGS := ipc_unmuxed rtas_filter 3 3 4 4 CFLAGS += -I../../../../../usr/include 5 5
+285
tools/testing/selftests/powerpc/syscalls/rtas_filter.c
··· 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 282 + int main(int argc, char *argv[]) 283 + { 284 + return test_harness(test, "rtas_filter"); 285 + }