linux/arch/powerpc/kernel/secvar-sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2019 IBM Corporation <nayna@linux.ibm.com>
   4 *
   5 * This code exposes secure variables to user via sysfs
   6 */
   7
   8#define pr_fmt(fmt) "secvar-sysfs: "fmt
   9
  10#include <linux/slab.h>
  11#include <linux/compat.h>
  12#include <linux/string.h>
  13#include <linux/of.h>
  14#include <asm/secvar.h>
  15
  16#define NAME_MAX_SIZE      1024
  17
  18static struct kobject *secvar_kobj;
  19static struct kset *secvar_kset;
  20
  21static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
  22                           char *buf)
  23{
  24        ssize_t rc = 0;
  25        struct device_node *node;
  26        const char *format;
  27
  28        node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
  29        if (!of_device_is_available(node))
  30                return -ENODEV;
  31
  32        rc = of_property_read_string(node, "format", &format);
  33        if (rc)
  34                return rc;
  35
  36        rc = sprintf(buf, "%s\n", format);
  37
  38        of_node_put(node);
  39
  40        return rc;
  41}
  42
  43
  44static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
  45                         char *buf)
  46{
  47        uint64_t dsize;
  48        int rc;
  49
  50        rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
  51        if (rc) {
  52                pr_err("Error retrieving %s variable size %d\n", kobj->name,
  53                       rc);
  54                return rc;
  55        }
  56
  57        return sprintf(buf, "%llu\n", dsize);
  58}
  59
  60static ssize_t data_read(struct file *filep, struct kobject *kobj,
  61                         struct bin_attribute *attr, char *buf, loff_t off,
  62                         size_t count)
  63{
  64        uint64_t dsize;
  65        char *data;
  66        int rc;
  67
  68        rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
  69        if (rc) {
  70                pr_err("Error getting %s variable size %d\n", kobj->name, rc);
  71                return rc;
  72        }
  73        pr_debug("dsize is %llu\n", dsize);
  74
  75        data = kzalloc(dsize, GFP_KERNEL);
  76        if (!data)
  77                return -ENOMEM;
  78
  79        rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, data, &dsize);
  80        if (rc) {
  81                pr_err("Error getting %s variable %d\n", kobj->name, rc);
  82                goto data_fail;
  83        }
  84
  85        rc = memory_read_from_buffer(buf, count, &off, data, dsize);
  86
  87data_fail:
  88        kfree(data);
  89        return rc;
  90}
  91
  92static ssize_t update_write(struct file *filep, struct kobject *kobj,
  93                            struct bin_attribute *attr, char *buf, loff_t off,
  94                            size_t count)
  95{
  96        int rc;
  97
  98        pr_debug("count is %ld\n", count);
  99        rc = secvar_ops->set(kobj->name, strlen(kobj->name) + 1, buf, count);
 100        if (rc) {
 101                pr_err("Error setting the %s variable %d\n", kobj->name, rc);
 102                return rc;
 103        }
 104
 105        return count;
 106}
 107
 108static struct kobj_attribute format_attr = __ATTR_RO(format);
 109
 110static struct kobj_attribute size_attr = __ATTR_RO(size);
 111
 112static struct bin_attribute data_attr = __BIN_ATTR_RO(data, 0);
 113
 114static struct bin_attribute update_attr = __BIN_ATTR_WO(update, 0);
 115
 116static struct bin_attribute *secvar_bin_attrs[] = {
 117        &data_attr,
 118        &update_attr,
 119        NULL,
 120};
 121
 122static struct attribute *secvar_attrs[] = {
 123        &size_attr.attr,
 124        NULL,
 125};
 126
 127static const struct attribute_group secvar_attr_group = {
 128        .attrs = secvar_attrs,
 129        .bin_attrs = secvar_bin_attrs,
 130};
 131__ATTRIBUTE_GROUPS(secvar_attr);
 132
 133static struct kobj_type secvar_ktype = {
 134        .sysfs_ops      = &kobj_sysfs_ops,
 135        .default_groups = secvar_attr_groups,
 136};
 137
 138static int update_kobj_size(void)
 139{
 140
 141        struct device_node *node;
 142        u64 varsize;
 143        int rc = 0;
 144
 145        node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
 146        if (!of_device_is_available(node)) {
 147                rc = -ENODEV;
 148                goto out;
 149        }
 150
 151        rc = of_property_read_u64(node, "max-var-size", &varsize);
 152        if (rc)
 153                goto out;
 154
 155        data_attr.size = varsize;
 156        update_attr.size = varsize;
 157
 158out:
 159        of_node_put(node);
 160
 161        return rc;
 162}
 163
 164static int secvar_sysfs_load(void)
 165{
 166        char *name;
 167        uint64_t namesize = 0;
 168        struct kobject *kobj;
 169        int rc;
 170
 171        name = kzalloc(NAME_MAX_SIZE, GFP_KERNEL);
 172        if (!name)
 173                return -ENOMEM;
 174
 175        do {
 176                rc = secvar_ops->get_next(name, &namesize, NAME_MAX_SIZE);
 177                if (rc) {
 178                        if (rc != -ENOENT)
 179                                pr_err("error getting secvar from firmware %d\n",
 180                                       rc);
 181                        break;
 182                }
 183
 184                kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
 185                if (!kobj) {
 186                        rc = -ENOMEM;
 187                        break;
 188                }
 189
 190                kobject_init(kobj, &secvar_ktype);
 191
 192                rc = kobject_add(kobj, &secvar_kset->kobj, "%s", name);
 193                if (rc) {
 194                        pr_warn("kobject_add error %d for attribute: %s\n", rc,
 195                                name);
 196                        kobject_put(kobj);
 197                        kobj = NULL;
 198                }
 199
 200                if (kobj)
 201                        kobject_uevent(kobj, KOBJ_ADD);
 202
 203        } while (!rc);
 204
 205        kfree(name);
 206        return rc;
 207}
 208
 209static int secvar_sysfs_init(void)
 210{
 211        int rc;
 212
 213        if (!secvar_ops) {
 214                pr_warn("secvar: failed to retrieve secvar operations.\n");
 215                return -ENODEV;
 216        }
 217
 218        secvar_kobj = kobject_create_and_add("secvar", firmware_kobj);
 219        if (!secvar_kobj) {
 220                pr_err("secvar: Failed to create firmware kobj\n");
 221                return -ENOMEM;
 222        }
 223
 224        rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
 225        if (rc) {
 226                kobject_put(secvar_kobj);
 227                return -ENOMEM;
 228        }
 229
 230        secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
 231        if (!secvar_kset) {
 232                pr_err("secvar: sysfs kobject registration failed.\n");
 233                kobject_put(secvar_kobj);
 234                return -ENOMEM;
 235        }
 236
 237        rc = update_kobj_size();
 238        if (rc) {
 239                pr_err("Cannot read the size of the attribute\n");
 240                return rc;
 241        }
 242
 243        secvar_sysfs_load();
 244
 245        return 0;
 246}
 247
 248late_initcall(secvar_sysfs_init);
 249