162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * main.c - Multi purpose firmware loading support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2003 Manuel Estrada Sainz
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Please see Documentation/driver-api/firmware/ for more information.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/capability.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/kernel_read_file.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/initrd.h>
1962306a36Sopenharmony_ci#include <linux/timer.h>
2062306a36Sopenharmony_ci#include <linux/vmalloc.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/bitops.h>
2362306a36Sopenharmony_ci#include <linux/mutex.h>
2462306a36Sopenharmony_ci#include <linux/workqueue.h>
2562306a36Sopenharmony_ci#include <linux/highmem.h>
2662306a36Sopenharmony_ci#include <linux/firmware.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/sched.h>
2962306a36Sopenharmony_ci#include <linux/file.h>
3062306a36Sopenharmony_ci#include <linux/list.h>
3162306a36Sopenharmony_ci#include <linux/fs.h>
3262306a36Sopenharmony_ci#include <linux/async.h>
3362306a36Sopenharmony_ci#include <linux/pm.h>
3462306a36Sopenharmony_ci#include <linux/suspend.h>
3562306a36Sopenharmony_ci#include <linux/syscore_ops.h>
3662306a36Sopenharmony_ci#include <linux/reboot.h>
3762306a36Sopenharmony_ci#include <linux/security.h>
3862306a36Sopenharmony_ci#include <linux/zstd.h>
3962306a36Sopenharmony_ci#include <linux/xz.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <generated/utsrelease.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include "../base.h"
4462306a36Sopenharmony_ci#include "firmware.h"
4562306a36Sopenharmony_ci#include "fallback.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciMODULE_AUTHOR("Manuel Estrada Sainz");
4862306a36Sopenharmony_ciMODULE_DESCRIPTION("Multi purpose firmware loading support");
4962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct firmware_cache {
5262306a36Sopenharmony_ci	/* firmware_buf instance will be added into the below list */
5362306a36Sopenharmony_ci	spinlock_t lock;
5462306a36Sopenharmony_ci	struct list_head head;
5562306a36Sopenharmony_ci	int state;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#ifdef CONFIG_FW_CACHE
5862306a36Sopenharmony_ci	/*
5962306a36Sopenharmony_ci	 * Names of firmware images which have been cached successfully
6062306a36Sopenharmony_ci	 * will be added into the below list so that device uncache
6162306a36Sopenharmony_ci	 * helper can trace which firmware images have been cached
6262306a36Sopenharmony_ci	 * before.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	spinlock_t name_lock;
6562306a36Sopenharmony_ci	struct list_head fw_names;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	struct delayed_work work;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	struct notifier_block   pm_notify;
7062306a36Sopenharmony_ci#endif
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct fw_cache_entry {
7462306a36Sopenharmony_ci	struct list_head list;
7562306a36Sopenharmony_ci	const char *name;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistruct fw_name_devm {
7962306a36Sopenharmony_ci	unsigned long magic;
8062306a36Sopenharmony_ci	const char *name;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic inline struct fw_priv *to_fw_priv(struct kref *ref)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return container_of(ref, struct fw_priv, ref);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define	FW_LOADER_NO_CACHE	0
8962306a36Sopenharmony_ci#define	FW_LOADER_START_CACHE	1
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* fw_lock could be moved to 'struct fw_sysfs' but since it is just
9262306a36Sopenharmony_ci * guarding for corner cases a global lock should be OK */
9362306a36Sopenharmony_ciDEFINE_MUTEX(fw_lock);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct firmware_cache fw_cache;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_civoid fw_state_init(struct fw_priv *fw_priv)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct fw_state *fw_st = &fw_priv->fw_st;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	init_completion(&fw_st->completion);
10262306a36Sopenharmony_ci	fw_st->status = FW_STATUS_UNKNOWN;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic inline int fw_state_wait(struct fw_priv *fw_priv)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void fw_cache_piggyback_on_request(struct fw_priv *fw_priv);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic struct fw_priv *__allocate_fw_priv(const char *fw_name,
11362306a36Sopenharmony_ci					  struct firmware_cache *fwc,
11462306a36Sopenharmony_ci					  void *dbuf,
11562306a36Sopenharmony_ci					  size_t size,
11662306a36Sopenharmony_ci					  size_t offset,
11762306a36Sopenharmony_ci					  u32 opt_flags)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct fw_priv *fw_priv;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* For a partial read, the buffer must be preallocated. */
12262306a36Sopenharmony_ci	if ((opt_flags & FW_OPT_PARTIAL) && !dbuf)
12362306a36Sopenharmony_ci		return NULL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Only partial reads are allowed to use an offset. */
12662306a36Sopenharmony_ci	if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL))
12762306a36Sopenharmony_ci		return NULL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
13062306a36Sopenharmony_ci	if (!fw_priv)
13162306a36Sopenharmony_ci		return NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	fw_priv->fw_name = kstrdup_const(fw_name, GFP_ATOMIC);
13462306a36Sopenharmony_ci	if (!fw_priv->fw_name) {
13562306a36Sopenharmony_ci		kfree(fw_priv);
13662306a36Sopenharmony_ci		return NULL;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	kref_init(&fw_priv->ref);
14062306a36Sopenharmony_ci	fw_priv->fwc = fwc;
14162306a36Sopenharmony_ci	fw_priv->data = dbuf;
14262306a36Sopenharmony_ci	fw_priv->allocated_size = size;
14362306a36Sopenharmony_ci	fw_priv->offset = offset;
14462306a36Sopenharmony_ci	fw_priv->opt_flags = opt_flags;
14562306a36Sopenharmony_ci	fw_state_init(fw_priv);
14662306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER
14762306a36Sopenharmony_ci	INIT_LIST_HEAD(&fw_priv->pending_list);
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	pr_debug("%s: fw-%s fw_priv=%p\n", __func__, fw_name, fw_priv);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return fw_priv;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic struct fw_priv *__lookup_fw_priv(const char *fw_name)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct fw_priv *tmp;
15862306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	list_for_each_entry(tmp, &fwc->head, list)
16162306a36Sopenharmony_ci		if (!strcmp(tmp->fw_name, fw_name))
16262306a36Sopenharmony_ci			return tmp;
16362306a36Sopenharmony_ci	return NULL;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* Returns 1 for batching firmware requests with the same name */
16762306a36Sopenharmony_ciint alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc,
16862306a36Sopenharmony_ci			 struct fw_priv **fw_priv, void *dbuf, size_t size,
16962306a36Sopenharmony_ci			 size_t offset, u32 opt_flags)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct fw_priv *tmp;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	spin_lock(&fwc->lock);
17462306a36Sopenharmony_ci	/*
17562306a36Sopenharmony_ci	 * Do not merge requests that are marked to be non-cached or
17662306a36Sopenharmony_ci	 * are performing partial reads.
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) {
17962306a36Sopenharmony_ci		tmp = __lookup_fw_priv(fw_name);
18062306a36Sopenharmony_ci		if (tmp) {
18162306a36Sopenharmony_ci			kref_get(&tmp->ref);
18262306a36Sopenharmony_ci			spin_unlock(&fwc->lock);
18362306a36Sopenharmony_ci			*fw_priv = tmp;
18462306a36Sopenharmony_ci			pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n");
18562306a36Sopenharmony_ci			return 1;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags);
19062306a36Sopenharmony_ci	if (tmp) {
19162306a36Sopenharmony_ci		INIT_LIST_HEAD(&tmp->list);
19262306a36Sopenharmony_ci		if (!(opt_flags & FW_OPT_NOCACHE))
19362306a36Sopenharmony_ci			list_add(&tmp->list, &fwc->head);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	spin_unlock(&fwc->lock);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	*fw_priv = tmp;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return tmp ? 0 : -ENOMEM;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void __free_fw_priv(struct kref *ref)
20362306a36Sopenharmony_ci	__releases(&fwc->lock)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct fw_priv *fw_priv = to_fw_priv(ref);
20662306a36Sopenharmony_ci	struct firmware_cache *fwc = fw_priv->fwc;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
20962306a36Sopenharmony_ci		 __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
21062306a36Sopenharmony_ci		 (unsigned int)fw_priv->size);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	list_del(&fw_priv->list);
21362306a36Sopenharmony_ci	spin_unlock(&fwc->lock);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (fw_is_paged_buf(fw_priv))
21662306a36Sopenharmony_ci		fw_free_paged_buf(fw_priv);
21762306a36Sopenharmony_ci	else if (!fw_priv->allocated_size)
21862306a36Sopenharmony_ci		vfree(fw_priv->data);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	kfree_const(fw_priv->fw_name);
22162306a36Sopenharmony_ci	kfree(fw_priv);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_civoid free_fw_priv(struct fw_priv *fw_priv)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct firmware_cache *fwc = fw_priv->fwc;
22762306a36Sopenharmony_ci	spin_lock(&fwc->lock);
22862306a36Sopenharmony_ci	if (!kref_put(&fw_priv->ref, __free_fw_priv))
22962306a36Sopenharmony_ci		spin_unlock(&fwc->lock);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_PAGED_BUF
23362306a36Sopenharmony_cibool fw_is_paged_buf(struct fw_priv *fw_priv)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	return fw_priv->is_paged_buf;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_civoid fw_free_paged_buf(struct fw_priv *fw_priv)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	int i;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (!fw_priv->pages)
24362306a36Sopenharmony_ci		return;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	vunmap(fw_priv->data);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for (i = 0; i < fw_priv->nr_pages; i++)
24862306a36Sopenharmony_ci		__free_page(fw_priv->pages[i]);
24962306a36Sopenharmony_ci	kvfree(fw_priv->pages);
25062306a36Sopenharmony_ci	fw_priv->pages = NULL;
25162306a36Sopenharmony_ci	fw_priv->page_array_size = 0;
25262306a36Sopenharmony_ci	fw_priv->nr_pages = 0;
25362306a36Sopenharmony_ci	fw_priv->data = NULL;
25462306a36Sopenharmony_ci	fw_priv->size = 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	/* If the array of pages is too small, grow it */
26062306a36Sopenharmony_ci	if (fw_priv->page_array_size < pages_needed) {
26162306a36Sopenharmony_ci		int new_array_size = max(pages_needed,
26262306a36Sopenharmony_ci					 fw_priv->page_array_size * 2);
26362306a36Sopenharmony_ci		struct page **new_pages;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		new_pages = kvmalloc_array(new_array_size, sizeof(void *),
26662306a36Sopenharmony_ci					   GFP_KERNEL);
26762306a36Sopenharmony_ci		if (!new_pages)
26862306a36Sopenharmony_ci			return -ENOMEM;
26962306a36Sopenharmony_ci		memcpy(new_pages, fw_priv->pages,
27062306a36Sopenharmony_ci		       fw_priv->page_array_size * sizeof(void *));
27162306a36Sopenharmony_ci		memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
27262306a36Sopenharmony_ci		       (new_array_size - fw_priv->page_array_size));
27362306a36Sopenharmony_ci		kvfree(fw_priv->pages);
27462306a36Sopenharmony_ci		fw_priv->pages = new_pages;
27562306a36Sopenharmony_ci		fw_priv->page_array_size = new_array_size;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	while (fw_priv->nr_pages < pages_needed) {
27962306a36Sopenharmony_ci		fw_priv->pages[fw_priv->nr_pages] =
28062306a36Sopenharmony_ci			alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (!fw_priv->pages[fw_priv->nr_pages])
28362306a36Sopenharmony_ci			return -ENOMEM;
28462306a36Sopenharmony_ci		fw_priv->nr_pages++;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ciint fw_map_paged_buf(struct fw_priv *fw_priv)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	/* one pages buffer should be mapped/unmapped only once */
29362306a36Sopenharmony_ci	if (!fw_priv->pages)
29462306a36Sopenharmony_ci		return 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	vunmap(fw_priv->data);
29762306a36Sopenharmony_ci	fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
29862306a36Sopenharmony_ci			     PAGE_KERNEL_RO);
29962306a36Sopenharmony_ci	if (!fw_priv->data)
30062306a36Sopenharmony_ci		return -ENOMEM;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci#endif
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/*
30762306a36Sopenharmony_ci * ZSTD-compressed firmware support
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
31062306a36Sopenharmony_cistatic int fw_decompress_zstd(struct device *dev, struct fw_priv *fw_priv,
31162306a36Sopenharmony_ci			      size_t in_size, const void *in_buffer)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	size_t len, out_size, workspace_size;
31462306a36Sopenharmony_ci	void *workspace, *out_buf;
31562306a36Sopenharmony_ci	zstd_dctx *ctx;
31662306a36Sopenharmony_ci	int err;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (fw_priv->allocated_size) {
31962306a36Sopenharmony_ci		out_size = fw_priv->allocated_size;
32062306a36Sopenharmony_ci		out_buf = fw_priv->data;
32162306a36Sopenharmony_ci	} else {
32262306a36Sopenharmony_ci		zstd_frame_header params;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		if (zstd_get_frame_header(&params, in_buffer, in_size) ||
32562306a36Sopenharmony_ci		    params.frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
32662306a36Sopenharmony_ci			dev_dbg(dev, "%s: invalid zstd header\n", __func__);
32762306a36Sopenharmony_ci			return -EINVAL;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		out_size = params.frameContentSize;
33062306a36Sopenharmony_ci		out_buf = vzalloc(out_size);
33162306a36Sopenharmony_ci		if (!out_buf)
33262306a36Sopenharmony_ci			return -ENOMEM;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	workspace_size = zstd_dctx_workspace_bound();
33662306a36Sopenharmony_ci	workspace = kvzalloc(workspace_size, GFP_KERNEL);
33762306a36Sopenharmony_ci	if (!workspace) {
33862306a36Sopenharmony_ci		err = -ENOMEM;
33962306a36Sopenharmony_ci		goto error;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	ctx = zstd_init_dctx(workspace, workspace_size);
34362306a36Sopenharmony_ci	if (!ctx) {
34462306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed to initialize context\n", __func__);
34562306a36Sopenharmony_ci		err = -EINVAL;
34662306a36Sopenharmony_ci		goto error;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	len = zstd_decompress_dctx(ctx, out_buf, out_size, in_buffer, in_size);
35062306a36Sopenharmony_ci	if (zstd_is_error(len)) {
35162306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed to decompress: %d\n", __func__,
35262306a36Sopenharmony_ci			zstd_get_error_code(len));
35362306a36Sopenharmony_ci		err = -EINVAL;
35462306a36Sopenharmony_ci		goto error;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (!fw_priv->allocated_size)
35862306a36Sopenharmony_ci		fw_priv->data = out_buf;
35962306a36Sopenharmony_ci	fw_priv->size = len;
36062306a36Sopenharmony_ci	err = 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci error:
36362306a36Sopenharmony_ci	kvfree(workspace);
36462306a36Sopenharmony_ci	if (err && !fw_priv->allocated_size)
36562306a36Sopenharmony_ci		vfree(out_buf);
36662306a36Sopenharmony_ci	return err;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci#endif /* CONFIG_FW_LOADER_COMPRESS_ZSTD */
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/*
37162306a36Sopenharmony_ci * XZ-compressed firmware support
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
37462306a36Sopenharmony_ci/* show an error and return the standard error code */
37562306a36Sopenharmony_cistatic int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	if (xz_ret != XZ_STREAM_END) {
37862306a36Sopenharmony_ci		dev_warn(dev, "xz decompression failed (xz_ret=%d)\n", xz_ret);
37962306a36Sopenharmony_ci		return xz_ret == XZ_MEM_ERROR ? -ENOMEM : -EINVAL;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci/* single-shot decompression onto the pre-allocated buffer */
38562306a36Sopenharmony_cistatic int fw_decompress_xz_single(struct device *dev, struct fw_priv *fw_priv,
38662306a36Sopenharmony_ci				   size_t in_size, const void *in_buffer)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct xz_dec *xz_dec;
38962306a36Sopenharmony_ci	struct xz_buf xz_buf;
39062306a36Sopenharmony_ci	enum xz_ret xz_ret;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	xz_dec = xz_dec_init(XZ_SINGLE, (u32)-1);
39362306a36Sopenharmony_ci	if (!xz_dec)
39462306a36Sopenharmony_ci		return -ENOMEM;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	xz_buf.in_size = in_size;
39762306a36Sopenharmony_ci	xz_buf.in = in_buffer;
39862306a36Sopenharmony_ci	xz_buf.in_pos = 0;
39962306a36Sopenharmony_ci	xz_buf.out_size = fw_priv->allocated_size;
40062306a36Sopenharmony_ci	xz_buf.out = fw_priv->data;
40162306a36Sopenharmony_ci	xz_buf.out_pos = 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	xz_ret = xz_dec_run(xz_dec, &xz_buf);
40462306a36Sopenharmony_ci	xz_dec_end(xz_dec);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	fw_priv->size = xz_buf.out_pos;
40762306a36Sopenharmony_ci	return fw_decompress_xz_error(dev, xz_ret);
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/* decompression on paged buffer and map it */
41162306a36Sopenharmony_cistatic int fw_decompress_xz_pages(struct device *dev, struct fw_priv *fw_priv,
41262306a36Sopenharmony_ci				  size_t in_size, const void *in_buffer)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct xz_dec *xz_dec;
41562306a36Sopenharmony_ci	struct xz_buf xz_buf;
41662306a36Sopenharmony_ci	enum xz_ret xz_ret;
41762306a36Sopenharmony_ci	struct page *page;
41862306a36Sopenharmony_ci	int err = 0;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1);
42162306a36Sopenharmony_ci	if (!xz_dec)
42262306a36Sopenharmony_ci		return -ENOMEM;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	xz_buf.in_size = in_size;
42562306a36Sopenharmony_ci	xz_buf.in = in_buffer;
42662306a36Sopenharmony_ci	xz_buf.in_pos = 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	fw_priv->is_paged_buf = true;
42962306a36Sopenharmony_ci	fw_priv->size = 0;
43062306a36Sopenharmony_ci	do {
43162306a36Sopenharmony_ci		if (fw_grow_paged_buf(fw_priv, fw_priv->nr_pages + 1)) {
43262306a36Sopenharmony_ci			err = -ENOMEM;
43362306a36Sopenharmony_ci			goto out;
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		/* decompress onto the new allocated page */
43762306a36Sopenharmony_ci		page = fw_priv->pages[fw_priv->nr_pages - 1];
43862306a36Sopenharmony_ci		xz_buf.out = kmap_local_page(page);
43962306a36Sopenharmony_ci		xz_buf.out_pos = 0;
44062306a36Sopenharmony_ci		xz_buf.out_size = PAGE_SIZE;
44162306a36Sopenharmony_ci		xz_ret = xz_dec_run(xz_dec, &xz_buf);
44262306a36Sopenharmony_ci		kunmap_local(xz_buf.out);
44362306a36Sopenharmony_ci		fw_priv->size += xz_buf.out_pos;
44462306a36Sopenharmony_ci		/* partial decompression means either end or error */
44562306a36Sopenharmony_ci		if (xz_buf.out_pos != PAGE_SIZE)
44662306a36Sopenharmony_ci			break;
44762306a36Sopenharmony_ci	} while (xz_ret == XZ_OK);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	err = fw_decompress_xz_error(dev, xz_ret);
45062306a36Sopenharmony_ci	if (!err)
45162306a36Sopenharmony_ci		err = fw_map_paged_buf(fw_priv);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci out:
45462306a36Sopenharmony_ci	xz_dec_end(xz_dec);
45562306a36Sopenharmony_ci	return err;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
45962306a36Sopenharmony_ci			    size_t in_size, const void *in_buffer)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	/* if the buffer is pre-allocated, we can perform in single-shot mode */
46262306a36Sopenharmony_ci	if (fw_priv->data)
46362306a36Sopenharmony_ci		return fw_decompress_xz_single(dev, fw_priv, in_size, in_buffer);
46462306a36Sopenharmony_ci	else
46562306a36Sopenharmony_ci		return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci#endif /* CONFIG_FW_LOADER_COMPRESS_XZ */
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/* direct firmware loading support */
47062306a36Sopenharmony_cistatic char fw_path_para[256];
47162306a36Sopenharmony_cistatic const char * const fw_path[] = {
47262306a36Sopenharmony_ci	fw_path_para,
47362306a36Sopenharmony_ci	"/lib/firmware/updates/" UTS_RELEASE,
47462306a36Sopenharmony_ci	"/lib/firmware/updates",
47562306a36Sopenharmony_ci	"/lib/firmware/" UTS_RELEASE,
47662306a36Sopenharmony_ci	"/lib/firmware"
47762306a36Sopenharmony_ci};
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/*
48062306a36Sopenharmony_ci * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH'
48162306a36Sopenharmony_ci * from kernel command line because firmware_class is generally built in
48262306a36Sopenharmony_ci * kernel instead of module.
48362306a36Sopenharmony_ci */
48462306a36Sopenharmony_cimodule_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
48562306a36Sopenharmony_ciMODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int
48862306a36Sopenharmony_cifw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
48962306a36Sopenharmony_ci			   const char *suffix,
49062306a36Sopenharmony_ci			   int (*decompress)(struct device *dev,
49162306a36Sopenharmony_ci					     struct fw_priv *fw_priv,
49262306a36Sopenharmony_ci					     size_t in_size,
49362306a36Sopenharmony_ci					     const void *in_buffer))
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	size_t size;
49662306a36Sopenharmony_ci	int i, len, maxlen = 0;
49762306a36Sopenharmony_ci	int rc = -ENOENT;
49862306a36Sopenharmony_ci	char *path, *nt = NULL;
49962306a36Sopenharmony_ci	size_t msize = INT_MAX;
50062306a36Sopenharmony_ci	void *buffer = NULL;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Already populated data member means we're loading into a buffer */
50362306a36Sopenharmony_ci	if (!decompress && fw_priv->data) {
50462306a36Sopenharmony_ci		buffer = fw_priv->data;
50562306a36Sopenharmony_ci		msize = fw_priv->allocated_size;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	path = __getname();
50962306a36Sopenharmony_ci	if (!path)
51062306a36Sopenharmony_ci		return -ENOMEM;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	wait_for_initramfs();
51362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
51462306a36Sopenharmony_ci		size_t file_size = 0;
51562306a36Sopenharmony_ci		size_t *file_size_ptr = NULL;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		/* skip the unset customized path */
51862306a36Sopenharmony_ci		if (!fw_path[i][0])
51962306a36Sopenharmony_ci			continue;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		/* strip off \n from customized path */
52262306a36Sopenharmony_ci		maxlen = strlen(fw_path[i]);
52362306a36Sopenharmony_ci		if (i == 0) {
52462306a36Sopenharmony_ci			nt = strchr(fw_path[i], '\n');
52562306a36Sopenharmony_ci			if (nt)
52662306a36Sopenharmony_ci				maxlen = nt - fw_path[i];
52762306a36Sopenharmony_ci		}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		len = snprintf(path, PATH_MAX, "%.*s/%s%s",
53062306a36Sopenharmony_ci			       maxlen, fw_path[i],
53162306a36Sopenharmony_ci			       fw_priv->fw_name, suffix);
53262306a36Sopenharmony_ci		if (len >= PATH_MAX) {
53362306a36Sopenharmony_ci			rc = -ENAMETOOLONG;
53462306a36Sopenharmony_ci			break;
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		fw_priv->size = 0;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		/*
54062306a36Sopenharmony_ci		 * The total file size is only examined when doing a partial
54162306a36Sopenharmony_ci		 * read; the "full read" case needs to fail if the whole
54262306a36Sopenharmony_ci		 * firmware was not completely loaded.
54362306a36Sopenharmony_ci		 */
54462306a36Sopenharmony_ci		if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer)
54562306a36Sopenharmony_ci			file_size_ptr = &file_size;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		/* load firmware files from the mount namespace of init */
54862306a36Sopenharmony_ci		rc = kernel_read_file_from_path_initns(path, fw_priv->offset,
54962306a36Sopenharmony_ci						       &buffer, msize,
55062306a36Sopenharmony_ci						       file_size_ptr,
55162306a36Sopenharmony_ci						       READING_FIRMWARE);
55262306a36Sopenharmony_ci		if (rc < 0) {
55362306a36Sopenharmony_ci			if (rc != -ENOENT)
55462306a36Sopenharmony_ci				dev_warn(device, "loading %s failed with error %d\n",
55562306a36Sopenharmony_ci					 path, rc);
55662306a36Sopenharmony_ci			else
55762306a36Sopenharmony_ci				dev_dbg(device, "loading %s failed for no such file or directory.\n",
55862306a36Sopenharmony_ci					 path);
55962306a36Sopenharmony_ci			continue;
56062306a36Sopenharmony_ci		}
56162306a36Sopenharmony_ci		size = rc;
56262306a36Sopenharmony_ci		rc = 0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		dev_dbg(device, "Loading firmware from %s\n", path);
56562306a36Sopenharmony_ci		if (decompress) {
56662306a36Sopenharmony_ci			dev_dbg(device, "f/w decompressing %s\n",
56762306a36Sopenharmony_ci				fw_priv->fw_name);
56862306a36Sopenharmony_ci			rc = decompress(device, fw_priv, size, buffer);
56962306a36Sopenharmony_ci			/* discard the superfluous original content */
57062306a36Sopenharmony_ci			vfree(buffer);
57162306a36Sopenharmony_ci			buffer = NULL;
57262306a36Sopenharmony_ci			if (rc) {
57362306a36Sopenharmony_ci				fw_free_paged_buf(fw_priv);
57462306a36Sopenharmony_ci				continue;
57562306a36Sopenharmony_ci			}
57662306a36Sopenharmony_ci		} else {
57762306a36Sopenharmony_ci			dev_dbg(device, "direct-loading %s\n",
57862306a36Sopenharmony_ci				fw_priv->fw_name);
57962306a36Sopenharmony_ci			if (!fw_priv->data)
58062306a36Sopenharmony_ci				fw_priv->data = buffer;
58162306a36Sopenharmony_ci			fw_priv->size = size;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci		fw_state_done(fw_priv);
58462306a36Sopenharmony_ci		break;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	__putname(path);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return rc;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/* firmware holds the ownership of pages */
59262306a36Sopenharmony_cistatic void firmware_free_data(const struct firmware *fw)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	/* Loaded directly? */
59562306a36Sopenharmony_ci	if (!fw->priv) {
59662306a36Sopenharmony_ci		vfree(fw->data);
59762306a36Sopenharmony_ci		return;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci	free_fw_priv(fw->priv);
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci/* store the pages buffer info firmware from buf */
60362306a36Sopenharmony_cistatic void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	fw->priv = fw_priv;
60662306a36Sopenharmony_ci	fw->size = fw_priv->size;
60762306a36Sopenharmony_ci	fw->data = fw_priv->data;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
61062306a36Sopenharmony_ci		 __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
61162306a36Sopenharmony_ci		 (unsigned int)fw_priv->size);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci#ifdef CONFIG_FW_CACHE
61562306a36Sopenharmony_cistatic void fw_name_devm_release(struct device *dev, void *res)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct fw_name_devm *fwn = res;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (fwn->magic == (unsigned long)&fw_cache)
62062306a36Sopenharmony_ci		pr_debug("%s: fw_name-%s devm-%p released\n",
62162306a36Sopenharmony_ci				__func__, fwn->name, res);
62262306a36Sopenharmony_ci	kfree_const(fwn->name);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int fw_devm_match(struct device *dev, void *res,
62662306a36Sopenharmony_ci		void *match_data)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct fw_name_devm *fwn = res;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return (fwn->magic == (unsigned long)&fw_cache) &&
63162306a36Sopenharmony_ci		!strcmp(fwn->name, match_data);
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic struct fw_name_devm *fw_find_devm_name(struct device *dev,
63562306a36Sopenharmony_ci		const char *name)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct fw_name_devm *fwn;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	fwn = devres_find(dev, fw_name_devm_release,
64062306a36Sopenharmony_ci			  fw_devm_match, (void *)name);
64162306a36Sopenharmony_ci	return fwn;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic bool fw_cache_is_setup(struct device *dev, const char *name)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct fw_name_devm *fwn;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	fwn = fw_find_devm_name(dev, name);
64962306a36Sopenharmony_ci	if (fwn)
65062306a36Sopenharmony_ci		return true;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return false;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/* add firmware name into devres list */
65662306a36Sopenharmony_cistatic int fw_add_devm_name(struct device *dev, const char *name)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct fw_name_devm *fwn;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (fw_cache_is_setup(dev, name))
66162306a36Sopenharmony_ci		return 0;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm),
66462306a36Sopenharmony_ci			   GFP_KERNEL);
66562306a36Sopenharmony_ci	if (!fwn)
66662306a36Sopenharmony_ci		return -ENOMEM;
66762306a36Sopenharmony_ci	fwn->name = kstrdup_const(name, GFP_KERNEL);
66862306a36Sopenharmony_ci	if (!fwn->name) {
66962306a36Sopenharmony_ci		devres_free(fwn);
67062306a36Sopenharmony_ci		return -ENOMEM;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	fwn->magic = (unsigned long)&fw_cache;
67462306a36Sopenharmony_ci	devres_add(dev, fwn);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	return 0;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci#else
67962306a36Sopenharmony_cistatic bool fw_cache_is_setup(struct device *dev, const char *name)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	return false;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int fw_add_devm_name(struct device *dev, const char *name)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci#endif
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ciint assign_fw(struct firmware *fw, struct device *device)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct fw_priv *fw_priv = fw->priv;
69362306a36Sopenharmony_ci	int ret;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	mutex_lock(&fw_lock);
69662306a36Sopenharmony_ci	if (!fw_priv->size || fw_state_is_aborted(fw_priv)) {
69762306a36Sopenharmony_ci		mutex_unlock(&fw_lock);
69862306a36Sopenharmony_ci		return -ENOENT;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/*
70262306a36Sopenharmony_ci	 * add firmware name into devres list so that we can auto cache
70362306a36Sopenharmony_ci	 * and uncache firmware for device.
70462306a36Sopenharmony_ci	 *
70562306a36Sopenharmony_ci	 * device may has been deleted already, but the problem
70662306a36Sopenharmony_ci	 * should be fixed in devres or driver core.
70762306a36Sopenharmony_ci	 */
70862306a36Sopenharmony_ci	/* don't cache firmware handled without uevent */
70962306a36Sopenharmony_ci	if (device && (fw_priv->opt_flags & FW_OPT_UEVENT) &&
71062306a36Sopenharmony_ci	    !(fw_priv->opt_flags & FW_OPT_NOCACHE)) {
71162306a36Sopenharmony_ci		ret = fw_add_devm_name(device, fw_priv->fw_name);
71262306a36Sopenharmony_ci		if (ret) {
71362306a36Sopenharmony_ci			mutex_unlock(&fw_lock);
71462306a36Sopenharmony_ci			return ret;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/*
71962306a36Sopenharmony_ci	 * After caching firmware image is started, let it piggyback
72062306a36Sopenharmony_ci	 * on request firmware.
72162306a36Sopenharmony_ci	 */
72262306a36Sopenharmony_ci	if (!(fw_priv->opt_flags & FW_OPT_NOCACHE) &&
72362306a36Sopenharmony_ci	    fw_priv->fwc->state == FW_LOADER_START_CACHE)
72462306a36Sopenharmony_ci		fw_cache_piggyback_on_request(fw_priv);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* pass the pages buffer to driver at the last minute */
72762306a36Sopenharmony_ci	fw_set_page_data(fw_priv, fw);
72862306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/* prepare firmware and firmware_buf structs;
73362306a36Sopenharmony_ci * return 0 if a firmware is already assigned, 1 if need to load one,
73462306a36Sopenharmony_ci * or a negative error code
73562306a36Sopenharmony_ci */
73662306a36Sopenharmony_cistatic int
73762306a36Sopenharmony_ci_request_firmware_prepare(struct firmware **firmware_p, const char *name,
73862306a36Sopenharmony_ci			  struct device *device, void *dbuf, size_t size,
73962306a36Sopenharmony_ci			  size_t offset, u32 opt_flags)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct firmware *firmware;
74262306a36Sopenharmony_ci	struct fw_priv *fw_priv;
74362306a36Sopenharmony_ci	int ret;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
74662306a36Sopenharmony_ci	if (!firmware) {
74762306a36Sopenharmony_ci		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
74862306a36Sopenharmony_ci			__func__);
74962306a36Sopenharmony_ci		return -ENOMEM;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (firmware_request_builtin_buf(firmware, name, dbuf, size)) {
75362306a36Sopenharmony_ci		dev_dbg(device, "using built-in %s\n", name);
75462306a36Sopenharmony_ci		return 0; /* assigned */
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size,
75862306a36Sopenharmony_ci				   offset, opt_flags);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/*
76162306a36Sopenharmony_ci	 * bind with 'priv' now to avoid warning in failure path
76262306a36Sopenharmony_ci	 * of requesting firmware.
76362306a36Sopenharmony_ci	 */
76462306a36Sopenharmony_ci	firmware->priv = fw_priv;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (ret > 0) {
76762306a36Sopenharmony_ci		ret = fw_state_wait(fw_priv);
76862306a36Sopenharmony_ci		if (!ret) {
76962306a36Sopenharmony_ci			fw_set_page_data(fw_priv, firmware);
77062306a36Sopenharmony_ci			return 0; /* assigned */
77162306a36Sopenharmony_ci		}
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (ret < 0)
77562306a36Sopenharmony_ci		return ret;
77662306a36Sopenharmony_ci	return 1; /* need to load */
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/*
78062306a36Sopenharmony_ci * Batched requests need only one wake, we need to do this step last due to the
78162306a36Sopenharmony_ci * fallback mechanism. The buf is protected with kref_get(), and it won't be
78262306a36Sopenharmony_ci * released until the last user calls release_firmware().
78362306a36Sopenharmony_ci *
78462306a36Sopenharmony_ci * Failed batched requests are possible as well, in such cases we just share
78562306a36Sopenharmony_ci * the struct fw_priv and won't release it until all requests are woken
78662306a36Sopenharmony_ci * and have gone through this same path.
78762306a36Sopenharmony_ci */
78862306a36Sopenharmony_cistatic void fw_abort_batch_reqs(struct firmware *fw)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct fw_priv *fw_priv;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* Loaded directly? */
79362306a36Sopenharmony_ci	if (!fw || !fw->priv)
79462306a36Sopenharmony_ci		return;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	fw_priv = fw->priv;
79762306a36Sopenharmony_ci	mutex_lock(&fw_lock);
79862306a36Sopenharmony_ci	if (!fw_state_is_aborted(fw_priv))
79962306a36Sopenharmony_ci		fw_state_aborted(fw_priv);
80062306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci#if defined(CONFIG_FW_LOADER_DEBUG)
80462306a36Sopenharmony_ci#include <crypto/hash.h>
80562306a36Sopenharmony_ci#include <crypto/sha2.h>
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic void fw_log_firmware_info(const struct firmware *fw, const char *name, struct device *device)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	struct shash_desc *shash;
81062306a36Sopenharmony_ci	struct crypto_shash *alg;
81162306a36Sopenharmony_ci	u8 *sha256buf;
81262306a36Sopenharmony_ci	char *outbuf;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	alg = crypto_alloc_shash("sha256", 0, 0);
81562306a36Sopenharmony_ci	if (IS_ERR(alg))
81662306a36Sopenharmony_ci		return;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	sha256buf = kmalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
81962306a36Sopenharmony_ci	outbuf = kmalloc(SHA256_BLOCK_SIZE + 1, GFP_KERNEL);
82062306a36Sopenharmony_ci	shash = kmalloc(sizeof(*shash) + crypto_shash_descsize(alg), GFP_KERNEL);
82162306a36Sopenharmony_ci	if (!sha256buf || !outbuf || !shash)
82262306a36Sopenharmony_ci		goto out_free;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	shash->tfm = alg;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (crypto_shash_digest(shash, fw->data, fw->size, sha256buf) < 0)
82762306a36Sopenharmony_ci		goto out_shash;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	for (int i = 0; i < SHA256_DIGEST_SIZE; i++)
83062306a36Sopenharmony_ci		sprintf(&outbuf[i * 2], "%02x", sha256buf[i]);
83162306a36Sopenharmony_ci	outbuf[SHA256_BLOCK_SIZE] = 0;
83262306a36Sopenharmony_ci	dev_dbg(device, "Loaded FW: %s, sha256: %s\n", name, outbuf);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ciout_shash:
83562306a36Sopenharmony_ci	crypto_free_shash(alg);
83662306a36Sopenharmony_ciout_free:
83762306a36Sopenharmony_ci	kfree(shash);
83862306a36Sopenharmony_ci	kfree(outbuf);
83962306a36Sopenharmony_ci	kfree(sha256buf);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci#else
84262306a36Sopenharmony_cistatic void fw_log_firmware_info(const struct firmware *fw, const char *name,
84362306a36Sopenharmony_ci				 struct device *device)
84462306a36Sopenharmony_ci{}
84562306a36Sopenharmony_ci#endif
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci/* called from request_firmware() and request_firmware_work_func() */
84862306a36Sopenharmony_cistatic int
84962306a36Sopenharmony_ci_request_firmware(const struct firmware **firmware_p, const char *name,
85062306a36Sopenharmony_ci		  struct device *device, void *buf, size_t size,
85162306a36Sopenharmony_ci		  size_t offset, u32 opt_flags)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct firmware *fw = NULL;
85462306a36Sopenharmony_ci	struct cred *kern_cred = NULL;
85562306a36Sopenharmony_ci	const struct cred *old_cred;
85662306a36Sopenharmony_ci	bool nondirect = false;
85762306a36Sopenharmony_ci	int ret;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (!firmware_p)
86062306a36Sopenharmony_ci		return -EINVAL;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	if (!name || name[0] == '\0') {
86362306a36Sopenharmony_ci		ret = -EINVAL;
86462306a36Sopenharmony_ci		goto out;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	ret = _request_firmware_prepare(&fw, name, device, buf, size,
86862306a36Sopenharmony_ci					offset, opt_flags);
86962306a36Sopenharmony_ci	if (ret <= 0) /* error or already assigned */
87062306a36Sopenharmony_ci		goto out;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/*
87362306a36Sopenharmony_ci	 * We are about to try to access the firmware file. Because we may have been
87462306a36Sopenharmony_ci	 * called by a driver when serving an unrelated request from userland, we use
87562306a36Sopenharmony_ci	 * the kernel credentials to read the file.
87662306a36Sopenharmony_ci	 */
87762306a36Sopenharmony_ci	kern_cred = prepare_kernel_cred(&init_task);
87862306a36Sopenharmony_ci	if (!kern_cred) {
87962306a36Sopenharmony_ci		ret = -ENOMEM;
88062306a36Sopenharmony_ci		goto out;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci	old_cred = override_creds(kern_cred);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/* Only full reads can support decompression, platform, and sysfs. */
88762306a36Sopenharmony_ci	if (!(opt_flags & FW_OPT_PARTIAL))
88862306a36Sopenharmony_ci		nondirect = true;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
89162306a36Sopenharmony_ci	if (ret == -ENOENT && nondirect)
89262306a36Sopenharmony_ci		ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
89362306a36Sopenharmony_ci						 fw_decompress_zstd);
89462306a36Sopenharmony_ci#endif
89562306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
89662306a36Sopenharmony_ci	if (ret == -ENOENT && nondirect)
89762306a36Sopenharmony_ci		ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
89862306a36Sopenharmony_ci						 fw_decompress_xz);
89962306a36Sopenharmony_ci#endif
90062306a36Sopenharmony_ci	if (ret == -ENOENT && nondirect)
90162306a36Sopenharmony_ci		ret = firmware_fallback_platform(fw->priv);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (ret) {
90462306a36Sopenharmony_ci		if (!(opt_flags & FW_OPT_NO_WARN))
90562306a36Sopenharmony_ci			dev_warn(device,
90662306a36Sopenharmony_ci				 "Direct firmware load for %s failed with error %d\n",
90762306a36Sopenharmony_ci				 name, ret);
90862306a36Sopenharmony_ci		if (nondirect)
90962306a36Sopenharmony_ci			ret = firmware_fallback_sysfs(fw, name, device,
91062306a36Sopenharmony_ci						      opt_flags, ret);
91162306a36Sopenharmony_ci	} else
91262306a36Sopenharmony_ci		ret = assign_fw(fw, device);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	revert_creds(old_cred);
91562306a36Sopenharmony_ci	put_cred(kern_cred);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ciout:
91862306a36Sopenharmony_ci	if (ret < 0) {
91962306a36Sopenharmony_ci		fw_abort_batch_reqs(fw);
92062306a36Sopenharmony_ci		release_firmware(fw);
92162306a36Sopenharmony_ci		fw = NULL;
92262306a36Sopenharmony_ci	} else {
92362306a36Sopenharmony_ci		fw_log_firmware_info(fw, name, device);
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	*firmware_p = fw;
92762306a36Sopenharmony_ci	return ret;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci/**
93162306a36Sopenharmony_ci * request_firmware() - send firmware request and wait for it
93262306a36Sopenharmony_ci * @firmware_p: pointer to firmware image
93362306a36Sopenharmony_ci * @name: name of firmware file
93462306a36Sopenharmony_ci * @device: device for which firmware is being loaded
93562306a36Sopenharmony_ci *
93662306a36Sopenharmony_ci *      @firmware_p will be used to return a firmware image by the name
93762306a36Sopenharmony_ci *      of @name for device @device.
93862306a36Sopenharmony_ci *
93962306a36Sopenharmony_ci *      Should be called from user context where sleeping is allowed.
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci *      @name will be used as $FIRMWARE in the uevent environment and
94262306a36Sopenharmony_ci *      should be distinctive enough not to be confused with any other
94362306a36Sopenharmony_ci *      firmware image for this or any other device.
94462306a36Sopenharmony_ci *
94562306a36Sopenharmony_ci *	Caller must hold the reference count of @device.
94662306a36Sopenharmony_ci *
94762306a36Sopenharmony_ci *	The function can be called safely inside device's suspend and
94862306a36Sopenharmony_ci *	resume callback.
94962306a36Sopenharmony_ci **/
95062306a36Sopenharmony_ciint
95162306a36Sopenharmony_cirequest_firmware(const struct firmware **firmware_p, const char *name,
95262306a36Sopenharmony_ci		 struct device *device)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	int ret;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Need to pin this module until return */
95762306a36Sopenharmony_ci	__module_get(THIS_MODULE);
95862306a36Sopenharmony_ci	ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
95962306a36Sopenharmony_ci				FW_OPT_UEVENT);
96062306a36Sopenharmony_ci	module_put(THIS_MODULE);
96162306a36Sopenharmony_ci	return ret;
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ciEXPORT_SYMBOL(request_firmware);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci/**
96662306a36Sopenharmony_ci * firmware_request_nowarn() - request for an optional fw module
96762306a36Sopenharmony_ci * @firmware: pointer to firmware image
96862306a36Sopenharmony_ci * @name: name of firmware file
96962306a36Sopenharmony_ci * @device: device for which firmware is being loaded
97062306a36Sopenharmony_ci *
97162306a36Sopenharmony_ci * This function is similar in behaviour to request_firmware(), except it
97262306a36Sopenharmony_ci * doesn't produce warning messages when the file is not found. The sysfs
97362306a36Sopenharmony_ci * fallback mechanism is enabled if direct filesystem lookup fails. However,
97462306a36Sopenharmony_ci * failures to find the firmware file with it are still suppressed. It is
97562306a36Sopenharmony_ci * therefore up to the driver to check for the return value of this call and to
97662306a36Sopenharmony_ci * decide when to inform the users of errors.
97762306a36Sopenharmony_ci **/
97862306a36Sopenharmony_ciint firmware_request_nowarn(const struct firmware **firmware, const char *name,
97962306a36Sopenharmony_ci			    struct device *device)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	int ret;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Need to pin this module until return */
98462306a36Sopenharmony_ci	__module_get(THIS_MODULE);
98562306a36Sopenharmony_ci	ret = _request_firmware(firmware, name, device, NULL, 0, 0,
98662306a36Sopenharmony_ci				FW_OPT_UEVENT | FW_OPT_NO_WARN);
98762306a36Sopenharmony_ci	module_put(THIS_MODULE);
98862306a36Sopenharmony_ci	return ret;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(firmware_request_nowarn);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci/**
99362306a36Sopenharmony_ci * request_firmware_direct() - load firmware directly without usermode helper
99462306a36Sopenharmony_ci * @firmware_p: pointer to firmware image
99562306a36Sopenharmony_ci * @name: name of firmware file
99662306a36Sopenharmony_ci * @device: device for which firmware is being loaded
99762306a36Sopenharmony_ci *
99862306a36Sopenharmony_ci * This function works pretty much like request_firmware(), but this doesn't
99962306a36Sopenharmony_ci * fall back to usermode helper even if the firmware couldn't be loaded
100062306a36Sopenharmony_ci * directly from fs.  Hence it's useful for loading optional firmwares, which
100162306a36Sopenharmony_ci * aren't always present, without extra long timeouts of udev.
100262306a36Sopenharmony_ci **/
100362306a36Sopenharmony_ciint request_firmware_direct(const struct firmware **firmware_p,
100462306a36Sopenharmony_ci			    const char *name, struct device *device)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	int ret;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	__module_get(THIS_MODULE);
100962306a36Sopenharmony_ci	ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
101062306a36Sopenharmony_ci				FW_OPT_UEVENT | FW_OPT_NO_WARN |
101162306a36Sopenharmony_ci				FW_OPT_NOFALLBACK_SYSFS);
101262306a36Sopenharmony_ci	module_put(THIS_MODULE);
101362306a36Sopenharmony_ci	return ret;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(request_firmware_direct);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci/**
101862306a36Sopenharmony_ci * firmware_request_platform() - request firmware with platform-fw fallback
101962306a36Sopenharmony_ci * @firmware: pointer to firmware image
102062306a36Sopenharmony_ci * @name: name of firmware file
102162306a36Sopenharmony_ci * @device: device for which firmware is being loaded
102262306a36Sopenharmony_ci *
102362306a36Sopenharmony_ci * This function is similar in behaviour to request_firmware, except that if
102462306a36Sopenharmony_ci * direct filesystem lookup fails, it will fallback to looking for a copy of the
102562306a36Sopenharmony_ci * requested firmware embedded in the platform's main (e.g. UEFI) firmware.
102662306a36Sopenharmony_ci **/
102762306a36Sopenharmony_ciint firmware_request_platform(const struct firmware **firmware,
102862306a36Sopenharmony_ci			      const char *name, struct device *device)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	int ret;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Need to pin this module until return */
103362306a36Sopenharmony_ci	__module_get(THIS_MODULE);
103462306a36Sopenharmony_ci	ret = _request_firmware(firmware, name, device, NULL, 0, 0,
103562306a36Sopenharmony_ci				FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM);
103662306a36Sopenharmony_ci	module_put(THIS_MODULE);
103762306a36Sopenharmony_ci	return ret;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(firmware_request_platform);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci/**
104262306a36Sopenharmony_ci * firmware_request_cache() - cache firmware for suspend so resume can use it
104362306a36Sopenharmony_ci * @name: name of firmware file
104462306a36Sopenharmony_ci * @device: device for which firmware should be cached for
104562306a36Sopenharmony_ci *
104662306a36Sopenharmony_ci * There are some devices with an optimization that enables the device to not
104762306a36Sopenharmony_ci * require loading firmware on system reboot. This optimization may still
104862306a36Sopenharmony_ci * require the firmware present on resume from suspend. This routine can be
104962306a36Sopenharmony_ci * used to ensure the firmware is present on resume from suspend in these
105062306a36Sopenharmony_ci * situations. This helper is not compatible with drivers which use
105162306a36Sopenharmony_ci * request_firmware_into_buf() or request_firmware_nowait() with no uevent set.
105262306a36Sopenharmony_ci **/
105362306a36Sopenharmony_ciint firmware_request_cache(struct device *device, const char *name)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	int ret;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	mutex_lock(&fw_lock);
105862306a36Sopenharmony_ci	ret = fw_add_devm_name(device, name);
105962306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	return ret;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(firmware_request_cache);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci/**
106662306a36Sopenharmony_ci * request_firmware_into_buf() - load firmware into a previously allocated buffer
106762306a36Sopenharmony_ci * @firmware_p: pointer to firmware image
106862306a36Sopenharmony_ci * @name: name of firmware file
106962306a36Sopenharmony_ci * @device: device for which firmware is being loaded and DMA region allocated
107062306a36Sopenharmony_ci * @buf: address of buffer to load firmware into
107162306a36Sopenharmony_ci * @size: size of buffer
107262306a36Sopenharmony_ci *
107362306a36Sopenharmony_ci * This function works pretty much like request_firmware(), but it doesn't
107462306a36Sopenharmony_ci * allocate a buffer to hold the firmware data. Instead, the firmware
107562306a36Sopenharmony_ci * is loaded directly into the buffer pointed to by @buf and the @firmware_p
107662306a36Sopenharmony_ci * data member is pointed at @buf.
107762306a36Sopenharmony_ci *
107862306a36Sopenharmony_ci * This function doesn't cache firmware either.
107962306a36Sopenharmony_ci */
108062306a36Sopenharmony_ciint
108162306a36Sopenharmony_cirequest_firmware_into_buf(const struct firmware **firmware_p, const char *name,
108262306a36Sopenharmony_ci			  struct device *device, void *buf, size_t size)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	int ret;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (fw_cache_is_setup(device, name))
108762306a36Sopenharmony_ci		return -EOPNOTSUPP;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	__module_get(THIS_MODULE);
109062306a36Sopenharmony_ci	ret = _request_firmware(firmware_p, name, device, buf, size, 0,
109162306a36Sopenharmony_ci				FW_OPT_UEVENT | FW_OPT_NOCACHE);
109262306a36Sopenharmony_ci	module_put(THIS_MODULE);
109362306a36Sopenharmony_ci	return ret;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ciEXPORT_SYMBOL(request_firmware_into_buf);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci/**
109862306a36Sopenharmony_ci * request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer
109962306a36Sopenharmony_ci * @firmware_p: pointer to firmware image
110062306a36Sopenharmony_ci * @name: name of firmware file
110162306a36Sopenharmony_ci * @device: device for which firmware is being loaded and DMA region allocated
110262306a36Sopenharmony_ci * @buf: address of buffer to load firmware into
110362306a36Sopenharmony_ci * @size: size of buffer
110462306a36Sopenharmony_ci * @offset: offset into file to read
110562306a36Sopenharmony_ci *
110662306a36Sopenharmony_ci * This function works pretty much like request_firmware_into_buf except
110762306a36Sopenharmony_ci * it allows a partial read of the file.
110862306a36Sopenharmony_ci */
110962306a36Sopenharmony_ciint
111062306a36Sopenharmony_cirequest_partial_firmware_into_buf(const struct firmware **firmware_p,
111162306a36Sopenharmony_ci				  const char *name, struct device *device,
111262306a36Sopenharmony_ci				  void *buf, size_t size, size_t offset)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	int ret;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (fw_cache_is_setup(device, name))
111762306a36Sopenharmony_ci		return -EOPNOTSUPP;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	__module_get(THIS_MODULE);
112062306a36Sopenharmony_ci	ret = _request_firmware(firmware_p, name, device, buf, size, offset,
112162306a36Sopenharmony_ci				FW_OPT_UEVENT | FW_OPT_NOCACHE |
112262306a36Sopenharmony_ci				FW_OPT_PARTIAL);
112362306a36Sopenharmony_ci	module_put(THIS_MODULE);
112462306a36Sopenharmony_ci	return ret;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ciEXPORT_SYMBOL(request_partial_firmware_into_buf);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci/**
112962306a36Sopenharmony_ci * release_firmware() - release the resource associated with a firmware image
113062306a36Sopenharmony_ci * @fw: firmware resource to release
113162306a36Sopenharmony_ci **/
113262306a36Sopenharmony_civoid release_firmware(const struct firmware *fw)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	if (fw) {
113562306a36Sopenharmony_ci		if (!firmware_is_builtin(fw))
113662306a36Sopenharmony_ci			firmware_free_data(fw);
113762306a36Sopenharmony_ci		kfree(fw);
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ciEXPORT_SYMBOL(release_firmware);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci/* Async support */
114362306a36Sopenharmony_cistruct firmware_work {
114462306a36Sopenharmony_ci	struct work_struct work;
114562306a36Sopenharmony_ci	struct module *module;
114662306a36Sopenharmony_ci	const char *name;
114762306a36Sopenharmony_ci	struct device *device;
114862306a36Sopenharmony_ci	void *context;
114962306a36Sopenharmony_ci	void (*cont)(const struct firmware *fw, void *context);
115062306a36Sopenharmony_ci	u32 opt_flags;
115162306a36Sopenharmony_ci};
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic void request_firmware_work_func(struct work_struct *work)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct firmware_work *fw_work;
115662306a36Sopenharmony_ci	const struct firmware *fw;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	fw_work = container_of(work, struct firmware_work, work);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
116162306a36Sopenharmony_ci			  fw_work->opt_flags);
116262306a36Sopenharmony_ci	fw_work->cont(fw, fw_work->context);
116362306a36Sopenharmony_ci	put_device(fw_work->device); /* taken in request_firmware_nowait() */
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	module_put(fw_work->module);
116662306a36Sopenharmony_ci	kfree_const(fw_work->name);
116762306a36Sopenharmony_ci	kfree(fw_work);
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci/**
117162306a36Sopenharmony_ci * request_firmware_nowait() - asynchronous version of request_firmware
117262306a36Sopenharmony_ci * @module: module requesting the firmware
117362306a36Sopenharmony_ci * @uevent: sends uevent to copy the firmware image if this flag
117462306a36Sopenharmony_ci *	is non-zero else the firmware copy must be done manually.
117562306a36Sopenharmony_ci * @name: name of firmware file
117662306a36Sopenharmony_ci * @device: device for which firmware is being loaded
117762306a36Sopenharmony_ci * @gfp: allocation flags
117862306a36Sopenharmony_ci * @context: will be passed over to @cont, and
117962306a36Sopenharmony_ci *	@fw may be %NULL if firmware request fails.
118062306a36Sopenharmony_ci * @cont: function will be called asynchronously when the firmware
118162306a36Sopenharmony_ci *	request is over.
118262306a36Sopenharmony_ci *
118362306a36Sopenharmony_ci *	Caller must hold the reference count of @device.
118462306a36Sopenharmony_ci *
118562306a36Sopenharmony_ci *	Asynchronous variant of request_firmware() for user contexts:
118662306a36Sopenharmony_ci *		- sleep for as small periods as possible since it may
118762306a36Sopenharmony_ci *		  increase kernel boot time of built-in device drivers
118862306a36Sopenharmony_ci *		  requesting firmware in their ->probe() methods, if
118962306a36Sopenharmony_ci *		  @gfp is GFP_KERNEL.
119062306a36Sopenharmony_ci *
119162306a36Sopenharmony_ci *		- can't sleep at all if @gfp is GFP_ATOMIC.
119262306a36Sopenharmony_ci **/
119362306a36Sopenharmony_ciint
119462306a36Sopenharmony_cirequest_firmware_nowait(
119562306a36Sopenharmony_ci	struct module *module, bool uevent,
119662306a36Sopenharmony_ci	const char *name, struct device *device, gfp_t gfp, void *context,
119762306a36Sopenharmony_ci	void (*cont)(const struct firmware *fw, void *context))
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	struct firmware_work *fw_work;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	fw_work = kzalloc(sizeof(struct firmware_work), gfp);
120262306a36Sopenharmony_ci	if (!fw_work)
120362306a36Sopenharmony_ci		return -ENOMEM;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	fw_work->module = module;
120662306a36Sopenharmony_ci	fw_work->name = kstrdup_const(name, gfp);
120762306a36Sopenharmony_ci	if (!fw_work->name) {
120862306a36Sopenharmony_ci		kfree(fw_work);
120962306a36Sopenharmony_ci		return -ENOMEM;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci	fw_work->device = device;
121262306a36Sopenharmony_ci	fw_work->context = context;
121362306a36Sopenharmony_ci	fw_work->cont = cont;
121462306a36Sopenharmony_ci	fw_work->opt_flags = FW_OPT_NOWAIT |
121562306a36Sopenharmony_ci		(uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	if (!uevent && fw_cache_is_setup(device, name)) {
121862306a36Sopenharmony_ci		kfree_const(fw_work->name);
121962306a36Sopenharmony_ci		kfree(fw_work);
122062306a36Sopenharmony_ci		return -EOPNOTSUPP;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (!try_module_get(module)) {
122462306a36Sopenharmony_ci		kfree_const(fw_work->name);
122562306a36Sopenharmony_ci		kfree(fw_work);
122662306a36Sopenharmony_ci		return -EFAULT;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	get_device(fw_work->device);
123062306a36Sopenharmony_ci	INIT_WORK(&fw_work->work, request_firmware_work_func);
123162306a36Sopenharmony_ci	schedule_work(&fw_work->work);
123262306a36Sopenharmony_ci	return 0;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ciEXPORT_SYMBOL(request_firmware_nowait);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci#ifdef CONFIG_FW_CACHE
123762306a36Sopenharmony_cistatic ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci/**
124062306a36Sopenharmony_ci * cache_firmware() - cache one firmware image in kernel memory space
124162306a36Sopenharmony_ci * @fw_name: the firmware image name
124262306a36Sopenharmony_ci *
124362306a36Sopenharmony_ci * Cache firmware in kernel memory so that drivers can use it when
124462306a36Sopenharmony_ci * system isn't ready for them to request firmware image from userspace.
124562306a36Sopenharmony_ci * Once it returns successfully, driver can use request_firmware or its
124662306a36Sopenharmony_ci * nowait version to get the cached firmware without any interacting
124762306a36Sopenharmony_ci * with userspace
124862306a36Sopenharmony_ci *
124962306a36Sopenharmony_ci * Return 0 if the firmware image has been cached successfully
125062306a36Sopenharmony_ci * Return !0 otherwise
125162306a36Sopenharmony_ci *
125262306a36Sopenharmony_ci */
125362306a36Sopenharmony_cistatic int cache_firmware(const char *fw_name)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	int ret;
125662306a36Sopenharmony_ci	const struct firmware *fw;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	pr_debug("%s: %s\n", __func__, fw_name);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	ret = request_firmware(&fw, fw_name, NULL);
126162306a36Sopenharmony_ci	if (!ret)
126262306a36Sopenharmony_ci		kfree(fw);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return ret;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic struct fw_priv *lookup_fw_priv(const char *fw_name)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct fw_priv *tmp;
127262306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	spin_lock(&fwc->lock);
127562306a36Sopenharmony_ci	tmp = __lookup_fw_priv(fw_name);
127662306a36Sopenharmony_ci	spin_unlock(&fwc->lock);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	return tmp;
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci/**
128262306a36Sopenharmony_ci * uncache_firmware() - remove one cached firmware image
128362306a36Sopenharmony_ci * @fw_name: the firmware image name
128462306a36Sopenharmony_ci *
128562306a36Sopenharmony_ci * Uncache one firmware image which has been cached successfully
128662306a36Sopenharmony_ci * before.
128762306a36Sopenharmony_ci *
128862306a36Sopenharmony_ci * Return 0 if the firmware cache has been removed successfully
128962306a36Sopenharmony_ci * Return !0 otherwise
129062306a36Sopenharmony_ci *
129162306a36Sopenharmony_ci */
129262306a36Sopenharmony_cistatic int uncache_firmware(const char *fw_name)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	struct fw_priv *fw_priv;
129562306a36Sopenharmony_ci	struct firmware fw;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	pr_debug("%s: %s\n", __func__, fw_name);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (firmware_request_builtin(&fw, fw_name))
130062306a36Sopenharmony_ci		return 0;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	fw_priv = lookup_fw_priv(fw_name);
130362306a36Sopenharmony_ci	if (fw_priv) {
130462306a36Sopenharmony_ci		free_fw_priv(fw_priv);
130562306a36Sopenharmony_ci		return 0;
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return -EINVAL;
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct fw_cache_entry *fce;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	fce = kzalloc(sizeof(*fce), GFP_ATOMIC);
131662306a36Sopenharmony_ci	if (!fce)
131762306a36Sopenharmony_ci		goto exit;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	fce->name = kstrdup_const(name, GFP_ATOMIC);
132062306a36Sopenharmony_ci	if (!fce->name) {
132162306a36Sopenharmony_ci		kfree(fce);
132262306a36Sopenharmony_ci		fce = NULL;
132362306a36Sopenharmony_ci		goto exit;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ciexit:
132662306a36Sopenharmony_ci	return fce;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic int __fw_entry_found(const char *name)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
133262306a36Sopenharmony_ci	struct fw_cache_entry *fce;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	list_for_each_entry(fce, &fwc->fw_names, list) {
133562306a36Sopenharmony_ci		if (!strcmp(fce->name, name))
133662306a36Sopenharmony_ci			return 1;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci	return 0;
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_cistatic void fw_cache_piggyback_on_request(struct fw_priv *fw_priv)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	const char *name = fw_priv->fw_name;
134462306a36Sopenharmony_ci	struct firmware_cache *fwc = fw_priv->fwc;
134562306a36Sopenharmony_ci	struct fw_cache_entry *fce;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	spin_lock(&fwc->name_lock);
134862306a36Sopenharmony_ci	if (__fw_entry_found(name))
134962306a36Sopenharmony_ci		goto found;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	fce = alloc_fw_cache_entry(name);
135262306a36Sopenharmony_ci	if (fce) {
135362306a36Sopenharmony_ci		list_add(&fce->list, &fwc->fw_names);
135462306a36Sopenharmony_ci		kref_get(&fw_priv->ref);
135562306a36Sopenharmony_ci		pr_debug("%s: fw: %s\n", __func__, name);
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_cifound:
135862306a36Sopenharmony_ci	spin_unlock(&fwc->name_lock);
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic void free_fw_cache_entry(struct fw_cache_entry *fce)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	kfree_const(fce->name);
136462306a36Sopenharmony_ci	kfree(fce);
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic void __async_dev_cache_fw_image(void *fw_entry,
136862306a36Sopenharmony_ci				       async_cookie_t cookie)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	struct fw_cache_entry *fce = fw_entry;
137162306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
137262306a36Sopenharmony_ci	int ret;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	ret = cache_firmware(fce->name);
137562306a36Sopenharmony_ci	if (ret) {
137662306a36Sopenharmony_ci		spin_lock(&fwc->name_lock);
137762306a36Sopenharmony_ci		list_del(&fce->list);
137862306a36Sopenharmony_ci		spin_unlock(&fwc->name_lock);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		free_fw_cache_entry(fce);
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci/* called with dev->devres_lock held */
138562306a36Sopenharmony_cistatic void dev_create_fw_entry(struct device *dev, void *res,
138662306a36Sopenharmony_ci				void *data)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	struct fw_name_devm *fwn = res;
138962306a36Sopenharmony_ci	const char *fw_name = fwn->name;
139062306a36Sopenharmony_ci	struct list_head *head = data;
139162306a36Sopenharmony_ci	struct fw_cache_entry *fce;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	fce = alloc_fw_cache_entry(fw_name);
139462306a36Sopenharmony_ci	if (fce)
139562306a36Sopenharmony_ci		list_add(&fce->list, head);
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistatic int devm_name_match(struct device *dev, void *res,
139962306a36Sopenharmony_ci			   void *match_data)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	struct fw_name_devm *fwn = res;
140262306a36Sopenharmony_ci	return (fwn->magic == (unsigned long)match_data);
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic void dev_cache_fw_image(struct device *dev, void *data)
140662306a36Sopenharmony_ci{
140762306a36Sopenharmony_ci	LIST_HEAD(todo);
140862306a36Sopenharmony_ci	struct fw_cache_entry *fce;
140962306a36Sopenharmony_ci	struct fw_cache_entry *fce_next;
141062306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	devres_for_each_res(dev, fw_name_devm_release,
141362306a36Sopenharmony_ci			    devm_name_match, &fw_cache,
141462306a36Sopenharmony_ci			    dev_create_fw_entry, &todo);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	list_for_each_entry_safe(fce, fce_next, &todo, list) {
141762306a36Sopenharmony_ci		list_del(&fce->list);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci		spin_lock(&fwc->name_lock);
142062306a36Sopenharmony_ci		/* only one cache entry for one firmware */
142162306a36Sopenharmony_ci		if (!__fw_entry_found(fce->name)) {
142262306a36Sopenharmony_ci			list_add(&fce->list, &fwc->fw_names);
142362306a36Sopenharmony_ci		} else {
142462306a36Sopenharmony_ci			free_fw_cache_entry(fce);
142562306a36Sopenharmony_ci			fce = NULL;
142662306a36Sopenharmony_ci		}
142762306a36Sopenharmony_ci		spin_unlock(&fwc->name_lock);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		if (fce)
143062306a36Sopenharmony_ci			async_schedule_domain(__async_dev_cache_fw_image,
143162306a36Sopenharmony_ci					      (void *)fce,
143262306a36Sopenharmony_ci					      &fw_cache_domain);
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cistatic void __device_uncache_fw_images(void)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
143962306a36Sopenharmony_ci	struct fw_cache_entry *fce;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	spin_lock(&fwc->name_lock);
144262306a36Sopenharmony_ci	while (!list_empty(&fwc->fw_names)) {
144362306a36Sopenharmony_ci		fce = list_entry(fwc->fw_names.next,
144462306a36Sopenharmony_ci				struct fw_cache_entry, list);
144562306a36Sopenharmony_ci		list_del(&fce->list);
144662306a36Sopenharmony_ci		spin_unlock(&fwc->name_lock);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci		uncache_firmware(fce->name);
144962306a36Sopenharmony_ci		free_fw_cache_entry(fce);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci		spin_lock(&fwc->name_lock);
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci	spin_unlock(&fwc->name_lock);
145462306a36Sopenharmony_ci}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci/**
145762306a36Sopenharmony_ci * device_cache_fw_images() - cache devices' firmware
145862306a36Sopenharmony_ci *
145962306a36Sopenharmony_ci * If one device called request_firmware or its nowait version
146062306a36Sopenharmony_ci * successfully before, the firmware names are recored into the
146162306a36Sopenharmony_ci * device's devres link list, so device_cache_fw_images can call
146262306a36Sopenharmony_ci * cache_firmware() to cache these firmwares for the device,
146362306a36Sopenharmony_ci * then the device driver can load its firmwares easily at
146462306a36Sopenharmony_ci * time when system is not ready to complete loading firmware.
146562306a36Sopenharmony_ci */
146662306a36Sopenharmony_cistatic void device_cache_fw_images(void)
146762306a36Sopenharmony_ci{
146862306a36Sopenharmony_ci	struct firmware_cache *fwc = &fw_cache;
146962306a36Sopenharmony_ci	DEFINE_WAIT(wait);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	/* cancel uncache work */
147462306a36Sopenharmony_ci	cancel_delayed_work_sync(&fwc->work);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	fw_fallback_set_cache_timeout();
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	mutex_lock(&fw_lock);
147962306a36Sopenharmony_ci	fwc->state = FW_LOADER_START_CACHE;
148062306a36Sopenharmony_ci	dpm_for_each_dev(NULL, dev_cache_fw_image);
148162306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	/* wait for completion of caching firmware for all devices */
148462306a36Sopenharmony_ci	async_synchronize_full_domain(&fw_cache_domain);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	fw_fallback_set_default_timeout();
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci/**
149062306a36Sopenharmony_ci * device_uncache_fw_images() - uncache devices' firmware
149162306a36Sopenharmony_ci *
149262306a36Sopenharmony_ci * uncache all firmwares which have been cached successfully
149362306a36Sopenharmony_ci * by device_uncache_fw_images earlier
149462306a36Sopenharmony_ci */
149562306a36Sopenharmony_cistatic void device_uncache_fw_images(void)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
149862306a36Sopenharmony_ci	__device_uncache_fw_images();
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic void device_uncache_fw_images_work(struct work_struct *work)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	device_uncache_fw_images();
150462306a36Sopenharmony_ci}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci/**
150762306a36Sopenharmony_ci * device_uncache_fw_images_delay() - uncache devices firmwares
150862306a36Sopenharmony_ci * @delay: number of milliseconds to delay uncache device firmwares
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * uncache all devices's firmwares which has been cached successfully
151162306a36Sopenharmony_ci * by device_cache_fw_images after @delay milliseconds.
151262306a36Sopenharmony_ci */
151362306a36Sopenharmony_cistatic void device_uncache_fw_images_delay(unsigned long delay)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &fw_cache.work,
151662306a36Sopenharmony_ci			   msecs_to_jiffies(delay));
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cistatic int fw_pm_notify(struct notifier_block *notify_block,
152062306a36Sopenharmony_ci			unsigned long mode, void *unused)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	switch (mode) {
152362306a36Sopenharmony_ci	case PM_HIBERNATION_PREPARE:
152462306a36Sopenharmony_ci	case PM_SUSPEND_PREPARE:
152562306a36Sopenharmony_ci	case PM_RESTORE_PREPARE:
152662306a36Sopenharmony_ci		/*
152762306a36Sopenharmony_ci		 * kill pending fallback requests with a custom fallback
152862306a36Sopenharmony_ci		 * to avoid stalling suspend.
152962306a36Sopenharmony_ci		 */
153062306a36Sopenharmony_ci		kill_pending_fw_fallback_reqs(true);
153162306a36Sopenharmony_ci		device_cache_fw_images();
153262306a36Sopenharmony_ci		break;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	case PM_POST_SUSPEND:
153562306a36Sopenharmony_ci	case PM_POST_HIBERNATION:
153662306a36Sopenharmony_ci	case PM_POST_RESTORE:
153762306a36Sopenharmony_ci		/*
153862306a36Sopenharmony_ci		 * In case that system sleep failed and syscore_suspend is
153962306a36Sopenharmony_ci		 * not called.
154062306a36Sopenharmony_ci		 */
154162306a36Sopenharmony_ci		mutex_lock(&fw_lock);
154262306a36Sopenharmony_ci		fw_cache.state = FW_LOADER_NO_CACHE;
154362306a36Sopenharmony_ci		mutex_unlock(&fw_lock);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci		device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
154662306a36Sopenharmony_ci		break;
154762306a36Sopenharmony_ci	}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	return 0;
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci/* stop caching firmware once syscore_suspend is reached */
155362306a36Sopenharmony_cistatic int fw_suspend(void)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	fw_cache.state = FW_LOADER_NO_CACHE;
155662306a36Sopenharmony_ci	return 0;
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic struct syscore_ops fw_syscore_ops = {
156062306a36Sopenharmony_ci	.suspend = fw_suspend,
156162306a36Sopenharmony_ci};
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic int __init register_fw_pm_ops(void)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	int ret;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	spin_lock_init(&fw_cache.name_lock);
156862306a36Sopenharmony_ci	INIT_LIST_HEAD(&fw_cache.fw_names);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&fw_cache.work,
157162306a36Sopenharmony_ci			  device_uncache_fw_images_work);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	fw_cache.pm_notify.notifier_call = fw_pm_notify;
157462306a36Sopenharmony_ci	ret = register_pm_notifier(&fw_cache.pm_notify);
157562306a36Sopenharmony_ci	if (ret)
157662306a36Sopenharmony_ci		return ret;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	register_syscore_ops(&fw_syscore_ops);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	return ret;
158162306a36Sopenharmony_ci}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_cistatic inline void unregister_fw_pm_ops(void)
158462306a36Sopenharmony_ci{
158562306a36Sopenharmony_ci	unregister_syscore_ops(&fw_syscore_ops);
158662306a36Sopenharmony_ci	unregister_pm_notifier(&fw_cache.pm_notify);
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci#else
158962306a36Sopenharmony_cistatic void fw_cache_piggyback_on_request(struct fw_priv *fw_priv)
159062306a36Sopenharmony_ci{
159162306a36Sopenharmony_ci}
159262306a36Sopenharmony_cistatic inline int register_fw_pm_ops(void)
159362306a36Sopenharmony_ci{
159462306a36Sopenharmony_ci	return 0;
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_cistatic inline void unregister_fw_pm_ops(void)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci#endif
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cistatic void __init fw_cache_init(void)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	spin_lock_init(&fw_cache.lock);
160462306a36Sopenharmony_ci	INIT_LIST_HEAD(&fw_cache.head);
160562306a36Sopenharmony_ci	fw_cache.state = FW_LOADER_NO_CACHE;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int fw_shutdown_notify(struct notifier_block *unused1,
160962306a36Sopenharmony_ci			      unsigned long unused2, void *unused3)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	/*
161262306a36Sopenharmony_ci	 * Kill all pending fallback requests to avoid both stalling shutdown,
161362306a36Sopenharmony_ci	 * and avoid a deadlock with the usermode_lock.
161462306a36Sopenharmony_ci	 */
161562306a36Sopenharmony_ci	kill_pending_fw_fallback_reqs(false);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	return NOTIFY_DONE;
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic struct notifier_block fw_shutdown_nb = {
162162306a36Sopenharmony_ci	.notifier_call = fw_shutdown_notify,
162262306a36Sopenharmony_ci};
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_cistatic int __init firmware_class_init(void)
162562306a36Sopenharmony_ci{
162662306a36Sopenharmony_ci	int ret;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	/* No need to unfold these on exit */
162962306a36Sopenharmony_ci	fw_cache_init();
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	ret = register_fw_pm_ops();
163262306a36Sopenharmony_ci	if (ret)
163362306a36Sopenharmony_ci		return ret;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	ret = register_reboot_notifier(&fw_shutdown_nb);
163662306a36Sopenharmony_ci	if (ret)
163762306a36Sopenharmony_ci		goto out;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	return register_sysfs_loader();
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ciout:
164262306a36Sopenharmony_ci	unregister_fw_pm_ops();
164362306a36Sopenharmony_ci	return ret;
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_cistatic void __exit firmware_class_exit(void)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	unregister_fw_pm_ops();
164962306a36Sopenharmony_ci	unregister_reboot_notifier(&fw_shutdown_nb);
165062306a36Sopenharmony_ci	unregister_sysfs_loader();
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cifs_initcall(firmware_class_init);
165462306a36Sopenharmony_cimodule_exit(firmware_class_exit);
1655