123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2019 IBM Corporation <[email protected]>
- *
- * This code exposes secure variables to user via sysfs
- */
- #define pr_fmt(fmt) "secvar-sysfs: "fmt
- #include <linux/slab.h>
- #include <linux/compat.h>
- #include <linux/string.h>
- #include <linux/of.h>
- #include <asm/secvar.h>
- #define NAME_MAX_SIZE 1024
- static struct kobject *secvar_kobj;
- static struct kset *secvar_kset;
- static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
- char *buf)
- {
- ssize_t rc = 0;
- struct device_node *node;
- const char *format;
- node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
- if (!of_device_is_available(node)) {
- rc = -ENODEV;
- goto out;
- }
- rc = of_property_read_string(node, "format", &format);
- if (rc)
- goto out;
- rc = sprintf(buf, "%s\n", format);
- out:
- of_node_put(node);
- return rc;
- }
- static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
- char *buf)
- {
- uint64_t dsize;
- int rc;
- rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
- if (rc) {
- pr_err("Error retrieving %s variable size %d\n", kobj->name,
- rc);
- return rc;
- }
- return sprintf(buf, "%llu\n", dsize);
- }
- static ssize_t data_read(struct file *filep, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
- size_t count)
- {
- uint64_t dsize;
- char *data;
- int rc;
- rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
- if (rc) {
- pr_err("Error getting %s variable size %d\n", kobj->name, rc);
- return rc;
- }
- pr_debug("dsize is %llu\n", dsize);
- data = kzalloc(dsize, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, data, &dsize);
- if (rc) {
- pr_err("Error getting %s variable %d\n", kobj->name, rc);
- goto data_fail;
- }
- rc = memory_read_from_buffer(buf, count, &off, data, dsize);
- data_fail:
- kfree(data);
- return rc;
- }
- static ssize_t update_write(struct file *filep, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
- size_t count)
- {
- int rc;
- pr_debug("count is %ld\n", count);
- rc = secvar_ops->set(kobj->name, strlen(kobj->name) + 1, buf, count);
- if (rc) {
- pr_err("Error setting the %s variable %d\n", kobj->name, rc);
- return rc;
- }
- return count;
- }
- static struct kobj_attribute format_attr = __ATTR_RO(format);
- static struct kobj_attribute size_attr = __ATTR_RO(size);
- static struct bin_attribute data_attr = __BIN_ATTR_RO(data, 0);
- static struct bin_attribute update_attr = __BIN_ATTR_WO(update, 0);
- static struct bin_attribute *secvar_bin_attrs[] = {
- &data_attr,
- &update_attr,
- NULL,
- };
- static struct attribute *secvar_attrs[] = {
- &size_attr.attr,
- NULL,
- };
- static const struct attribute_group secvar_attr_group = {
- .attrs = secvar_attrs,
- .bin_attrs = secvar_bin_attrs,
- };
- __ATTRIBUTE_GROUPS(secvar_attr);
- static struct kobj_type secvar_ktype = {
- .sysfs_ops = &kobj_sysfs_ops,
- .default_groups = secvar_attr_groups,
- };
- static int update_kobj_size(void)
- {
- struct device_node *node;
- u64 varsize;
- int rc = 0;
- node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
- if (!of_device_is_available(node)) {
- rc = -ENODEV;
- goto out;
- }
- rc = of_property_read_u64(node, "max-var-size", &varsize);
- if (rc)
- goto out;
- data_attr.size = varsize;
- update_attr.size = varsize;
- out:
- of_node_put(node);
- return rc;
- }
- static int secvar_sysfs_load(void)
- {
- char *name;
- uint64_t namesize = 0;
- struct kobject *kobj;
- int rc;
- name = kzalloc(NAME_MAX_SIZE, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
- do {
- rc = secvar_ops->get_next(name, &namesize, NAME_MAX_SIZE);
- if (rc) {
- if (rc != -ENOENT)
- pr_err("error getting secvar from firmware %d\n",
- rc);
- break;
- }
- kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
- if (!kobj) {
- rc = -ENOMEM;
- break;
- }
- kobject_init(kobj, &secvar_ktype);
- rc = kobject_add(kobj, &secvar_kset->kobj, "%s", name);
- if (rc) {
- pr_warn("kobject_add error %d for attribute: %s\n", rc,
- name);
- kobject_put(kobj);
- kobj = NULL;
- }
- if (kobj)
- kobject_uevent(kobj, KOBJ_ADD);
- } while (!rc);
- kfree(name);
- return rc;
- }
- static int secvar_sysfs_init(void)
- {
- int rc;
- if (!secvar_ops) {
- pr_warn("secvar: failed to retrieve secvar operations.\n");
- return -ENODEV;
- }
- secvar_kobj = kobject_create_and_add("secvar", firmware_kobj);
- if (!secvar_kobj) {
- pr_err("secvar: Failed to create firmware kobj\n");
- return -ENOMEM;
- }
- rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
- if (rc) {
- kobject_put(secvar_kobj);
- return -ENOMEM;
- }
- secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
- if (!secvar_kset) {
- pr_err("secvar: sysfs kobject registration failed.\n");
- kobject_put(secvar_kobj);
- return -ENOMEM;
- }
- rc = update_kobj_size();
- if (rc) {
- pr_err("Cannot read the size of the attribute\n");
- return rc;
- }
- secvar_sysfs_load();
- return 0;
- }
- late_initcall(secvar_sysfs_init);
|