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