|
| 1 | +#include <errno.h> |
| 2 | +#include <fcntl.h> |
| 3 | +#include <pthread.h> |
| 4 | +#include <stdbool.h> |
1 | 5 | #include <stdint.h>
|
2 | 6 | #include <string.h>
|
| 7 | +#include <sys/ioctl.h> |
| 8 | +#include <sys/stat.h> |
3 | 9 | #include "internal.h"
|
4 | 10 | #include "librtas.h"
|
| 11 | +#include "papr-sysparm.h" |
| 12 | + |
| 13 | +static const char sysparm_devpath[] = "/dev/papr-sysparm"; |
5 | 14 |
|
6 | 15 | /**
|
7 | 16 | * rtas_get_sysparm
|
|
15 | 24 | * @param data reference to buffer to return parameter in
|
16 | 25 | * @return 0 on success, !0 otherwise
|
17 | 26 | */
|
18 |
| -int rtas_get_sysparm(unsigned int parameter, unsigned int length, char *data) |
| 27 | +static int |
| 28 | +get_sysparm_syscall_fallback(unsigned int parameter, unsigned int length, char *data) |
19 | 29 | {
|
20 | 30 | uint32_t kernbuf_pa;
|
21 | 31 | void *kernbuf;
|
@@ -49,7 +59,8 @@ int rtas_get_sysparm(unsigned int parameter, unsigned int length, char *data)
|
49 | 59 | * @param data
|
50 | 60 | * @return 0 on success, !0 otherwise
|
51 | 61 | */
|
52 |
| -int rtas_set_sysparm(unsigned int parameter, char *data) |
| 62 | +static int |
| 63 | +set_sysparm_syscall_fallback(unsigned int parameter, char *data) |
53 | 64 | {
|
54 | 65 | uint32_t kernbuf_pa;
|
55 | 66 | void *kernbuf;
|
@@ -86,3 +97,132 @@ int rtas_set_sysparm(unsigned int parameter, char *data)
|
86 | 97 | dbg("(%u, %p) = %d\n", parameter, data, rc ? rc : status);
|
87 | 98 | return rc ? rc : status;
|
88 | 99 | }
|
| 100 | + |
| 101 | +static bool sysparm_can_use_chardev(void) |
| 102 | +{ |
| 103 | + struct stat statbuf; |
| 104 | + |
| 105 | + if (stat(sysparm_devpath, &statbuf)) |
| 106 | + return false; |
| 107 | + |
| 108 | + if (!S_ISCHR(statbuf.st_mode)) |
| 109 | + return false; |
| 110 | + |
| 111 | + if (close(open(sysparm_devpath, O_RDONLY))) |
| 112 | + return false; |
| 113 | + |
| 114 | + return true; |
| 115 | +} |
| 116 | + |
| 117 | +/* |
| 118 | + * Only to be used when converting an actual error from a syscall. |
| 119 | + */ |
| 120 | +static int chardev_backconvert_errno(int saved_errno) |
| 121 | +{ |
| 122 | + const struct { |
| 123 | + int linux_errno; |
| 124 | + int rtas_status; |
| 125 | + } map[] = { |
| 126 | +#define errno_to_status(e, s) { .linux_errno = (e), .rtas_status = (s), } |
| 127 | + errno_to_status(EINVAL, -9999), |
| 128 | + errno_to_status(EPERM, -9002), |
| 129 | + errno_to_status(EOPNOTSUPP, -3), |
| 130 | + errno_to_status(EIO, -1), |
| 131 | + errno_to_status(EFAULT, -1), |
| 132 | +#undef errno_to_status |
| 133 | + }; |
| 134 | + |
| 135 | + for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); ++i) |
| 136 | + if (map[i].linux_errno == saved_errno) |
| 137 | + return map[i].rtas_status; |
| 138 | + return -1; |
| 139 | +} |
| 140 | + |
| 141 | +static int get_sysparm_chardev(unsigned int parameter, unsigned int length, char *data) |
| 142 | +{ |
| 143 | + const int fd = open(sysparm_devpath, O_RDWR); |
| 144 | + struct papr_sysparm_io_block buf = { |
| 145 | + .parameter = parameter, |
| 146 | + }; |
| 147 | + |
| 148 | + if (fd < 0) { |
| 149 | + /* |
| 150 | + * Really shouldn't get here without misconfiguration, |
| 151 | + * e.g. removal of /dev/papr-sysparm. Synthesize a |
| 152 | + * hardware/platform error. |
| 153 | + */ |
| 154 | + return -1; |
| 155 | + } |
| 156 | + |
| 157 | + /* |
| 158 | + * It might make sense to have special handling for parameter |
| 159 | + * 60 (OS Service Entitlement Status), which takes input data, |
| 160 | + * but librtas has never handled that one correctly. So ignore |
| 161 | + * it for now and don't copy incoming data into the block we |
| 162 | + * pass to PAPR_SYSPARM_IOC_GET. |
| 163 | + */ |
| 164 | + |
| 165 | + const int res = ioctl(fd, PAPR_SYSPARM_IOC_GET, &buf); |
| 166 | + const int saved_errno = errno; |
| 167 | + (void)close(fd); |
| 168 | + |
| 169 | + if (res != 0) |
| 170 | + return chardev_backconvert_errno(saved_errno); |
| 171 | + |
| 172 | + const uint16_t result_size_msb = htobe16(buf.length); |
| 173 | + memcpy(data, &result_size_msb, sizeof(result_size_msb)); |
| 174 | + length -= sizeof(result_size_msb); |
| 175 | + data += sizeof(result_size_msb); |
| 176 | + |
| 177 | + /* |
| 178 | + * Copy no more than min(@length, sizeof(buf.data)). |
| 179 | + */ |
| 180 | + const size_t copy_size = sizeof(buf.data) < length ? |
| 181 | + sizeof(buf.data) : length; |
| 182 | + memcpy(data, buf.data, copy_size); |
| 183 | + return 0; |
| 184 | +} |
| 185 | + |
| 186 | +static int set_sysparm_chardev(unsigned int parameter, char *data) |
| 187 | +{ |
| 188 | + const int fd = open(sysparm_devpath, O_RDWR); |
| 189 | + struct papr_sysparm_io_block buf = { |
| 190 | + .parameter = parameter, |
| 191 | + .length = (((unsigned char)data[0] << 8) | (unsigned char)data[1]), |
| 192 | + }; |
| 193 | + |
| 194 | + memcpy(buf.data, data + 2, buf.length); |
| 195 | + |
| 196 | + const int res = ioctl(fd, PAPR_SYSPARM_IOC_SET, &buf); |
| 197 | + const int saved_errno = errno; |
| 198 | + (void)close(fd); |
| 199 | + |
| 200 | + return res == 0 ? 0 : chardev_backconvert_errno(saved_errno); |
| 201 | +} |
| 202 | + |
| 203 | +static int (*get_sysparm_fn)(unsigned int parameter, unsigned int length, char *data); |
| 204 | +static int (*set_sysparm_fn)(unsigned int parameter, char *data); |
| 205 | + |
| 206 | +static void sysparm_fn_setup(void) |
| 207 | +{ |
| 208 | + const bool use_chardev = sysparm_can_use_chardev(); |
| 209 | + |
| 210 | + get_sysparm_fn = use_chardev ? |
| 211 | + get_sysparm_chardev : get_sysparm_syscall_fallback; |
| 212 | + set_sysparm_fn = use_chardev ? |
| 213 | + set_sysparm_chardev : set_sysparm_syscall_fallback; |
| 214 | +} |
| 215 | + |
| 216 | +static pthread_once_t sysparm_fn_setup_once = PTHREAD_ONCE_INIT; |
| 217 | + |
| 218 | +int rtas_get_sysparm(unsigned int parameter, unsigned int length, char *data) |
| 219 | +{ |
| 220 | + pthread_once(&sysparm_fn_setup_once, sysparm_fn_setup); |
| 221 | + return get_sysparm_fn(parameter, length, data); |
| 222 | +} |
| 223 | + |
| 224 | +int rtas_set_sysparm(unsigned int parameter, char *data) |
| 225 | +{ |
| 226 | + pthread_once(&sysparm_fn_setup_once, sysparm_fn_setup); |
| 227 | + return set_sysparm_fn(parameter, data); |
| 228 | +} |
0 commit comments