ksysfs.c (8057B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Architecture specific sysfs attributes in /sys/kernel 4 * 5 * Copyright (C) 2007, Intel Corp. 6 * Huang Ying <ying.huang@intel.com> 7 * Copyright (C) 2013, 2013 Red Hat, Inc. 8 * Dave Young <dyoung@redhat.com> 9 */ 10 11#include <linux/kobject.h> 12#include <linux/string.h> 13#include <linux/sysfs.h> 14#include <linux/init.h> 15#include <linux/stat.h> 16#include <linux/slab.h> 17#include <linux/mm.h> 18#include <linux/io.h> 19 20#include <asm/setup.h> 21 22static ssize_t version_show(struct kobject *kobj, 23 struct kobj_attribute *attr, char *buf) 24{ 25 return sprintf(buf, "0x%04x\n", boot_params.hdr.version); 26} 27 28static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version); 29 30static ssize_t boot_params_data_read(struct file *fp, struct kobject *kobj, 31 struct bin_attribute *bin_attr, 32 char *buf, loff_t off, size_t count) 33{ 34 memcpy(buf, (void *)&boot_params + off, count); 35 return count; 36} 37 38static struct bin_attribute boot_params_data_attr = { 39 .attr = { 40 .name = "data", 41 .mode = S_IRUGO, 42 }, 43 .read = boot_params_data_read, 44 .size = sizeof(boot_params), 45}; 46 47static struct attribute *boot_params_version_attrs[] = { 48 &boot_params_version_attr.attr, 49 NULL, 50}; 51 52static struct bin_attribute *boot_params_data_attrs[] = { 53 &boot_params_data_attr, 54 NULL, 55}; 56 57static const struct attribute_group boot_params_attr_group = { 58 .attrs = boot_params_version_attrs, 59 .bin_attrs = boot_params_data_attrs, 60}; 61 62static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr) 63{ 64 const char *name; 65 66 name = kobject_name(kobj); 67 return kstrtoint(name, 10, nr); 68} 69 70static int get_setup_data_paddr(int nr, u64 *paddr) 71{ 72 int i = 0; 73 struct setup_data *data; 74 u64 pa_data = boot_params.hdr.setup_data; 75 76 while (pa_data) { 77 if (nr == i) { 78 *paddr = pa_data; 79 return 0; 80 } 81 data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); 82 if (!data) 83 return -ENOMEM; 84 85 pa_data = data->next; 86 memunmap(data); 87 i++; 88 } 89 return -EINVAL; 90} 91 92static int __init get_setup_data_size(int nr, size_t *size) 93{ 94 u64 pa_data = boot_params.hdr.setup_data, pa_next; 95 struct setup_indirect *indirect; 96 struct setup_data *data; 97 int i = 0; 98 u32 len; 99 100 while (pa_data) { 101 data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); 102 if (!data) 103 return -ENOMEM; 104 pa_next = data->next; 105 106 if (nr == i) { 107 if (data->type == SETUP_INDIRECT) { 108 len = sizeof(*data) + data->len; 109 memunmap(data); 110 data = memremap(pa_data, len, MEMREMAP_WB); 111 if (!data) 112 return -ENOMEM; 113 114 indirect = (struct setup_indirect *)data->data; 115 116 if (indirect->type != SETUP_INDIRECT) 117 *size = indirect->len; 118 else 119 *size = data->len; 120 } else { 121 *size = data->len; 122 } 123 124 memunmap(data); 125 return 0; 126 } 127 128 pa_data = pa_next; 129 memunmap(data); 130 i++; 131 } 132 return -EINVAL; 133} 134 135static ssize_t type_show(struct kobject *kobj, 136 struct kobj_attribute *attr, char *buf) 137{ 138 struct setup_indirect *indirect; 139 struct setup_data *data; 140 int nr, ret; 141 u64 paddr; 142 u32 len; 143 144 ret = kobj_to_setup_data_nr(kobj, &nr); 145 if (ret) 146 return ret; 147 148 ret = get_setup_data_paddr(nr, &paddr); 149 if (ret) 150 return ret; 151 data = memremap(paddr, sizeof(*data), MEMREMAP_WB); 152 if (!data) 153 return -ENOMEM; 154 155 if (data->type == SETUP_INDIRECT) { 156 len = sizeof(*data) + data->len; 157 memunmap(data); 158 data = memremap(paddr, len, MEMREMAP_WB); 159 if (!data) 160 return -ENOMEM; 161 162 indirect = (struct setup_indirect *)data->data; 163 164 ret = sprintf(buf, "0x%x\n", indirect->type); 165 } else { 166 ret = sprintf(buf, "0x%x\n", data->type); 167 } 168 169 memunmap(data); 170 return ret; 171} 172 173static ssize_t setup_data_data_read(struct file *fp, 174 struct kobject *kobj, 175 struct bin_attribute *bin_attr, 176 char *buf, 177 loff_t off, size_t count) 178{ 179 struct setup_indirect *indirect; 180 struct setup_data *data; 181 int nr, ret = 0; 182 u64 paddr, len; 183 void *p; 184 185 ret = kobj_to_setup_data_nr(kobj, &nr); 186 if (ret) 187 return ret; 188 189 ret = get_setup_data_paddr(nr, &paddr); 190 if (ret) 191 return ret; 192 data = memremap(paddr, sizeof(*data), MEMREMAP_WB); 193 if (!data) 194 return -ENOMEM; 195 196 if (data->type == SETUP_INDIRECT) { 197 len = sizeof(*data) + data->len; 198 memunmap(data); 199 data = memremap(paddr, len, MEMREMAP_WB); 200 if (!data) 201 return -ENOMEM; 202 203 indirect = (struct setup_indirect *)data->data; 204 205 if (indirect->type != SETUP_INDIRECT) { 206 paddr = indirect->addr; 207 len = indirect->len; 208 } else { 209 /* 210 * Even though this is technically undefined, return 211 * the data as though it is a normal setup_data struct. 212 * This will at least allow it to be inspected. 213 */ 214 paddr += sizeof(*data); 215 len = data->len; 216 } 217 } else { 218 paddr += sizeof(*data); 219 len = data->len; 220 } 221 222 if (off > len) { 223 ret = -EINVAL; 224 goto out; 225 } 226 227 if (count > len - off) 228 count = len - off; 229 230 if (!count) 231 goto out; 232 233 ret = count; 234 p = memremap(paddr, len, MEMREMAP_WB); 235 if (!p) { 236 ret = -ENOMEM; 237 goto out; 238 } 239 memcpy(buf, p + off, count); 240 memunmap(p); 241out: 242 memunmap(data); 243 return ret; 244} 245 246static struct kobj_attribute type_attr = __ATTR_RO(type); 247 248static struct bin_attribute data_attr __ro_after_init = { 249 .attr = { 250 .name = "data", 251 .mode = S_IRUGO, 252 }, 253 .read = setup_data_data_read, 254}; 255 256static struct attribute *setup_data_type_attrs[] = { 257 &type_attr.attr, 258 NULL, 259}; 260 261static struct bin_attribute *setup_data_data_attrs[] = { 262 &data_attr, 263 NULL, 264}; 265 266static const struct attribute_group setup_data_attr_group = { 267 .attrs = setup_data_type_attrs, 268 .bin_attrs = setup_data_data_attrs, 269}; 270 271static int __init create_setup_data_node(struct kobject *parent, 272 struct kobject **kobjp, int nr) 273{ 274 int ret = 0; 275 size_t size; 276 struct kobject *kobj; 277 char name[16]; /* should be enough for setup_data nodes numbers */ 278 snprintf(name, 16, "%d", nr); 279 280 kobj = kobject_create_and_add(name, parent); 281 if (!kobj) 282 return -ENOMEM; 283 284 ret = get_setup_data_size(nr, &size); 285 if (ret) 286 goto out_kobj; 287 288 data_attr.size = size; 289 ret = sysfs_create_group(kobj, &setup_data_attr_group); 290 if (ret) 291 goto out_kobj; 292 *kobjp = kobj; 293 294 return 0; 295out_kobj: 296 kobject_put(kobj); 297 return ret; 298} 299 300static void __init cleanup_setup_data_node(struct kobject *kobj) 301{ 302 sysfs_remove_group(kobj, &setup_data_attr_group); 303 kobject_put(kobj); 304} 305 306static int __init get_setup_data_total_num(u64 pa_data, int *nr) 307{ 308 int ret = 0; 309 struct setup_data *data; 310 311 *nr = 0; 312 while (pa_data) { 313 *nr += 1; 314 data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); 315 if (!data) { 316 ret = -ENOMEM; 317 goto out; 318 } 319 pa_data = data->next; 320 memunmap(data); 321 } 322 323out: 324 return ret; 325} 326 327static int __init create_setup_data_nodes(struct kobject *parent) 328{ 329 struct kobject *setup_data_kobj, **kobjp; 330 u64 pa_data; 331 int i, j, nr, ret = 0; 332 333 pa_data = boot_params.hdr.setup_data; 334 if (!pa_data) 335 return 0; 336 337 setup_data_kobj = kobject_create_and_add("setup_data", parent); 338 if (!setup_data_kobj) { 339 ret = -ENOMEM; 340 goto out; 341 } 342 343 ret = get_setup_data_total_num(pa_data, &nr); 344 if (ret) 345 goto out_setup_data_kobj; 346 347 kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL); 348 if (!kobjp) { 349 ret = -ENOMEM; 350 goto out_setup_data_kobj; 351 } 352 353 for (i = 0; i < nr; i++) { 354 ret = create_setup_data_node(setup_data_kobj, kobjp + i, i); 355 if (ret) 356 goto out_clean_nodes; 357 } 358 359 kfree(kobjp); 360 return 0; 361 362out_clean_nodes: 363 for (j = i - 1; j >= 0; j--) 364 cleanup_setup_data_node(*(kobjp + j)); 365 kfree(kobjp); 366out_setup_data_kobj: 367 kobject_put(setup_data_kobj); 368out: 369 return ret; 370} 371 372static int __init boot_params_ksysfs_init(void) 373{ 374 int ret; 375 struct kobject *boot_params_kobj; 376 377 boot_params_kobj = kobject_create_and_add("boot_params", 378 kernel_kobj); 379 if (!boot_params_kobj) { 380 ret = -ENOMEM; 381 goto out; 382 } 383 384 ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group); 385 if (ret) 386 goto out_boot_params_kobj; 387 388 ret = create_setup_data_nodes(boot_params_kobj); 389 if (ret) 390 goto out_create_group; 391 392 return 0; 393out_create_group: 394 sysfs_remove_group(boot_params_kobj, &boot_params_attr_group); 395out_boot_params_kobj: 396 kobject_put(boot_params_kobj); 397out: 398 return ret; 399} 400 401arch_initcall(boot_params_ksysfs_init);