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 <linux/limits.h> 12#include <stdio.h> 13#include <string.h> 14#include <sys/syscall.h> 15#include <sys/types.h> 16#include <unistd.h> 17#include <stdarg.h> 18#include <stdlib.h> 19#include <fcntl.h> 20#include <errno.h> 21#include "utils.h" 22 23#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 24#define cpu_to_be32(x) bswap_32(x) 25#define be32_to_cpu(x) bswap_32(x) 26#else 27#define cpu_to_be32(x) (x) 28#define be32_to_cpu(x) (x) 29#endif 30 31#define RTAS_IO_ASSERT -1098 /* Unexpected I/O Error */ 32#define RTAS_UNKNOWN_OP -1099 /* No Firmware Implementation of Function */ 33#define BLOCK_SIZE 4096 34#define PAGE_SIZE 4096 35#define MAX_PAGES 64 36 37static const char *ofdt_rtas_path = "/proc/device-tree/rtas"; 38 39typedef __be32 uint32_t; 40struct rtas_args { 41 __be32 token; 42 __be32 nargs; 43 __be32 nret; 44 __be32 args[16]; 45 __be32 *rets; /* Pointer to return values in args[]. */ 46}; 47 48struct region { 49 uint64_t addr; 50 uint32_t size; 51 struct region *next; 52}; 53 54static int get_property(const char *prop_path, const char *prop_name, 55 char **prop_val, size_t *prop_len) 56{ 57 char path[PATH_MAX]; 58 59 int len = snprintf(path, sizeof(path), "%s/%s", prop_path, prop_name); 60 if (len < 0 || len >= sizeof(path)) 61 return -ENOMEM; 62 63 return read_file_alloc(path, prop_val, prop_len); 64} 65 66int rtas_token(const char *call_name) 67{ 68 char *prop_buf = NULL; 69 size_t len; 70 int rc; 71 72 rc = get_property(ofdt_rtas_path, call_name, &prop_buf, &len); 73 if (rc < 0) { 74 rc = RTAS_UNKNOWN_OP; 75 goto err; 76 } 77 78 rc = be32_to_cpu(*(int *)prop_buf); 79 80err: 81 free(prop_buf); 82 return rc; 83} 84 85static int read_kregion_bounds(struct region *kregion) 86{ 87 char *buf; 88 int err; 89 90 err = read_file_alloc("/proc/ppc64/rtas/rmo_buffer", &buf, NULL); 91 if (err) { 92 perror("Could not open rmo_buffer file"); 93 return RTAS_IO_ASSERT; 94 } 95 96 sscanf(buf, "%" SCNx64 " %x", &kregion->addr, &kregion->size); 97 free(buf); 98 99 if (!(kregion->size && kregion->addr) || 100 (kregion->size > (PAGE_SIZE * MAX_PAGES))) { 101 printf("Unexpected kregion bounds\n"); 102 return RTAS_IO_ASSERT; 103 } 104 105 return 0; 106} 107 108static int rtas_call(const char *name, int nargs, 109 int nrets, ...) 110{ 111 struct rtas_args args; 112 __be32 *rets[16]; 113 int i, rc, token; 114 va_list ap; 115 116 va_start(ap, nrets); 117 118 token = rtas_token(name); 119 if (token == RTAS_UNKNOWN_OP) { 120 // We don't care if the call doesn't exist 121 printf("call '%s' not available, skipping...", name); 122 rc = RTAS_UNKNOWN_OP; 123 goto err; 124 } 125 126 args.token = cpu_to_be32(token); 127 args.nargs = cpu_to_be32(nargs); 128 args.nret = cpu_to_be32(nrets); 129 130 for (i = 0; i < nargs; i++) 131 args.args[i] = (__be32) va_arg(ap, unsigned long); 132 133 for (i = 0; i < nrets; i++) 134 rets[i] = (__be32 *) va_arg(ap, unsigned long); 135 136 rc = syscall(__NR_rtas, &args); 137 if (rc) { 138 rc = -errno; 139 goto err; 140 } 141 142 if (nrets) { 143 *(rets[0]) = be32_to_cpu(args.args[nargs]); 144 145 for (i = 1; i < nrets; i++) { 146 *(rets[i]) = args.args[nargs + i]; 147 } 148 } 149 150err: 151 va_end(ap); 152 return rc; 153} 154 155static int test(void) 156{ 157 struct region rmo_region; 158 uint32_t rmo_start; 159 uint32_t rmo_end; 160 __be32 rets[1]; 161 int rc; 162 163 // Test a legitimate harmless call 164 // Expected: call succeeds 165 printf("Test a permitted call, no parameters... "); 166 rc = rtas_call("get-time-of-day", 0, 1, rets); 167 printf("rc: %d\n", rc); 168 FAIL_IF(rc != 0 && rc != RTAS_UNKNOWN_OP); 169 170 // Test a prohibited call 171 // Expected: call returns -EINVAL 172 printf("Test a prohibited call... "); 173 rc = rtas_call("nvram-fetch", 0, 1, rets); 174 printf("rc: %d\n", rc); 175 FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP); 176 177 // Get RMO 178 rc = read_kregion_bounds(&rmo_region); 179 if (rc) { 180 printf("Couldn't read RMO region bounds, skipping remaining cases\n"); 181 return 0; 182 } 183 rmo_start = rmo_region.addr; 184 rmo_end = rmo_start + rmo_region.size - 1; 185 printf("RMO range: %08x - %08x\n", rmo_start, rmo_end); 186 187 // Test a permitted call, user-supplied size, buffer inside RMO 188 // Expected: call succeeds 189 printf("Test a permitted call, user-supplied size, buffer inside RMO... "); 190 rc = rtas_call("ibm,get-system-parameter", 3, 1, 0, cpu_to_be32(rmo_start), 191 cpu_to_be32(rmo_end - rmo_start + 1), rets); 192 printf("rc: %d\n", rc); 193 FAIL_IF(rc != 0 && rc != RTAS_UNKNOWN_OP); 194 195 // Test a permitted call, user-supplied size, buffer start outside RMO 196 // Expected: call returns -EINVAL 197 printf("Test a permitted call, user-supplied size, buffer start outside RMO... "); 198 rc = rtas_call("ibm,get-system-parameter", 3, 1, 0, cpu_to_be32(rmo_end + 1), 199 cpu_to_be32(4000), rets); 200 printf("rc: %d\n", rc); 201 FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP); 202 203 // Test a permitted call, user-supplied size, buffer end outside RMO 204 // Expected: call returns -EINVAL 205 printf("Test a permitted call, user-supplied size, buffer end outside RMO... "); 206 rc = rtas_call("ibm,get-system-parameter", 3, 1, 0, cpu_to_be32(rmo_start), 207 cpu_to_be32(rmo_end - rmo_start + 2), rets); 208 printf("rc: %d\n", rc); 209 FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP); 210 211 // Test a permitted call, fixed size, buffer end outside RMO 212 // Expected: call returns -EINVAL 213 printf("Test a permitted call, fixed size, buffer end outside RMO... "); 214 rc = rtas_call("ibm,configure-connector", 2, 1, cpu_to_be32(rmo_end - 4000), 0, rets); 215 printf("rc: %d\n", rc); 216 FAIL_IF(rc != -EINVAL && rc != RTAS_UNKNOWN_OP); 217 218 return 0; 219} 220 221int main(int argc, char *argv[]) 222{ 223 return test_harness(test, "rtas_filter"); 224} 225