1 // SPDX-License-Identifier: GPL-2.0+ 2 // Expose the vboot context nvram to userspace 3 // 4 // Copyright (C) 2012 Google, Inc. 5 // Copyright (C) 2015 Collabora Ltd. 6 7 #include <linux/of.h> 8 #include <linux/platform_device.h> 9 #include <linux/mfd/cros_ec.h> 10 #include <linux/module.h> 11 #include <linux/platform_data/cros_ec_commands.h> 12 #include <linux/platform_data/cros_ec_proto.h> 13 #include <linux/slab.h> 14 15 #define DRV_NAME "cros-ec-vbc" 16 17 static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, 18 struct bin_attribute *att, char *buf, 19 loff_t pos, size_t count) 20 { 21 struct device *dev = container_of(kobj, struct device, kobj); 22 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 23 struct cros_ec_device *ecdev = ec->ec_dev; 24 struct ec_params_vbnvcontext *params; 25 struct cros_ec_command *msg; 26 int err; 27 const size_t para_sz = sizeof(params->op); 28 const size_t resp_sz = sizeof(struct ec_response_vbnvcontext); 29 const size_t payload = max(para_sz, resp_sz); 30 31 msg = kmalloc(sizeof(*msg) + payload, GFP_KERNEL); 32 if (!msg) 33 return -ENOMEM; 34 35 /* NB: we only kmalloc()ated enough space for the op field */ 36 params = (struct ec_params_vbnvcontext *)msg->data; 37 params->op = EC_VBNV_CONTEXT_OP_READ; 38 39 msg->version = EC_VER_VBNV_CONTEXT; 40 msg->command = EC_CMD_VBNV_CONTEXT; 41 msg->outsize = para_sz; 42 msg->insize = resp_sz; 43 44 err = cros_ec_cmd_xfer(ecdev, msg); 45 if (err < 0) { 46 dev_err(dev, "Error sending read request: %d\n", err); 47 kfree(msg); 48 return err; 49 } 50 51 memcpy(buf, msg->data, resp_sz); 52 53 kfree(msg); 54 return resp_sz; 55 } 56 57 static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, 58 struct bin_attribute *attr, char *buf, 59 loff_t pos, size_t count) 60 { 61 struct device *dev = container_of(kobj, struct device, kobj); 62 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 63 struct cros_ec_device *ecdev = ec->ec_dev; 64 struct ec_params_vbnvcontext *params; 65 struct cros_ec_command *msg; 66 int err; 67 const size_t para_sz = sizeof(*params); 68 const size_t data_sz = sizeof(params->block); 69 70 /* Only write full values */ 71 if (count != data_sz) 72 return -EINVAL; 73 74 msg = kmalloc(sizeof(*msg) + para_sz, GFP_KERNEL); 75 if (!msg) 76 return -ENOMEM; 77 78 params = (struct ec_params_vbnvcontext *)msg->data; 79 params->op = EC_VBNV_CONTEXT_OP_WRITE; 80 memcpy(params->block, buf, data_sz); 81 82 msg->version = EC_VER_VBNV_CONTEXT; 83 msg->command = EC_CMD_VBNV_CONTEXT; 84 msg->outsize = para_sz; 85 msg->insize = 0; 86 87 err = cros_ec_cmd_xfer(ecdev, msg); 88 if (err < 0) { 89 dev_err(dev, "Error sending write request: %d\n", err); 90 kfree(msg); 91 return err; 92 } 93 94 kfree(msg); 95 return data_sz; 96 } 97 98 static BIN_ATTR_RW(vboot_context, 16); 99 100 static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { 101 &bin_attr_vboot_context, 102 NULL 103 }; 104 105 static struct attribute_group cros_ec_vbc_attr_group = { 106 .name = "vbc", 107 .bin_attrs = cros_ec_vbc_bin_attrs, 108 }; 109 110 static int cros_ec_vbc_probe(struct platform_device *pd) 111 { 112 struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); 113 struct device *dev = &pd->dev; 114 int ret; 115 116 ret = sysfs_create_group(&ec_dev->class_dev.kobj, 117 &cros_ec_vbc_attr_group); 118 if (ret < 0) 119 dev_err(dev, "failed to create %s attributes. err=%d\n", 120 cros_ec_vbc_attr_group.name, ret); 121 122 return ret; 123 } 124 125 static int cros_ec_vbc_remove(struct platform_device *pd) 126 { 127 struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); 128 129 sysfs_remove_group(&ec_dev->class_dev.kobj, 130 &cros_ec_vbc_attr_group); 131 132 return 0; 133 } 134 135 static struct platform_driver cros_ec_vbc_driver = { 136 .driver = { 137 .name = DRV_NAME, 138 }, 139 .probe = cros_ec_vbc_probe, 140 .remove = cros_ec_vbc_remove, 141 }; 142 143 module_platform_driver(cros_ec_vbc_driver); 144 145 MODULE_LICENSE("GPL"); 146 MODULE_DESCRIPTION("Expose the vboot context nvram to userspace"); 147 MODULE_ALIAS("platform:" DRV_NAME); 148