cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sysfs_upload.c (10038B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3#include <linux/firmware.h>
      4#include <linux/module.h>
      5#include <linux/slab.h>
      6
      7#include "sysfs_upload.h"
      8
      9/*
     10 * Support for user-space to initiate a firmware upload to a device.
     11 */
     12
     13static const char * const fw_upload_prog_str[] = {
     14	[FW_UPLOAD_PROG_IDLE]	      = "idle",
     15	[FW_UPLOAD_PROG_RECEIVING]    = "receiving",
     16	[FW_UPLOAD_PROG_PREPARING]    = "preparing",
     17	[FW_UPLOAD_PROG_TRANSFERRING] = "transferring",
     18	[FW_UPLOAD_PROG_PROGRAMMING]  = "programming"
     19};
     20
     21static const char * const fw_upload_err_str[] = {
     22	[FW_UPLOAD_ERR_NONE]	     = "none",
     23	[FW_UPLOAD_ERR_HW_ERROR]     = "hw-error",
     24	[FW_UPLOAD_ERR_TIMEOUT]	     = "timeout",
     25	[FW_UPLOAD_ERR_CANCELED]     = "user-abort",
     26	[FW_UPLOAD_ERR_BUSY]	     = "device-busy",
     27	[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
     28	[FW_UPLOAD_ERR_RW_ERROR]     = "read-write-error",
     29	[FW_UPLOAD_ERR_WEAROUT]	     = "flash-wearout",
     30};
     31
     32static const char *fw_upload_progress(struct device *dev,
     33				      enum fw_upload_prog prog)
     34{
     35	const char *status = "unknown-status";
     36
     37	if (prog < FW_UPLOAD_PROG_MAX)
     38		status = fw_upload_prog_str[prog];
     39	else
     40		dev_err(dev, "Invalid status during secure update: %d\n", prog);
     41
     42	return status;
     43}
     44
     45static const char *fw_upload_error(struct device *dev,
     46				   enum fw_upload_err err_code)
     47{
     48	const char *error = "unknown-error";
     49
     50	if (err_code < FW_UPLOAD_ERR_MAX)
     51		error = fw_upload_err_str[err_code];
     52	else
     53		dev_err(dev, "Invalid error code during secure update: %d\n",
     54			err_code);
     55
     56	return error;
     57}
     58
     59static ssize_t
     60status_show(struct device *dev, struct device_attribute *attr, char *buf)
     61{
     62	struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
     63
     64	return sysfs_emit(buf, "%s\n", fw_upload_progress(dev, fwlp->progress));
     65}
     66DEVICE_ATTR_RO(status);
     67
     68static ssize_t
     69error_show(struct device *dev, struct device_attribute *attr, char *buf)
     70{
     71	struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
     72	int ret;
     73
     74	mutex_lock(&fwlp->lock);
     75
     76	if (fwlp->progress != FW_UPLOAD_PROG_IDLE)
     77		ret = -EBUSY;
     78	else if (!fwlp->err_code)
     79		ret = 0;
     80	else
     81		ret = sysfs_emit(buf, "%s:%s\n",
     82				 fw_upload_progress(dev, fwlp->err_progress),
     83				 fw_upload_error(dev, fwlp->err_code));
     84
     85	mutex_unlock(&fwlp->lock);
     86
     87	return ret;
     88}
     89DEVICE_ATTR_RO(error);
     90
     91static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
     92			    const char *buf, size_t count)
     93{
     94	struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
     95	int ret = count;
     96	bool cancel;
     97
     98	if (kstrtobool(buf, &cancel) || !cancel)
     99		return -EINVAL;
    100
    101	mutex_lock(&fwlp->lock);
    102	if (fwlp->progress == FW_UPLOAD_PROG_IDLE)
    103		ret = -ENODEV;
    104
    105	fwlp->ops->cancel(fwlp->fw_upload);
    106	mutex_unlock(&fwlp->lock);
    107
    108	return ret;
    109}
    110DEVICE_ATTR_WO(cancel);
    111
    112static ssize_t remaining_size_show(struct device *dev,
    113				   struct device_attribute *attr, char *buf)
    114{
    115	struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
    116
    117	return sysfs_emit(buf, "%u\n", fwlp->remaining_size);
    118}
    119DEVICE_ATTR_RO(remaining_size);
    120
    121umode_t
    122fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n)
    123{
    124	static struct fw_sysfs *fw_sysfs;
    125
    126	fw_sysfs = to_fw_sysfs(kobj_to_dev(kobj));
    127
    128	if (fw_sysfs->fw_upload_priv || attr == &dev_attr_loading.attr)
    129		return attr->mode;
    130
    131	return 0;
    132}
    133
    134static void fw_upload_update_progress(struct fw_upload_priv *fwlp,
    135				      enum fw_upload_prog new_progress)
    136{
    137	mutex_lock(&fwlp->lock);
    138	fwlp->progress = new_progress;
    139	mutex_unlock(&fwlp->lock);
    140}
    141
    142static void fw_upload_set_error(struct fw_upload_priv *fwlp,
    143				enum fw_upload_err err_code)
    144{
    145	mutex_lock(&fwlp->lock);
    146	fwlp->err_progress = fwlp->progress;
    147	fwlp->err_code = err_code;
    148	mutex_unlock(&fwlp->lock);
    149}
    150
    151static void fw_upload_prog_complete(struct fw_upload_priv *fwlp)
    152{
    153	mutex_lock(&fwlp->lock);
    154	fwlp->progress = FW_UPLOAD_PROG_IDLE;
    155	mutex_unlock(&fwlp->lock);
    156}
    157
    158static void fw_upload_main(struct work_struct *work)
    159{
    160	struct fw_upload_priv *fwlp;
    161	struct fw_sysfs *fw_sysfs;
    162	u32 written = 0, offset = 0;
    163	enum fw_upload_err ret;
    164	struct device *fw_dev;
    165	struct fw_upload *fwl;
    166
    167	fwlp = container_of(work, struct fw_upload_priv, work);
    168	fwl = fwlp->fw_upload;
    169	fw_sysfs = (struct fw_sysfs *)fwl->priv;
    170	fw_dev = &fw_sysfs->dev;
    171
    172	fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_PREPARING);
    173	ret = fwlp->ops->prepare(fwl, fwlp->data, fwlp->remaining_size);
    174	if (ret != FW_UPLOAD_ERR_NONE) {
    175		fw_upload_set_error(fwlp, ret);
    176		goto putdev_exit;
    177	}
    178
    179	fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_TRANSFERRING);
    180	while (fwlp->remaining_size) {
    181		ret = fwlp->ops->write(fwl, fwlp->data, offset,
    182					fwlp->remaining_size, &written);
    183		if (ret != FW_UPLOAD_ERR_NONE || !written) {
    184			if (ret == FW_UPLOAD_ERR_NONE) {
    185				dev_warn(fw_dev, "write-op wrote zero data\n");
    186				ret = FW_UPLOAD_ERR_RW_ERROR;
    187			}
    188			fw_upload_set_error(fwlp, ret);
    189			goto done;
    190		}
    191
    192		fwlp->remaining_size -= written;
    193		offset += written;
    194	}
    195
    196	fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_PROGRAMMING);
    197	ret = fwlp->ops->poll_complete(fwl);
    198	if (ret != FW_UPLOAD_ERR_NONE)
    199		fw_upload_set_error(fwlp, ret);
    200
    201done:
    202	if (fwlp->ops->cleanup)
    203		fwlp->ops->cleanup(fwl);
    204
    205putdev_exit:
    206	put_device(fw_dev->parent);
    207
    208	/*
    209	 * Note: fwlp->remaining_size is left unmodified here to provide
    210	 * additional information on errors. It will be reinitialized when
    211	 * the next firmeware upload begins.
    212	 */
    213	mutex_lock(&fw_lock);
    214	fw_free_paged_buf(fw_sysfs->fw_priv);
    215	fw_state_init(fw_sysfs->fw_priv);
    216	mutex_unlock(&fw_lock);
    217	fwlp->data = NULL;
    218	fw_upload_prog_complete(fwlp);
    219}
    220
    221/*
    222 * Start a worker thread to upload data to the parent driver.
    223 * Must be called with fw_lock held.
    224 */
    225int fw_upload_start(struct fw_sysfs *fw_sysfs)
    226{
    227	struct fw_priv *fw_priv = fw_sysfs->fw_priv;
    228	struct device *fw_dev = &fw_sysfs->dev;
    229	struct fw_upload_priv *fwlp;
    230
    231	if (!fw_sysfs->fw_upload_priv)
    232		return 0;
    233
    234	if (!fw_priv->size) {
    235		fw_free_paged_buf(fw_priv);
    236		fw_state_init(fw_sysfs->fw_priv);
    237		return 0;
    238	}
    239
    240	fwlp = fw_sysfs->fw_upload_priv;
    241	mutex_lock(&fwlp->lock);
    242
    243	/* Do not interfere with an on-going fw_upload */
    244	if (fwlp->progress != FW_UPLOAD_PROG_IDLE) {
    245		mutex_unlock(&fwlp->lock);
    246		return -EBUSY;
    247	}
    248
    249	get_device(fw_dev->parent); /* released in fw_upload_main */
    250
    251	fwlp->progress = FW_UPLOAD_PROG_RECEIVING;
    252	fwlp->err_code = 0;
    253	fwlp->remaining_size = fw_priv->size;
    254	fwlp->data = fw_priv->data;
    255
    256	pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
    257		 __func__, fw_priv->fw_name,
    258		 fw_priv, fw_priv->data,
    259		 (unsigned int)fw_priv->size);
    260
    261	queue_work(system_long_wq, &fwlp->work);
    262	mutex_unlock(&fwlp->lock);
    263
    264	return 0;
    265}
    266
    267/**
    268 * firmware_upload_register() - register for the firmware upload sysfs API
    269 * @module: kernel module of this device
    270 * @parent: parent device instantiating firmware upload
    271 * @name: firmware name to be associated with this device
    272 * @ops: pointer to structure of firmware upload ops
    273 * @dd_handle: pointer to parent driver private data
    274 *
    275 *	@name must be unique among all users of firmware upload. The firmware
    276 *	sysfs files for this device will be found at /sys/class/firmware/@name.
    277 *
    278 *	Return: struct fw_upload pointer or ERR_PTR()
    279 *
    280 **/
    281struct fw_upload *
    282firmware_upload_register(struct module *module, struct device *parent,
    283			 const char *name, const struct fw_upload_ops *ops,
    284			 void *dd_handle)
    285{
    286	u32 opt_flags = FW_OPT_NOCACHE;
    287	struct fw_upload *fw_upload;
    288	struct fw_upload_priv *fw_upload_priv;
    289	struct fw_sysfs *fw_sysfs;
    290	struct fw_priv *fw_priv;
    291	struct device *fw_dev;
    292	int ret;
    293
    294	if (!name || name[0] == '\0')
    295		return ERR_PTR(-EINVAL);
    296
    297	if (!ops || !ops->cancel || !ops->prepare ||
    298	    !ops->write || !ops->poll_complete) {
    299		dev_err(parent, "Attempt to register without all required ops\n");
    300		return ERR_PTR(-EINVAL);
    301	}
    302
    303	if (!try_module_get(module))
    304		return ERR_PTR(-EFAULT);
    305
    306	fw_upload = kzalloc(sizeof(*fw_upload), GFP_KERNEL);
    307	if (!fw_upload) {
    308		ret = -ENOMEM;
    309		goto exit_module_put;
    310	}
    311
    312	fw_upload_priv = kzalloc(sizeof(*fw_upload_priv), GFP_KERNEL);
    313	if (!fw_upload_priv) {
    314		ret = -ENOMEM;
    315		goto free_fw_upload;
    316	}
    317
    318	fw_upload_priv->fw_upload = fw_upload;
    319	fw_upload_priv->ops = ops;
    320	mutex_init(&fw_upload_priv->lock);
    321	fw_upload_priv->module = module;
    322	fw_upload_priv->name = name;
    323	fw_upload_priv->err_code = 0;
    324	fw_upload_priv->progress = FW_UPLOAD_PROG_IDLE;
    325	INIT_WORK(&fw_upload_priv->work, fw_upload_main);
    326	fw_upload->dd_handle = dd_handle;
    327
    328	fw_sysfs = fw_create_instance(NULL, name, parent, opt_flags);
    329	if (IS_ERR(fw_sysfs)) {
    330		ret = PTR_ERR(fw_sysfs);
    331		goto free_fw_upload_priv;
    332	}
    333	fw_upload->priv = fw_sysfs;
    334	fw_sysfs->fw_upload_priv = fw_upload_priv;
    335	fw_dev = &fw_sysfs->dev;
    336
    337	ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv,  NULL, 0, 0,
    338				   FW_OPT_NOCACHE);
    339	if (ret != 0) {
    340		if (ret > 0)
    341			ret = -EINVAL;
    342		goto free_fw_sysfs;
    343	}
    344	fw_priv->is_paged_buf = true;
    345	fw_sysfs->fw_priv = fw_priv;
    346
    347	ret = device_add(fw_dev);
    348	if (ret) {
    349		dev_err(fw_dev, "%s: device_register failed\n", __func__);
    350		put_device(fw_dev);
    351		goto exit_module_put;
    352	}
    353
    354	return fw_upload;
    355
    356free_fw_sysfs:
    357	kfree(fw_sysfs);
    358
    359free_fw_upload_priv:
    360	kfree(fw_upload_priv);
    361
    362free_fw_upload:
    363	kfree(fw_upload);
    364
    365exit_module_put:
    366	module_put(module);
    367
    368	return ERR_PTR(ret);
    369}
    370EXPORT_SYMBOL_GPL(firmware_upload_register);
    371
    372/**
    373 * firmware_upload_unregister() - Unregister firmware upload interface
    374 * @fw_upload: pointer to struct fw_upload
    375 **/
    376void firmware_upload_unregister(struct fw_upload *fw_upload)
    377{
    378	struct fw_sysfs *fw_sysfs = fw_upload->priv;
    379	struct fw_upload_priv *fw_upload_priv = fw_sysfs->fw_upload_priv;
    380
    381	mutex_lock(&fw_upload_priv->lock);
    382	if (fw_upload_priv->progress == FW_UPLOAD_PROG_IDLE) {
    383		mutex_unlock(&fw_upload_priv->lock);
    384		goto unregister;
    385	}
    386
    387	fw_upload_priv->ops->cancel(fw_upload);
    388	mutex_unlock(&fw_upload_priv->lock);
    389
    390	/* Ensure lower-level device-driver is finished */
    391	flush_work(&fw_upload_priv->work);
    392
    393unregister:
    394	device_unregister(&fw_sysfs->dev);
    395	module_put(fw_upload_priv->module);
    396}
    397EXPORT_SYMBOL_GPL(firmware_upload_unregister);