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