1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PowerNV system parameter code 4 * 5 * Copyright (C) 2013 IBM 6 */ 7 8 #include <linux/kobject.h> 9 #include <linux/mutex.h> 10 #include <linux/slab.h> 11 #include <linux/of.h> 12 #include <linux/gfp.h> 13 #include <linux/stat.h> 14 #include <asm/opal.h> 15 16 #define MAX_PARAM_DATA_LEN 64 17 18 static DEFINE_MUTEX(opal_sysparam_mutex); 19 static struct kobject *sysparam_kobj; 20 static void *param_data_buf; 21 22 struct param_attr { 23 struct list_head list; 24 u32 param_id; 25 u32 param_size; 26 struct kobj_attribute kobj_attr; 27 }; 28 29 static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer) 30 { 31 struct opal_msg msg; 32 ssize_t ret; 33 int token; 34 35 token = opal_async_get_token_interruptible(); 36 if (token < 0) { 37 if (token != -ERESTARTSYS) 38 pr_err("%s: Couldn't get the token, returning\n", 39 __func__); 40 ret = token; 41 goto out; 42 } 43 44 ret = opal_get_param(token, param_id, (u64)buffer, length); 45 if (ret != OPAL_ASYNC_COMPLETION) { 46 ret = opal_error_code(ret); 47 goto out_token; 48 } 49 50 ret = opal_async_wait_response(token, &msg); 51 if (ret) { 52 pr_err("%s: Failed to wait for the async response, %zd\n", 53 __func__, ret); 54 goto out_token; 55 } 56 57 ret = opal_error_code(opal_get_async_rc(msg)); 58 59 out_token: 60 opal_async_release_token(token); 61 out: 62 return ret; 63 } 64 65 static int opal_set_sys_param(u32 param_id, u32 length, void *buffer) 66 { 67 struct opal_msg msg; 68 int ret, token; 69 70 token = opal_async_get_token_interruptible(); 71 if (token < 0) { 72 if (token != -ERESTARTSYS) 73 pr_err("%s: Couldn't get the token, returning\n", 74 __func__); 75 ret = token; 76 goto out; 77 } 78 79 ret = opal_set_param(token, param_id, (u64)buffer, length); 80 81 if (ret != OPAL_ASYNC_COMPLETION) { 82 ret = opal_error_code(ret); 83 goto out_token; 84 } 85 86 ret = opal_async_wait_response(token, &msg); 87 if (ret) { 88 pr_err("%s: Failed to wait for the async response, %d\n", 89 __func__, ret); 90 goto out_token; 91 } 92 93 ret = opal_error_code(opal_get_async_rc(msg)); 94 95 out_token: 96 opal_async_release_token(token); 97 out: 98 return ret; 99 } 100 101 static ssize_t sys_param_show(struct kobject *kobj, 102 struct kobj_attribute *kobj_attr, char *buf) 103 { 104 struct param_attr *attr = container_of(kobj_attr, struct param_attr, 105 kobj_attr); 106 ssize_t ret; 107 108 mutex_lock(&opal_sysparam_mutex); 109 ret = opal_get_sys_param(attr->param_id, attr->param_size, 110 param_data_buf); 111 if (ret) 112 goto out; 113 114 memcpy(buf, param_data_buf, attr->param_size); 115 116 ret = attr->param_size; 117 out: 118 mutex_unlock(&opal_sysparam_mutex); 119 return ret; 120 } 121 122 static ssize_t sys_param_store(struct kobject *kobj, 123 struct kobj_attribute *kobj_attr, const char *buf, size_t count) 124 { 125 struct param_attr *attr = container_of(kobj_attr, struct param_attr, 126 kobj_attr); 127 ssize_t ret; 128 129 /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */ 130 if (count > MAX_PARAM_DATA_LEN) 131 count = MAX_PARAM_DATA_LEN; 132 133 mutex_lock(&opal_sysparam_mutex); 134 memcpy(param_data_buf, buf, count); 135 ret = opal_set_sys_param(attr->param_id, attr->param_size, 136 param_data_buf); 137 mutex_unlock(&opal_sysparam_mutex); 138 if (!ret) 139 ret = count; 140 return ret; 141 } 142 143 void __init opal_sys_param_init(void) 144 { 145 struct device_node *sysparam; 146 struct param_attr *attr; 147 u32 *id, *size; 148 int count, i; 149 u8 *perm; 150 151 if (!opal_kobj) { 152 pr_warn("SYSPARAM: opal kobject is not available\n"); 153 goto out; 154 } 155 156 /* Some systems do not use sysparams; this is not an error */ 157 sysparam = of_find_node_by_path("/ibm,opal/sysparams"); 158 if (!sysparam) 159 goto out; 160 161 if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) { 162 pr_err("SYSPARAM: Opal sysparam node not compatible\n"); 163 goto out_node_put; 164 } 165 166 sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj); 167 if (!sysparam_kobj) { 168 pr_err("SYSPARAM: Failed to create sysparam kobject\n"); 169 goto out_node_put; 170 } 171 172 /* Allocate big enough buffer for any get/set transactions */ 173 param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL); 174 if (!param_data_buf) { 175 pr_err("SYSPARAM: Failed to allocate memory for param data " 176 "buf\n"); 177 goto out_kobj_put; 178 } 179 180 /* Number of parameters exposed through DT */ 181 count = of_property_count_strings(sysparam, "param-name"); 182 if (count < 0) { 183 pr_err("SYSPARAM: No string found of property param-name in " 184 "the node %pOFn\n", sysparam); 185 goto out_param_buf; 186 } 187 188 id = kcalloc(count, sizeof(*id), GFP_KERNEL); 189 if (!id) { 190 pr_err("SYSPARAM: Failed to allocate memory to read parameter " 191 "id\n"); 192 goto out_param_buf; 193 } 194 195 size = kcalloc(count, sizeof(*size), GFP_KERNEL); 196 if (!size) { 197 pr_err("SYSPARAM: Failed to allocate memory to read parameter " 198 "size\n"); 199 goto out_free_id; 200 } 201 202 perm = kcalloc(count, sizeof(*perm), GFP_KERNEL); 203 if (!perm) { 204 pr_err("SYSPARAM: Failed to allocate memory to read supported " 205 "action on the parameter"); 206 goto out_free_size; 207 } 208 209 if (of_property_read_u32_array(sysparam, "param-id", id, count)) { 210 pr_err("SYSPARAM: Missing property param-id in the DT\n"); 211 goto out_free_perm; 212 } 213 214 if (of_property_read_u32_array(sysparam, "param-len", size, count)) { 215 pr_err("SYSPARAM: Missing property param-len in the DT\n"); 216 goto out_free_perm; 217 } 218 219 220 if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) { 221 pr_err("SYSPARAM: Missing property param-perm in the DT\n"); 222 goto out_free_perm; 223 } 224 225 attr = kcalloc(count, sizeof(*attr), GFP_KERNEL); 226 if (!attr) { 227 pr_err("SYSPARAM: Failed to allocate memory for parameter " 228 "attributes\n"); 229 goto out_free_perm; 230 } 231 232 /* For each of the parameters, populate the parameter attributes */ 233 for (i = 0; i < count; i++) { 234 if (size[i] > MAX_PARAM_DATA_LEN) { 235 pr_warn("SYSPARAM: Not creating parameter %d as size " 236 "exceeds buffer length\n", i); 237 continue; 238 } 239 240 sysfs_attr_init(&attr[i].kobj_attr.attr); 241 attr[i].param_id = id[i]; 242 attr[i].param_size = size[i]; 243 if (of_property_read_string_index(sysparam, "param-name", i, 244 &attr[i].kobj_attr.attr.name)) 245 continue; 246 247 /* If the parameter is read-only or read-write */ 248 switch (perm[i] & 3) { 249 case OPAL_SYSPARAM_READ: 250 attr[i].kobj_attr.attr.mode = 0444; 251 break; 252 case OPAL_SYSPARAM_WRITE: 253 attr[i].kobj_attr.attr.mode = 0200; 254 break; 255 case OPAL_SYSPARAM_RW: 256 attr[i].kobj_attr.attr.mode = 0644; 257 break; 258 default: 259 break; 260 } 261 262 attr[i].kobj_attr.show = sys_param_show; 263 attr[i].kobj_attr.store = sys_param_store; 264 265 if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) { 266 pr_err("SYSPARAM: Failed to create sysfs file %s\n", 267 attr[i].kobj_attr.attr.name); 268 goto out_free_attr; 269 } 270 } 271 272 kfree(perm); 273 kfree(size); 274 kfree(id); 275 of_node_put(sysparam); 276 return; 277 278 out_free_attr: 279 kfree(attr); 280 out_free_perm: 281 kfree(perm); 282 out_free_size: 283 kfree(size); 284 out_free_id: 285 kfree(id); 286 out_param_buf: 287 kfree(param_data_buf); 288 out_kobj_put: 289 kobject_put(sysparam_kobj); 290 out_node_put: 291 of_node_put(sysparam); 292 out: 293 return; 294 } 295