Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
45 kB · 1745 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746// SPDX-License-Identifier: GPL-2.0/* * main.c - Multi purpose firmware loading support * * Copyright (c) 2003 Manuel Estrada Sainz * * Please see Documentation/driver-api/firmware/ for more information. * */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/capability.h>#include <linux/device.h>#include <linux/kernel_read_file.h>#include <linux/module.h>#include <linux/init.h>#include <linux/initrd.h>#include <linux/timer.h>#include <linux/vmalloc.h>#include <linux/interrupt.h>#include <linux/bitops.h>#include <linux/mutex.h>#include <linux/workqueue.h>#include <linux/highmem.h>#include <linux/firmware.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/file.h>#include <linux/list.h>#include <linux/fs.h>#include <linux/async.h>#include <linux/pm.h>#include <linux/suspend.h>#include <linux/syscore_ops.h>#include <linux/reboot.h>#include <linux/security.h>#include <linux/zstd.h>#include <linux/xz.h>
#include <generated/utsrelease.h>
#include "../base.h"#include "firmware.h"#include "fallback.h"
MODULE_AUTHOR("Manuel Estrada Sainz");MODULE_DESCRIPTION("Multi purpose firmware loading support");MODULE_LICENSE("GPL");
struct firmware_cache { /* firmware_buf instance will be added into the below list */ spinlock_t lock; struct list_head head; int state;
#ifdef CONFIG_FW_CACHE /* * Names of firmware images which have been cached successfully * will be added into the below list so that device uncache * helper can trace which firmware images have been cached * before. */ spinlock_t name_lock; struct list_head fw_names;
struct delayed_work work;
struct notifier_block pm_notify;#endif};
struct fw_cache_entry { struct list_head list; const char *name;};
struct fw_name_devm { unsigned long magic; const char *name;};
static inline struct fw_priv *to_fw_priv(struct kref *ref){ return container_of(ref, struct fw_priv, ref);}
#define FW_LOADER_NO_CACHE 0#define FW_LOADER_START_CACHE 1
/* fw_lock could be moved to 'struct fw_sysfs' but since it is just * guarding for corner cases a global lock should be OK */DEFINE_MUTEX(fw_lock);
struct firmware_cache fw_cache;bool fw_load_abort_all;
void fw_state_init(struct fw_priv *fw_priv){ struct fw_state *fw_st = &fw_priv->fw_st;
init_completion(&fw_st->completion); fw_st->status = FW_STATUS_UNKNOWN;}
static inline int fw_state_wait(struct fw_priv *fw_priv){ return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT);}
static void fw_cache_piggyback_on_request(struct fw_priv *fw_priv);
static struct fw_priv *__allocate_fw_priv(const char *fw_name, struct firmware_cache *fwc, void *dbuf, size_t size, size_t offset, u32 opt_flags){ struct fw_priv *fw_priv;
/* For a partial read, the buffer must be preallocated. */ if ((opt_flags & FW_OPT_PARTIAL) && !dbuf) return NULL;
/* Only partial reads are allowed to use an offset. */ if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL)) return NULL;
fw_priv = kzalloc_obj(*fw_priv, GFP_ATOMIC); if (!fw_priv) return NULL;
fw_priv->fw_name = kstrdup_const(fw_name, GFP_ATOMIC); if (!fw_priv->fw_name) { kfree(fw_priv); return NULL; }
kref_init(&fw_priv->ref); fw_priv->fwc = fwc; fw_priv->data = dbuf; fw_priv->allocated_size = size; fw_priv->offset = offset; fw_priv->opt_flags = opt_flags; fw_state_init(fw_priv);#ifdef CONFIG_FW_LOADER_USER_HELPER INIT_LIST_HEAD(&fw_priv->pending_list);#endif
pr_debug("%s: fw-%s fw_priv=%p\n", __func__, fw_name, fw_priv);
return fw_priv;}
static struct fw_priv *__lookup_fw_priv(const char *fw_name){ struct fw_priv *tmp; struct firmware_cache *fwc = &fw_cache;
list_for_each_entry(tmp, &fwc->head, list) if (!strcmp(tmp->fw_name, fw_name)) return tmp; return NULL;}
/* Returns 1 for batching firmware requests with the same name */int alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc, struct fw_priv **fw_priv, void *dbuf, size_t size, size_t offset, u32 opt_flags){ struct fw_priv *tmp;
spin_lock(&fwc->lock); /* * Do not merge requests that are marked to be non-cached or * are performing partial reads. */ if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) { tmp = __lookup_fw_priv(fw_name); if (tmp) { kref_get(&tmp->ref); spin_unlock(&fwc->lock); *fw_priv = tmp; pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n"); return 1; } }
tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags); if (tmp) { INIT_LIST_HEAD(&tmp->list); if (!(opt_flags & FW_OPT_NOCACHE)) list_add(&tmp->list, &fwc->head); } spin_unlock(&fwc->lock);
*fw_priv = tmp;
return tmp ? 0 : -ENOMEM;}
static void __free_fw_priv(struct kref *ref) __releases(&fwc->lock){ struct fw_priv *fw_priv = to_fw_priv(ref); struct firmware_cache *fwc = fw_priv->fwc;
pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n", __func__, fw_priv->fw_name, fw_priv, fw_priv->data, (unsigned int)fw_priv->size);
list_del(&fw_priv->list); spin_unlock(&fwc->lock);
if (fw_is_paged_buf(fw_priv)) fw_free_paged_buf(fw_priv); else if (!fw_priv->allocated_size) vfree(fw_priv->data);
kfree_const(fw_priv->fw_name); kfree(fw_priv);}
void free_fw_priv(struct fw_priv *fw_priv){ struct firmware_cache *fwc = fw_priv->fwc; spin_lock(&fwc->lock); if (!kref_put(&fw_priv->ref, __free_fw_priv)) spin_unlock(&fwc->lock);}
#ifdef CONFIG_FW_LOADER_PAGED_BUFbool fw_is_paged_buf(struct fw_priv *fw_priv){ return fw_priv->is_paged_buf;}
void fw_free_paged_buf(struct fw_priv *fw_priv){ int i;
if (!fw_priv->pages) return;
vunmap(fw_priv->data);
for (i = 0; i < fw_priv->nr_pages; i++) __free_page(fw_priv->pages[i]); kvfree(fw_priv->pages); fw_priv->pages = NULL; fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; fw_priv->data = NULL; fw_priv->size = 0;}
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed){ /* If the array of pages is too small, grow it */ if (fw_priv->page_array_size < pages_needed) { int new_array_size = max(pages_needed, fw_priv->page_array_size * 2); struct page **new_pages;
new_pages = kvmalloc_array(new_array_size, sizeof(void *), GFP_KERNEL); if (!new_pages) return -ENOMEM; memcpy(new_pages, fw_priv->pages, fw_priv->page_array_size * sizeof(void *)); memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) * (new_array_size - fw_priv->page_array_size)); kvfree(fw_priv->pages); fw_priv->pages = new_pages; fw_priv->page_array_size = new_array_size; }
while (fw_priv->nr_pages < pages_needed) { fw_priv->pages[fw_priv->nr_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
if (!fw_priv->pages[fw_priv->nr_pages]) return -ENOMEM; fw_priv->nr_pages++; }
return 0;}
int fw_map_paged_buf(struct fw_priv *fw_priv){ /* one pages buffer should be mapped/unmapped only once */ if (!fw_priv->pages) return 0;
vunmap(fw_priv->data); fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0, PAGE_KERNEL_RO); if (!fw_priv->data) return -ENOMEM;
return 0;}#endif
/* * ZSTD-compressed firmware support */#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTDstatic int fw_decompress_zstd(struct device *dev, struct fw_priv *fw_priv, size_t in_size, const void *in_buffer){ size_t len, out_size, workspace_size; void *workspace, *out_buf; zstd_dctx *ctx; int err;
if (fw_priv->allocated_size) { out_size = fw_priv->allocated_size; out_buf = fw_priv->data; } else { zstd_frame_header params;
if (zstd_get_frame_header(¶ms, in_buffer, in_size) || params.frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) { dev_dbg(dev, "%s: invalid zstd header\n", __func__); return -EINVAL; } out_size = params.frameContentSize; out_buf = vzalloc(out_size); if (!out_buf) return -ENOMEM; }
workspace_size = zstd_dctx_workspace_bound(); workspace = kvzalloc(workspace_size, GFP_KERNEL); if (!workspace) { err = -ENOMEM; goto error; }
ctx = zstd_init_dctx(workspace, workspace_size); if (!ctx) { dev_dbg(dev, "%s: failed to initialize context\n", __func__); err = -EINVAL; goto error; }
len = zstd_decompress_dctx(ctx, out_buf, out_size, in_buffer, in_size); if (zstd_is_error(len)) { dev_dbg(dev, "%s: failed to decompress: %d\n", __func__, zstd_get_error_code(len)); err = -EINVAL; goto error; }
if (!fw_priv->allocated_size) fw_priv->data = out_buf; fw_priv->size = len; err = 0;
error: kvfree(workspace); if (err && !fw_priv->allocated_size) vfree(out_buf); return err;}#endif /* CONFIG_FW_LOADER_COMPRESS_ZSTD */
/* * XZ-compressed firmware support */#ifdef CONFIG_FW_LOADER_COMPRESS_XZ/* show an error and return the standard error code */static int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret){ if (xz_ret != XZ_STREAM_END) { dev_warn(dev, "xz decompression failed (xz_ret=%d)\n", xz_ret); return xz_ret == XZ_MEM_ERROR ? -ENOMEM : -EINVAL; } return 0;}
/* single-shot decompression onto the pre-allocated buffer */static int fw_decompress_xz_single(struct device *dev, struct fw_priv *fw_priv, size_t in_size, const void *in_buffer){ struct xz_dec *xz_dec; struct xz_buf xz_buf; enum xz_ret xz_ret;
xz_dec = xz_dec_init(XZ_SINGLE, (u32)-1); if (!xz_dec) return -ENOMEM;
xz_buf.in_size = in_size; xz_buf.in = in_buffer; xz_buf.in_pos = 0; xz_buf.out_size = fw_priv->allocated_size; xz_buf.out = fw_priv->data; xz_buf.out_pos = 0;
xz_ret = xz_dec_run(xz_dec, &xz_buf); xz_dec_end(xz_dec);
fw_priv->size = xz_buf.out_pos; return fw_decompress_xz_error(dev, xz_ret);}
/* decompression on paged buffer and map it */static int fw_decompress_xz_pages(struct device *dev, struct fw_priv *fw_priv, size_t in_size, const void *in_buffer){ struct xz_dec *xz_dec; struct xz_buf xz_buf; enum xz_ret xz_ret; struct page *page; int err = 0;
xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1); if (!xz_dec) return -ENOMEM;
xz_buf.in_size = in_size; xz_buf.in = in_buffer; xz_buf.in_pos = 0;
fw_priv->is_paged_buf = true; fw_priv->size = 0; do { if (fw_grow_paged_buf(fw_priv, fw_priv->nr_pages + 1)) { err = -ENOMEM; goto out; }
/* decompress onto the new allocated page */ page = fw_priv->pages[fw_priv->nr_pages - 1]; xz_buf.out = kmap_local_page(page); xz_buf.out_pos = 0; xz_buf.out_size = PAGE_SIZE; xz_ret = xz_dec_run(xz_dec, &xz_buf); kunmap_local(xz_buf.out); fw_priv->size += xz_buf.out_pos; /* partial decompression means either end or error */ if (xz_buf.out_pos != PAGE_SIZE) break; } while (xz_ret == XZ_OK);
err = fw_decompress_xz_error(dev, xz_ret); if (!err) err = fw_map_paged_buf(fw_priv);
out: xz_dec_end(xz_dec); return err;}
static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv, size_t in_size, const void *in_buffer){ /* if the buffer is pre-allocated, we can perform in single-shot mode */ if (fw_priv->data) return fw_decompress_xz_single(dev, fw_priv, in_size, in_buffer); else return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);}#endif /* CONFIG_FW_LOADER_COMPRESS_XZ */
/* direct firmware loading support */static char fw_path_para[256];static const char * const fw_path[] = { fw_path_para, "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, "/lib/firmware"};
/* * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH' * from kernel command line because firmware_class is generally built in * kernel instead of module. */module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
static intfw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, const char *suffix, int (*decompress)(struct device *dev, struct fw_priv *fw_priv, size_t in_size, const void *in_buffer)){ size_t size; int i, len, maxlen = 0; int rc = -ENOENT; char *path, *nt = NULL; size_t msize = INT_MAX; void *buffer = NULL;
/* Already populated data member means we're loading into a buffer */ if (!decompress && fw_priv->data) { buffer = fw_priv->data; msize = fw_priv->allocated_size; }
path = __getname(); if (!path) return -ENOMEM;
wait_for_initramfs(); for (i = 0; i < ARRAY_SIZE(fw_path); i++) { size_t file_size = 0; size_t *file_size_ptr = NULL;
/* skip the unset customized path */ if (!fw_path[i][0]) continue;
/* strip off \n from customized path */ maxlen = strlen(fw_path[i]); if (i == 0) { nt = strchr(fw_path[i], '\n'); if (nt) maxlen = nt - fw_path[i]; }
len = snprintf(path, PATH_MAX, "%.*s/%s%s", maxlen, fw_path[i], fw_priv->fw_name, suffix); if (len >= PATH_MAX) { rc = -ENAMETOOLONG; break; }
fw_priv->size = 0;
/* * The total file size is only examined when doing a partial * read; the "full read" case needs to fail if the whole * firmware was not completely loaded. */ if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer) file_size_ptr = &file_size;
/* load firmware files from the mount namespace of init */ rc = kernel_read_file_from_path_initns(path, fw_priv->offset, &buffer, msize, file_size_ptr, READING_FIRMWARE); if (rc < 0) { if (!(fw_priv->opt_flags & FW_OPT_NO_WARN)) { if (rc != -ENOENT) dev_warn(device, "loading %s failed with error %d\n", path, rc); else dev_dbg(device, "loading %s failed for no such file or directory.\n", path); } continue; } size = rc; rc = 0;
dev_dbg(device, "Loading firmware from %s\n", path); if (decompress) { dev_dbg(device, "f/w decompressing %s\n", fw_priv->fw_name); rc = decompress(device, fw_priv, size, buffer); /* discard the superfluous original content */ vfree(buffer); buffer = NULL; if (rc) { fw_free_paged_buf(fw_priv); continue; } } else { dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name); if (!fw_priv->data) fw_priv->data = buffer; fw_priv->size = size; } fw_state_done(fw_priv); break; } __putname(path);
return rc;}
/* firmware holds the ownership of pages */static void firmware_free_data(const struct firmware *fw){ /* Loaded directly? */ if (!fw->priv) { vfree(fw->data); return; } free_fw_priv(fw->priv);}
/* store the pages buffer info firmware from buf */static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw){ fw->priv = fw_priv; fw->size = fw_priv->size; fw->data = fw_priv->data;
pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n", __func__, fw_priv->fw_name, fw_priv, fw_priv->data, (unsigned int)fw_priv->size);}
#ifdef CONFIG_FW_CACHEstatic void fw_name_devm_release(struct device *dev, void *res){ struct fw_name_devm *fwn = res;
if (fwn->magic == (unsigned long)&fw_cache) pr_debug("%s: fw_name-%s devm-%p released\n", __func__, fwn->name, res); kfree_const(fwn->name);}
static int fw_devm_match(struct device *dev, void *res, void *match_data){ struct fw_name_devm *fwn = res;
return (fwn->magic == (unsigned long)&fw_cache) && !strcmp(fwn->name, match_data);}
static struct fw_name_devm *fw_find_devm_name(struct device *dev, const char *name){ struct fw_name_devm *fwn;
fwn = devres_find(dev, fw_name_devm_release, fw_devm_match, (void *)name); return fwn;}
static bool fw_cache_is_setup(struct device *dev, const char *name){ struct fw_name_devm *fwn;
fwn = fw_find_devm_name(dev, name); if (fwn) return true;
return false;}
/* add firmware name into devres list */static int fw_add_devm_name(struct device *dev, const char *name){ struct fw_name_devm *fwn;
if (fw_cache_is_setup(dev, name)) return 0;
fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm), GFP_KERNEL); if (!fwn) return -ENOMEM; fwn->name = kstrdup_const(name, GFP_KERNEL); if (!fwn->name) { devres_free(fwn); return -ENOMEM; }
fwn->magic = (unsigned long)&fw_cache; devres_add(dev, fwn);
return 0;}#elsestatic bool fw_cache_is_setup(struct device *dev, const char *name){ return false;}
static int fw_add_devm_name(struct device *dev, const char *name){ return 0;}#endif
int assign_fw(struct firmware *fw, struct device *device){ struct fw_priv *fw_priv = fw->priv; int ret;
mutex_lock(&fw_lock); if (!fw_priv->size || fw_state_is_aborted(fw_priv)) { mutex_unlock(&fw_lock); return -ENOENT; }
/* * add firmware name into devres list so that we can auto cache * and uncache firmware for device. * * device may has been deleted already, but the problem * should be fixed in devres or driver core. */ /* don't cache firmware handled without uevent */ if (device && (fw_priv->opt_flags & FW_OPT_UEVENT) && !(fw_priv->opt_flags & FW_OPT_NOCACHE)) { ret = fw_add_devm_name(device, fw_priv->fw_name); if (ret) { mutex_unlock(&fw_lock); return ret; } }
/* * After caching firmware image is started, let it piggyback * on request firmware. */ if (!(fw_priv->opt_flags & FW_OPT_NOCACHE) && fw_priv->fwc->state == FW_LOADER_START_CACHE) fw_cache_piggyback_on_request(fw_priv);
/* pass the pages buffer to driver at the last minute */ fw_set_page_data(fw_priv, fw); mutex_unlock(&fw_lock); return 0;}
/* prepare firmware and firmware_buf structs; * return 0 if a firmware is already assigned, 1 if need to load one, * or a negative error code */static int_request_firmware_prepare(struct firmware **firmware_p, const char *name, struct device *device, void *dbuf, size_t size, size_t offset, u32 opt_flags){ struct firmware *firmware; struct fw_priv *fw_priv; int ret;
*firmware_p = firmware = kzalloc_obj(*firmware); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); return -ENOMEM; }
if (firmware_request_builtin_buf(firmware, name, dbuf, size)) { dev_dbg(device, "using built-in %s\n", name); return 0; /* assigned */ }
ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size, offset, opt_flags);
/* * bind with 'priv' now to avoid warning in failure path * of requesting firmware. */ firmware->priv = fw_priv;
if (ret > 0) { ret = fw_state_wait(fw_priv); if (!ret) { fw_set_page_data(fw_priv, firmware); return 0; /* assigned */ } }
if (ret < 0) return ret; return 1; /* need to load */}
/* * Batched requests need only one wake, we need to do this step last due to the * fallback mechanism. The buf is protected with kref_get(), and it won't be * released until the last user calls release_firmware(). * * Failed batched requests are possible as well, in such cases we just share * the struct fw_priv and won't release it until all requests are woken * and have gone through this same path. */static void fw_abort_batch_reqs(struct firmware *fw){ struct fw_priv *fw_priv;
/* Loaded directly? */ if (!fw || !fw->priv) return;
fw_priv = fw->priv; mutex_lock(&fw_lock); if (!fw_state_is_aborted(fw_priv)) fw_state_aborted(fw_priv); mutex_unlock(&fw_lock);}
#if defined(CONFIG_FW_LOADER_DEBUG)#include <crypto/sha2.h>
static void fw_log_firmware_info(const struct firmware *fw, const char *name, struct device *device){ u8 digest[SHA256_DIGEST_SIZE];
sha256(fw->data, fw->size, digest); dev_dbg(device, "Loaded FW: %s, sha256: %*phN\n", name, SHA256_DIGEST_SIZE, digest);}#elsestatic void fw_log_firmware_info(const struct firmware *fw, const char *name, struct device *device){}#endif
/* called from request_firmware() and request_firmware_work_func() */static int_request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size, size_t offset, u32 opt_flags){ struct firmware *fw = NULL; bool nondirect = false; int ret;
if (!firmware_p) return -EINVAL;
if (!name || name[0] == '\0') { ret = -EINVAL; goto out; }
/* * Reject firmware file names with ".." path components. * There are drivers that construct firmware file names from * device-supplied strings, and we don't want some device to be * able to tell us "I would like to be sent my firmware from * ../../../etc/shadow, please". * * This intentionally only looks at the firmware name, not at * the firmware base directory or at symlink contents. */ if (name_contains_dotdot(name)) { dev_warn(device, "Firmware load for '%s' refused, path contains '..' component\n", name); ret = -EINVAL; goto out; }
ret = _request_firmware_prepare(&fw, name, device, buf, size, offset, opt_flags); if (ret <= 0) /* error or already assigned */ goto out;
/* * We are about to try to access the firmware file. Because we may have been * called by a driver when serving an unrelated request from userland, we use * the kernel credentials to read the file. */ scoped_with_kernel_creds() { ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
/* Only full reads can support decompression, platform, and sysfs. */ if (!(opt_flags & FW_OPT_PARTIAL)) nondirect = true;
#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD if (ret == -ENOENT && nondirect) ret = fw_get_filesystem_firmware(device, fw->priv, ".zst", fw_decompress_zstd);#endif#ifdef CONFIG_FW_LOADER_COMPRESS_XZ if (ret == -ENOENT && nondirect) ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", fw_decompress_xz);#endif if (ret == -ENOENT && nondirect) ret = firmware_fallback_platform(fw->priv);
if (ret) { if (!(opt_flags & FW_OPT_NO_WARN)) dev_warn(device, "Direct firmware load for %s failed with error %d\n", name, ret); if (nondirect) ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret); } else { ret = assign_fw(fw, device); } }
out: if (ret < 0) { fw_abort_batch_reqs(fw); release_firmware(fw); fw = NULL; } else { fw_log_firmware_info(fw, name, device); }
*firmware_p = fw; return ret;}
/** * request_firmware() - send firmware request and wait for it * @firmware_p: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded * * @firmware_p will be used to return a firmware image by the name * of @name for device @device. * * Should be called from user context where sleeping is allowed. * * @name will be used as $FIRMWARE in the uevent environment and * should be distinctive enough not to be confused with any other * firmware image for this or any other device. * It must not contain any ".." path components - "foo/bar..bin" is * allowed, but "foo/../bar.bin" is not. * * Caller must hold the reference count of @device. * * The function can be called safely inside device's suspend and * resume callback. **/intrequest_firmware(const struct firmware **firmware_p, const char *name, struct device *device){ int ret;
/* Need to pin this module until return */ __module_get(THIS_MODULE); ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, FW_OPT_UEVENT); module_put(THIS_MODULE); return ret;}EXPORT_SYMBOL(request_firmware);
/** * firmware_request_nowarn() - request for an optional fw module * @firmware: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded * * This function is similar in behaviour to request_firmware(), except it * doesn't produce warning messages when the file is not found. The sysfs * fallback mechanism is enabled if direct filesystem lookup fails. However, * failures to find the firmware file with it are still suppressed. It is * therefore up to the driver to check for the return value of this call and to * decide when to inform the users of errors. **/int firmware_request_nowarn(const struct firmware **firmware, const char *name, struct device *device){ int ret;
/* Need to pin this module until return */ __module_get(THIS_MODULE); ret = _request_firmware(firmware, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_NO_WARN); module_put(THIS_MODULE); return ret;}EXPORT_SYMBOL_GPL(firmware_request_nowarn);
/** * request_firmware_direct() - load firmware directly without usermode helper * @firmware_p: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded * * This function works pretty much like request_firmware(), but this doesn't * fall back to usermode helper even if the firmware couldn't be loaded * directly from fs. Hence it's useful for loading optional firmwares, which * aren't always present, without extra long timeouts of udev. **/int request_firmware_direct(const struct firmware **firmware_p, const char *name, struct device *device){ int ret;
__module_get(THIS_MODULE); ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_NO_WARN | FW_OPT_NOFALLBACK_SYSFS); module_put(THIS_MODULE); return ret;}EXPORT_SYMBOL_GPL(request_firmware_direct);
/** * firmware_request_platform() - request firmware with platform-fw fallback * @firmware: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded * * This function is similar in behaviour to request_firmware, except that if * direct filesystem lookup fails, it will fallback to looking for a copy of the * requested firmware embedded in the platform's main (e.g. UEFI) firmware. **/int firmware_request_platform(const struct firmware **firmware, const char *name, struct device *device){ int ret;
/* Need to pin this module until return */ __module_get(THIS_MODULE); ret = _request_firmware(firmware, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); module_put(THIS_MODULE); return ret;}EXPORT_SYMBOL_GPL(firmware_request_platform);
/** * firmware_request_cache() - cache firmware for suspend so resume can use it * @device: device for which firmware should be cached for * @name: name of firmware file * * There are some devices with an optimization that enables the device to not * require loading firmware on system reboot. This optimization may still * require the firmware present on resume from suspend. This routine can be * used to ensure the firmware is present on resume from suspend in these * situations. This helper is not compatible with drivers which use * request_firmware_into_buf() or request_firmware_nowait() with no uevent set. **/int firmware_request_cache(struct device *device, const char *name){ int ret;
mutex_lock(&fw_lock); ret = fw_add_devm_name(device, name); mutex_unlock(&fw_lock);
return ret;}EXPORT_SYMBOL_GPL(firmware_request_cache);
/** * request_firmware_into_buf() - load firmware into a previously allocated buffer * @firmware_p: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded and DMA region allocated * @buf: address of buffer to load firmware into * @size: size of buffer * * This function works pretty much like request_firmware(), but it doesn't * allocate a buffer to hold the firmware data. Instead, the firmware * is loaded directly into the buffer pointed to by @buf and the @firmware_p * data member is pointed at @buf. * * This function doesn't cache firmware either. */intrequest_firmware_into_buf(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size){ int ret;
if (fw_cache_is_setup(device, name)) return -EOPNOTSUPP;
__module_get(THIS_MODULE); ret = _request_firmware(firmware_p, name, device, buf, size, 0, FW_OPT_UEVENT | FW_OPT_NOCACHE); module_put(THIS_MODULE); return ret;}EXPORT_SYMBOL(request_firmware_into_buf);
/** * request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer * @firmware_p: pointer to firmware image * @name: name of firmware file * @device: device for which firmware is being loaded and DMA region allocated * @buf: address of buffer to load firmware into * @size: size of buffer * @offset: offset into file to read * * This function works pretty much like request_firmware_into_buf except * it allows a partial read of the file. */intrequest_partial_firmware_into_buf(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size, size_t offset){ int ret;
if (fw_cache_is_setup(device, name)) return -EOPNOTSUPP;
__module_get(THIS_MODULE); ret = _request_firmware(firmware_p, name, device, buf, size, offset, FW_OPT_UEVENT | FW_OPT_NOCACHE | FW_OPT_PARTIAL); module_put(THIS_MODULE); return ret;}EXPORT_SYMBOL(request_partial_firmware_into_buf);
/** * release_firmware() - release the resource associated with a firmware image * @fw: firmware resource to release **/void release_firmware(const struct firmware *fw){ if (fw) { if (!firmware_is_builtin(fw)) firmware_free_data(fw); kfree(fw); }}EXPORT_SYMBOL(release_firmware);
/* Async support */struct firmware_work { struct work_struct work; struct list_head list; struct module *module; const char *name; struct device *device; void *context; void (*cont)(const struct firmware *fw, void *context); u32 opt_flags;};
static LIST_HEAD(firmware_work_list);static DEFINE_SPINLOCK(firmware_work_lock);
static void firmware_work_free(struct firmware_work *fw_work){ put_device(fw_work->device); /* taken in request_firmware_nowait() */ module_put(fw_work->module); kfree_const(fw_work->name); kfree(fw_work);}
static void request_firmware_work_func(struct work_struct *work){ struct firmware_work *fw_work; const struct firmware *fw;
fw_work = container_of(work, struct firmware_work, work);
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, fw_work->opt_flags); fw_work->cont(fw, fw_work->context);
spin_lock_irq(&firmware_work_lock); if (!list_empty(&fw_work->list)) { list_del_init(&fw_work->list); spin_unlock_irq(&firmware_work_lock); firmware_work_free(fw_work); return; } spin_unlock_irq(&firmware_work_lock);}
static int _request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context), bool nowarn){ struct firmware_work *fw_work; unsigned long flags;
fw_work = kzalloc_obj(struct firmware_work, gfp); if (!fw_work) return -ENOMEM;
fw_work->module = module; fw_work->name = kstrdup_const(name, gfp); if (!fw_work->name) { kfree(fw_work); return -ENOMEM; } fw_work->device = device; fw_work->context = context; fw_work->cont = cont; fw_work->opt_flags = FW_OPT_NOWAIT | (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER) | (nowarn ? FW_OPT_NO_WARN : 0);
if (!uevent && fw_cache_is_setup(device, name)) { kfree_const(fw_work->name); kfree(fw_work); return -EOPNOTSUPP; }
if (!try_module_get(module)) { kfree_const(fw_work->name); kfree(fw_work); return -EFAULT; }
get_device(fw_work->device); INIT_WORK(&fw_work->work, request_firmware_work_func);
spin_lock_irqsave(&firmware_work_lock, flags); list_add_tail(&fw_work->list, &firmware_work_list); schedule_work(&fw_work->work); spin_unlock_irqrestore(&firmware_work_lock, flags);
return 0;}
/** * request_firmware_nowait() - asynchronous version of request_firmware * @module: module requesting the firmware * @uevent: sends uevent to copy the firmware image if this flag * is non-zero else the firmware copy must be done manually. * @name: name of firmware file * @device: device for which firmware is being loaded * @gfp: allocation flags * @context: will be passed over to @cont, and * @fw may be %NULL if firmware request fails. * @cont: function will be called asynchronously when the firmware * request is over. * * Caller must hold the reference count of @device. * * Asynchronous variant of request_firmware() for user contexts: * - sleep for as small periods as possible since it may * increase kernel boot time of built-in device drivers * requesting firmware in their ->probe() methods, if * @gfp is GFP_KERNEL. * * - can't sleep at all if @gfp is GFP_ATOMIC. **/int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)){ return _request_firmware_nowait(module, uevent, name, device, gfp, context, cont, false);
}EXPORT_SYMBOL(request_firmware_nowait);
/** * firmware_request_nowait_nowarn() - async version of request_firmware_nowarn * @module: module requesting the firmware * @name: name of firmware file * @device: device for which firmware is being loaded * @gfp: allocation flags * @context: will be passed over to @cont, and * @fw may be %NULL if firmware request fails. * @cont: function will be called asynchronously when the firmware * request is over. * * Similar in function to request_firmware_nowait(), but doesn't print a warning * when the firmware file could not be found and always sends a uevent to copy * the firmware image. */int firmware_request_nowait_nowarn( struct module *module, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)){ return _request_firmware_nowait(module, FW_ACTION_UEVENT, name, device, gfp, context, cont, true);}EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn);
/** * request_firmware_nowait_cancel() - cancel an async firmware request * @device: device for which the firmware is being loaded * @context: context passed to request_firmware_nowait() * @cont: callback passed to request_firmware_nowait() * * Cancel a pending request_firmware_nowait() request for @device, @context * and @cont. If the associated work has already started, this function waits * until the callback has returned. If the callback has already completed, this * function does nothing. * * This function may sleep. */void request_firmware_nowait_cancel(struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context)){ struct firmware_work *fw_work = NULL; struct firmware_work *tmp;
spin_lock_irq(&firmware_work_lock); list_for_each_entry_reverse(tmp, &firmware_work_list, list) { if (tmp->device == device && tmp->context == context && tmp->cont == cont) { fw_work = tmp; list_del_init(&fw_work->list); break; } } spin_unlock_irq(&firmware_work_lock);
if (!fw_work) return; cancel_work_sync(&fw_work->work); firmware_work_free(fw_work);}EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel);
#ifdef CONFIG_FW_CACHEstatic ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
/** * cache_firmware() - cache one firmware image in kernel memory space * @fw_name: the firmware image name * * Cache firmware in kernel memory so that drivers can use it when * system isn't ready for them to request firmware image from userspace. * Once it returns successfully, driver can use request_firmware or its * nowait version to get the cached firmware without any interacting * with userspace * * Return 0 if the firmware image has been cached successfully * Return !0 otherwise * */static int cache_firmware(const char *fw_name){ int ret; const struct firmware *fw;
pr_debug("%s: %s\n", __func__, fw_name);
ret = request_firmware(&fw, fw_name, NULL); if (!ret) kfree(fw);
pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
return ret;}
static struct fw_priv *lookup_fw_priv(const char *fw_name){ struct fw_priv *tmp; struct firmware_cache *fwc = &fw_cache;
spin_lock(&fwc->lock); tmp = __lookup_fw_priv(fw_name); spin_unlock(&fwc->lock);
return tmp;}
/** * uncache_firmware() - remove one cached firmware image * @fw_name: the firmware image name * * Uncache one firmware image which has been cached successfully * before. * * Return 0 if the firmware cache has been removed successfully * Return !0 otherwise * */static int uncache_firmware(const char *fw_name){ struct fw_priv *fw_priv; struct firmware fw;
pr_debug("%s: %s\n", __func__, fw_name);
if (firmware_request_builtin(&fw, fw_name)) return 0;
fw_priv = lookup_fw_priv(fw_name); if (fw_priv) { free_fw_priv(fw_priv); return 0; }
return -EINVAL;}
static struct fw_cache_entry *alloc_fw_cache_entry(const char *name){ struct fw_cache_entry *fce;
fce = kzalloc_obj(*fce, GFP_ATOMIC); if (!fce) goto exit;
fce->name = kstrdup_const(name, GFP_ATOMIC); if (!fce->name) { kfree(fce); fce = NULL; goto exit; }exit: return fce;}
static int __fw_entry_found(const char *name){ struct firmware_cache *fwc = &fw_cache; struct fw_cache_entry *fce;
list_for_each_entry(fce, &fwc->fw_names, list) { if (!strcmp(fce->name, name)) return 1; } return 0;}
static void fw_cache_piggyback_on_request(struct fw_priv *fw_priv){ const char *name = fw_priv->fw_name; struct firmware_cache *fwc = fw_priv->fwc; struct fw_cache_entry *fce;
spin_lock(&fwc->name_lock); if (__fw_entry_found(name)) goto found;
fce = alloc_fw_cache_entry(name); if (fce) { list_add(&fce->list, &fwc->fw_names); kref_get(&fw_priv->ref); pr_debug("%s: fw: %s\n", __func__, name); }found: spin_unlock(&fwc->name_lock);}
static void free_fw_cache_entry(struct fw_cache_entry *fce){ kfree_const(fce->name); kfree(fce);}
static void __async_dev_cache_fw_image(void *fw_entry, async_cookie_t cookie){ struct fw_cache_entry *fce = fw_entry; struct firmware_cache *fwc = &fw_cache; int ret;
ret = cache_firmware(fce->name); if (ret) { spin_lock(&fwc->name_lock); list_del(&fce->list); spin_unlock(&fwc->name_lock);
free_fw_cache_entry(fce); }}
/* called with dev->devres_lock held */static void dev_create_fw_entry(struct device *dev, void *res, void *data){ struct fw_name_devm *fwn = res; const char *fw_name = fwn->name; struct list_head *head = data; struct fw_cache_entry *fce;
fce = alloc_fw_cache_entry(fw_name); if (fce) list_add(&fce->list, head);}
static int devm_name_match(struct device *dev, void *res, void *match_data){ struct fw_name_devm *fwn = res; return (fwn->magic == (unsigned long)match_data);}
static void dev_cache_fw_image(struct device *dev, void *data){ LIST_HEAD(todo); struct fw_cache_entry *fce; struct fw_cache_entry *fce_next; struct firmware_cache *fwc = &fw_cache;
devres_for_each_res(dev, fw_name_devm_release, devm_name_match, &fw_cache, dev_create_fw_entry, &todo);
list_for_each_entry_safe(fce, fce_next, &todo, list) { list_del(&fce->list);
spin_lock(&fwc->name_lock); /* only one cache entry for one firmware */ if (!__fw_entry_found(fce->name)) { list_add(&fce->list, &fwc->fw_names); } else { free_fw_cache_entry(fce); fce = NULL; } spin_unlock(&fwc->name_lock);
if (fce) async_schedule_domain(__async_dev_cache_fw_image, (void *)fce, &fw_cache_domain); }}
static void __device_uncache_fw_images(void){ struct firmware_cache *fwc = &fw_cache; struct fw_cache_entry *fce;
spin_lock(&fwc->name_lock); while (!list_empty(&fwc->fw_names)) { fce = list_entry(fwc->fw_names.next, struct fw_cache_entry, list); list_del(&fce->list); spin_unlock(&fwc->name_lock);
uncache_firmware(fce->name); free_fw_cache_entry(fce);
spin_lock(&fwc->name_lock); } spin_unlock(&fwc->name_lock);}
/** * device_cache_fw_images() - cache devices' firmware * * If one device called request_firmware or its nowait version * successfully before, the firmware names are recored into the * device's devres link list, so device_cache_fw_images can call * cache_firmware() to cache these firmwares for the device, * then the device driver can load its firmwares easily at * time when system is not ready to complete loading firmware. */static void device_cache_fw_images(void){ struct firmware_cache *fwc = &fw_cache; DEFINE_WAIT(wait);
pr_debug("%s\n", __func__);
/* cancel uncache work */ cancel_delayed_work_sync(&fwc->work);
fw_fallback_set_cache_timeout();
mutex_lock(&fw_lock); fwc->state = FW_LOADER_START_CACHE; mutex_unlock(&fw_lock);
dpm_for_each_dev(NULL, dev_cache_fw_image);
/* wait for completion of caching firmware for all devices */ async_synchronize_full_domain(&fw_cache_domain);
fw_fallback_set_default_timeout();}
/** * device_uncache_fw_images() - uncache devices' firmware * * uncache all firmwares which have been cached successfully * by device_uncache_fw_images earlier */static void device_uncache_fw_images(void){ pr_debug("%s\n", __func__); __device_uncache_fw_images();}
static void device_uncache_fw_images_work(struct work_struct *work){ device_uncache_fw_images();}
/** * device_uncache_fw_images_delay() - uncache devices firmwares * @delay: number of milliseconds to delay uncache device firmwares * * uncache all devices's firmwares which has been cached successfully * by device_cache_fw_images after @delay milliseconds. */static void device_uncache_fw_images_delay(unsigned long delay){ queue_delayed_work(system_power_efficient_wq, &fw_cache.work, msecs_to_jiffies(delay));}
static int fw_pm_notify(struct notifier_block *notify_block, unsigned long mode, void *unused){ switch (mode) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: case PM_RESTORE_PREPARE: /* * Here, kill pending fallback requests will only kill * non-uevent firmware request to avoid stalling suspend. */ kill_pending_fw_fallback_reqs(false); device_cache_fw_images(); break;
case PM_POST_SUSPEND: case PM_POST_HIBERNATION: case PM_POST_RESTORE: /* * In case that system sleep failed and syscore_suspend is * not called. */ mutex_lock(&fw_lock); fw_cache.state = FW_LOADER_NO_CACHE; mutex_unlock(&fw_lock);
device_uncache_fw_images_delay(10 * MSEC_PER_SEC); break; }
return 0;}
/* stop caching firmware once syscore_suspend is reached */static int fw_suspend(void *data){ fw_cache.state = FW_LOADER_NO_CACHE; return 0;}
static const struct syscore_ops fw_syscore_ops = { .suspend = fw_suspend,};
static struct syscore fw_syscore = { .ops = &fw_syscore_ops,};
static int __init register_fw_pm_ops(void){ int ret;
spin_lock_init(&fw_cache.name_lock); INIT_LIST_HEAD(&fw_cache.fw_names);
INIT_DELAYED_WORK(&fw_cache.work, device_uncache_fw_images_work);
fw_cache.pm_notify.notifier_call = fw_pm_notify; ret = register_pm_notifier(&fw_cache.pm_notify); if (ret) return ret;
register_syscore(&fw_syscore);
return ret;}
static inline void unregister_fw_pm_ops(void){ unregister_syscore(&fw_syscore); unregister_pm_notifier(&fw_cache.pm_notify);}#elsestatic void fw_cache_piggyback_on_request(struct fw_priv *fw_priv){}static inline int register_fw_pm_ops(void){ return 0;}static inline void unregister_fw_pm_ops(void){}#endif
static void __init fw_cache_init(void){ spin_lock_init(&fw_cache.lock); INIT_LIST_HEAD(&fw_cache.head); fw_cache.state = FW_LOADER_NO_CACHE;}
static int fw_shutdown_notify(struct notifier_block *unused1, unsigned long unused2, void *unused3){ /* * Kill all pending fallback requests to avoid both stalling shutdown, * and avoid a deadlock with the usermode_lock. */ kill_pending_fw_fallback_reqs(true);
return NOTIFY_DONE;}
static struct notifier_block fw_shutdown_nb = { .notifier_call = fw_shutdown_notify,};
static int __init firmware_class_init(void){ int ret;
/* No need to unfold these on exit */ fw_cache_init();
ret = register_fw_pm_ops(); if (ret) return ret;
ret = register_reboot_notifier(&fw_shutdown_nb); if (ret) goto out;
return register_sysfs_loader();
out: unregister_fw_pm_ops(); return ret;}
static void __exit firmware_class_exit(void){ unregister_fw_pm_ops(); unregister_reboot_notifier(&fw_shutdown_nb); unregister_sysfs_loader();}
fs_initcall(firmware_class_init);module_exit(firmware_class_exit);