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 22 static 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 28 static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version); 29 30 static 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 38 static 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 47 static struct attribute *boot_params_version_attrs[] = { 48 &boot_params_version_attr.attr, 49 NULL, 50 }; 51 52 static struct bin_attribute *boot_params_data_attrs[] = { 53 &boot_params_data_attr, 54 NULL, 55 }; 56 57 static const struct attribute_group boot_params_attr_group = { 58 .attrs = boot_params_version_attrs, 59 .bin_attrs = boot_params_data_attrs, 60 }; 61 62 static 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 70 static 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 92 static int __init get_setup_data_size(int nr, size_t *size) 93 { 94 int i = 0; 95 struct setup_data *data; 96 u64 pa_data = boot_params.hdr.setup_data; 97 98 while (pa_data) { 99 data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); 100 if (!data) 101 return -ENOMEM; 102 if (nr == i) { 103 *size = data->len; 104 memunmap(data); 105 return 0; 106 } 107 108 pa_data = data->next; 109 memunmap(data); 110 i++; 111 } 112 return -EINVAL; 113 } 114 115 static ssize_t type_show(struct kobject *kobj, 116 struct kobj_attribute *attr, char *buf) 117 { 118 int nr, ret; 119 u64 paddr; 120 struct setup_data *data; 121 122 ret = kobj_to_setup_data_nr(kobj, &nr); 123 if (ret) 124 return ret; 125 126 ret = get_setup_data_paddr(nr, &paddr); 127 if (ret) 128 return ret; 129 data = memremap(paddr, sizeof(*data), MEMREMAP_WB); 130 if (!data) 131 return -ENOMEM; 132 133 ret = sprintf(buf, "0x%x\n", data->type); 134 memunmap(data); 135 return ret; 136 } 137 138 static ssize_t setup_data_data_read(struct file *fp, 139 struct kobject *kobj, 140 struct bin_attribute *bin_attr, 141 char *buf, 142 loff_t off, size_t count) 143 { 144 int nr, ret = 0; 145 u64 paddr; 146 struct setup_data *data; 147 void *p; 148 149 ret = kobj_to_setup_data_nr(kobj, &nr); 150 if (ret) 151 return ret; 152 153 ret = get_setup_data_paddr(nr, &paddr); 154 if (ret) 155 return ret; 156 data = memremap(paddr, sizeof(*data), MEMREMAP_WB); 157 if (!data) 158 return -ENOMEM; 159 160 if (off > data->len) { 161 ret = -EINVAL; 162 goto out; 163 } 164 165 if (count > data->len - off) 166 count = data->len - off; 167 168 if (!count) 169 goto out; 170 171 ret = count; 172 p = memremap(paddr + sizeof(*data), data->len, MEMREMAP_WB); 173 if (!p) { 174 ret = -ENOMEM; 175 goto out; 176 } 177 memcpy(buf, p + off, count); 178 memunmap(p); 179 out: 180 memunmap(data); 181 return ret; 182 } 183 184 static struct kobj_attribute type_attr = __ATTR_RO(type); 185 186 static struct bin_attribute data_attr __ro_after_init = { 187 .attr = { 188 .name = "data", 189 .mode = S_IRUGO, 190 }, 191 .read = setup_data_data_read, 192 }; 193 194 static struct attribute *setup_data_type_attrs[] = { 195 &type_attr.attr, 196 NULL, 197 }; 198 199 static struct bin_attribute *setup_data_data_attrs[] = { 200 &data_attr, 201 NULL, 202 }; 203 204 static const struct attribute_group setup_data_attr_group = { 205 .attrs = setup_data_type_attrs, 206 .bin_attrs = setup_data_data_attrs, 207 }; 208 209 static int __init create_setup_data_node(struct kobject *parent, 210 struct kobject **kobjp, int nr) 211 { 212 int ret = 0; 213 size_t size; 214 struct kobject *kobj; 215 char name[16]; /* should be enough for setup_data nodes numbers */ 216 snprintf(name, 16, "%d", nr); 217 218 kobj = kobject_create_and_add(name, parent); 219 if (!kobj) 220 return -ENOMEM; 221 222 ret = get_setup_data_size(nr, &size); 223 if (ret) 224 goto out_kobj; 225 226 data_attr.size = size; 227 ret = sysfs_create_group(kobj, &setup_data_attr_group); 228 if (ret) 229 goto out_kobj; 230 *kobjp = kobj; 231 232 return 0; 233 out_kobj: 234 kobject_put(kobj); 235 return ret; 236 } 237 238 static void __init cleanup_setup_data_node(struct kobject *kobj) 239 { 240 sysfs_remove_group(kobj, &setup_data_attr_group); 241 kobject_put(kobj); 242 } 243 244 static int __init get_setup_data_total_num(u64 pa_data, int *nr) 245 { 246 int ret = 0; 247 struct setup_data *data; 248 249 *nr = 0; 250 while (pa_data) { 251 *nr += 1; 252 data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); 253 if (!data) { 254 ret = -ENOMEM; 255 goto out; 256 } 257 pa_data = data->next; 258 memunmap(data); 259 } 260 261 out: 262 return ret; 263 } 264 265 static int __init create_setup_data_nodes(struct kobject *parent) 266 { 267 struct kobject *setup_data_kobj, **kobjp; 268 u64 pa_data; 269 int i, j, nr, ret = 0; 270 271 pa_data = boot_params.hdr.setup_data; 272 if (!pa_data) 273 return 0; 274 275 setup_data_kobj = kobject_create_and_add("setup_data", parent); 276 if (!setup_data_kobj) { 277 ret = -ENOMEM; 278 goto out; 279 } 280 281 ret = get_setup_data_total_num(pa_data, &nr); 282 if (ret) 283 goto out_setup_data_kobj; 284 285 kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL); 286 if (!kobjp) { 287 ret = -ENOMEM; 288 goto out_setup_data_kobj; 289 } 290 291 for (i = 0; i < nr; i++) { 292 ret = create_setup_data_node(setup_data_kobj, kobjp + i, i); 293 if (ret) 294 goto out_clean_nodes; 295 } 296 297 kfree(kobjp); 298 return 0; 299 300 out_clean_nodes: 301 for (j = i - 1; j >= 0; j--) 302 cleanup_setup_data_node(*(kobjp + j)); 303 kfree(kobjp); 304 out_setup_data_kobj: 305 kobject_put(setup_data_kobj); 306 out: 307 return ret; 308 } 309 310 static int __init boot_params_ksysfs_init(void) 311 { 312 int ret; 313 struct kobject *boot_params_kobj; 314 315 boot_params_kobj = kobject_create_and_add("boot_params", 316 kernel_kobj); 317 if (!boot_params_kobj) { 318 ret = -ENOMEM; 319 goto out; 320 } 321 322 ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group); 323 if (ret) 324 goto out_boot_params_kobj; 325 326 ret = create_setup_data_nodes(boot_params_kobj); 327 if (ret) 328 goto out_create_group; 329 330 return 0; 331 out_create_group: 332 sysfs_remove_group(boot_params_kobj, &boot_params_attr_group); 333 out_boot_params_kobj: 334 kobject_put(boot_params_kobj); 335 out: 336 return ret; 337 } 338 339 arch_initcall(boot_params_ksysfs_init); 340