Message ID | 20200724213640.389191-19-keescook@chromium.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Introduce partial kernel_read_file() support | expand |
Long ago Takashi had some points about this strategy breaking compressed file use. Was that considered? Luis On Fri, Jul 24, 2020 at 02:36:39PM -0700, Kees Cook wrote: > From: Scott Branden <scott.branden@broadcom.com> > > Add request_partial_firmware_into_buf() to allow for portions of a > firmware file to be read into a buffer. This is needed when large firmware > must be loaded in portions from a file on memory constrained systems. > > Signed-off-by: Scott Branden <scott.branden@broadcom.com> > Co-developed-by: Kees Cook <keescook@chromium.org> > Signed-off-by: Kees Cook <keescook@chromium.org> > --- > drivers/base/firmware_loader/firmware.h | 4 + > drivers/base/firmware_loader/main.c | 109 +++++++++++++++++++----- > include/linux/firmware.h | 12 +++ > 3 files changed, 105 insertions(+), 20 deletions(-) > > diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h > index 7ad5fe52bc72..3f6eda46b3a2 100644 > --- a/drivers/base/firmware_loader/firmware.h > +++ b/drivers/base/firmware_loader/firmware.h > @@ -32,6 +32,8 @@ > * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in > * the platform's main firmware. If both this fallback and the sysfs > * fallback are enabled, then this fallback will be tried first. > + * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read > + * entire file. > */ > enum fw_opt { > FW_OPT_UEVENT = BIT(0), > @@ -41,6 +43,7 @@ enum fw_opt { > FW_OPT_NOCACHE = BIT(4), > FW_OPT_NOFALLBACK_SYSFS = BIT(5), > FW_OPT_FALLBACK_PLATFORM = BIT(6), > + FW_OPT_PARTIAL = BIT(7), > }; > > enum fw_status { > @@ -68,6 +71,7 @@ struct fw_priv { > void *data; > size_t size; > size_t allocated_size; > + size_t offset; > u32 opt_flags; > #ifdef CONFIG_FW_LOADER_PAGED_BUF > bool is_paged_buf; > diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c > index 814a18cc51bd..7aa22bdc2f60 100644 > --- a/drivers/base/firmware_loader/main.c > +++ b/drivers/base/firmware_loader/main.c > @@ -170,10 +170,19 @@ 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(sizeof(*fw_priv), GFP_ATOMIC); > if (!fw_priv) > return NULL; > @@ -188,6 +197,7 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name, > 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 > @@ -216,12 +226,17 @@ static int alloc_lookup_fw_priv(const char *fw_name, > struct fw_priv **fw_priv, > void *dbuf, > size_t size, > + size_t offset, > u32 opt_flags) > { > struct fw_priv *tmp; > > spin_lock(&fwc->lock); > - if (!(opt_flags & FW_OPT_NOCACHE)) { > + /* > + * 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); > @@ -232,7 +247,7 @@ static int alloc_lookup_fw_priv(const char *fw_name, > } > } > > - tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, opt_flags); > + 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)) > @@ -439,6 +454,12 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv, > else > return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer); > } > +#else > +static inline int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv, > + size_t in_size, const void *in_buffer) > +{ > + return -ENOENT; > +} > #endif /* CONFIG_FW_LOADER_COMPRESS */ > > /* direct firmware loading support */ > @@ -485,6 +506,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, > return -ENOMEM; > > 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; > @@ -498,9 +522,18 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, > > 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, 0, &buffer, msize, > - NULL, > + rc = kernel_read_file_from_path_initns(path, fw_priv->offset, > + &buffer, msize, > + file_size_ptr, > READING_FIRMWARE); > if (rc < 0) { > if (rc != -ENOENT) > @@ -691,7 +724,7 @@ int assign_fw(struct firmware *fw, struct device *device) > static int > _request_firmware_prepare(struct firmware **firmware_p, const char *name, > struct device *device, void *dbuf, size_t size, > - u32 opt_flags) > + size_t offset, u32 opt_flags) > { > struct firmware *firmware; > struct fw_priv *fw_priv; > @@ -710,7 +743,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, > } > > ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size, > - opt_flags); > + offset, opt_flags); > > /* > * bind with 'priv' now to avoid warning in failure path > @@ -757,9 +790,10 @@ static void fw_abort_batch_reqs(struct firmware *fw) > static int > _request_firmware(const struct firmware **firmware_p, const char *name, > struct device *device, void *buf, size_t size, > - u32 opt_flags) > + size_t offset, u32 opt_flags) > { > struct firmware *fw = NULL; > + bool nondirect = false; > int ret; > > if (!firmware_p) > @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name, > } > > ret = _request_firmware_prepare(&fw, name, device, buf, size, > - opt_flags); > + offset, opt_flags); > if (ret <= 0) /* error or already assigned */ > goto out; > > ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); > -#ifdef CONFIG_FW_LOADER_COMPRESS > - if (ret == -ENOENT) > + > + /* Only full reads can support decompression, platform, and sysfs. */ > + if (!(opt_flags & FW_OPT_PARTIAL)) > + nondirect = true; > + > + if (ret == -ENOENT && nondirect) > ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", > fw_decompress_xz); > -#endif > - > - if (ret == -ENOENT) > + if (ret == -ENOENT && nondirect) > ret = firmware_fallback_platform(fw->priv); > > if (ret) { > @@ -790,7 +826,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name, > dev_warn(device, > "Direct firmware load for %s failed with error %d\n", > name, ret); > - ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret); > + if (nondirect) > + ret = firmware_fallback_sysfs(fw, name, device, > + opt_flags, ret); > } else > ret = assign_fw(fw, device); > > @@ -833,7 +871,7 @@ request_firmware(const struct firmware **firmware_p, const char *name, > > /* Need to pin this module until return */ > __module_get(THIS_MODULE); > - ret = _request_firmware(firmware_p, name, device, NULL, 0, > + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, > FW_OPT_UEVENT); > module_put(THIS_MODULE); > return ret; > @@ -860,7 +898,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name, > > /* Need to pin this module until return */ > __module_get(THIS_MODULE); > - ret = _request_firmware(firmware, name, device, NULL, 0, > + ret = _request_firmware(firmware, name, device, NULL, 0, 0, > FW_OPT_UEVENT | FW_OPT_NO_WARN); > module_put(THIS_MODULE); > return ret; > @@ -884,7 +922,7 @@ int request_firmware_direct(const struct firmware **firmware_p, > int ret; > > __module_get(THIS_MODULE); > - ret = _request_firmware(firmware_p, name, device, NULL, 0, > + 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); > @@ -909,7 +947,7 @@ int firmware_request_platform(const struct firmware **firmware, > > /* Need to pin this module until return */ > __module_get(THIS_MODULE); > - ret = _request_firmware(firmware, name, device, NULL, 0, > + ret = _request_firmware(firmware, name, device, NULL, 0, 0, > FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); > module_put(THIS_MODULE); > return ret; > @@ -965,13 +1003,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name, > return -EOPNOTSUPP; > > __module_get(THIS_MODULE); > - ret = _request_firmware(firmware_p, name, device, buf, size, > + 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. > + */ > +int > +request_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 > @@ -1004,7 +1073,7 @@ static void request_firmware_work_func(struct work_struct *work) > > fw_work = container_of(work, struct firmware_work, work); > > - _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, > + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, > fw_work->opt_flags); > fw_work->cont(fw, fw_work->context); > put_device(fw_work->device); /* taken in request_firmware_nowait() */ > diff --git a/include/linux/firmware.h b/include/linux/firmware.h > index cb3e2c06ed8a..c15acadc6cf4 100644 > --- a/include/linux/firmware.h > +++ b/include/linux/firmware.h > @@ -53,6 +53,9 @@ int request_firmware_direct(const struct firmware **fw, const char *name, > struct device *device); > int request_firmware_into_buf(const struct firmware **firmware_p, > const char *name, struct device *device, void *buf, size_t size); > +int request_partial_firmware_into_buf(const struct firmware **firmware_p, > + const char *name, struct device *device, > + void *buf, size_t size, size_t offset); > > void release_firmware(const struct firmware *fw); > #else > @@ -102,6 +105,15 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p, > return -EINVAL; > } > > +static inline int request_partial_firmware_into_buf > + (const struct firmware **firmware_p, > + const char *name, > + struct device *device, > + void *buf, size_t size, size_t offset) > +{ > + return -EINVAL; > +} > + > #endif > > int firmware_request_cache(struct device *device, const char *name); > -- > 2.25.1 >
On Wed, 29 Jul 2020 03:17:39 +0200, Luis Chamberlain wrote: > > Long ago Takashi had some points about this strategy breaking > compressed file use. Was that considered? As long as I read the patch, it tries to skip both the compressed and the fallback loading when FW_OPT_PARTIAL is set, which is good. However... > > @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name, > > } > > > > ret = _request_firmware_prepare(&fw, name, device, buf, size, > > - opt_flags); > > + offset, opt_flags); > > if (ret <= 0) /* error or already assigned */ > > goto out; > > > > ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); > > -#ifdef CONFIG_FW_LOADER_COMPRESS > > - if (ret == -ENOENT) > > + > > + /* Only full reads can support decompression, platform, and sysfs. */ > > + if (!(opt_flags & FW_OPT_PARTIAL)) > > + nondirect = true; > > + > > + if (ret == -ENOENT && nondirect) > > ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", > > fw_decompress_xz); > > -#endif ... by dropping this ifdef, the fw loader would try to access *.xz file unnecessarily even if CONFIG_FW_LOADER_COMPRESS is disabled. thanks, Takashi
On Wed, Jul 29, 2020 at 08:22:17AM +0200, Takashi Iwai wrote: > On Wed, 29 Jul 2020 03:17:39 +0200, > Luis Chamberlain wrote: > > > > Long ago Takashi had some points about this strategy breaking > > compressed file use. Was that considered? > > As long as I read the patch, it tries to skip both the compressed and > the fallback loading when FW_OPT_PARTIAL is set, which is good. > > However... > > > > @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name, > > > } > > > > > > ret = _request_firmware_prepare(&fw, name, device, buf, size, > > > - opt_flags); > > > + offset, opt_flags); > > > if (ret <= 0) /* error or already assigned */ > > > goto out; > > > > > > ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); > > > -#ifdef CONFIG_FW_LOADER_COMPRESS > > > - if (ret == -ENOENT) > > > + > > > + /* Only full reads can support decompression, platform, and sysfs. */ > > > + if (!(opt_flags & FW_OPT_PARTIAL)) > > > + nondirect = true; > > > + > > > + if (ret == -ENOENT && nondirect) > > > ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", > > > fw_decompress_xz); > > > -#endif > > ... by dropping this ifdef, the fw loader would try to access *.xz > file unnecessarily even if CONFIG_FW_LOADER_COMPRESS is disabled. Ah, good point. I'd added the -ENOENT fw_decompress_xz, but I take your point about the needless access. I will switch this back to an #ifdef.
diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h index 7ad5fe52bc72..3f6eda46b3a2 100644 --- a/drivers/base/firmware_loader/firmware.h +++ b/drivers/base/firmware_loader/firmware.h @@ -32,6 +32,8 @@ * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in * the platform's main firmware. If both this fallback and the sysfs * fallback are enabled, then this fallback will be tried first. + * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read + * entire file. */ enum fw_opt { FW_OPT_UEVENT = BIT(0), @@ -41,6 +43,7 @@ enum fw_opt { FW_OPT_NOCACHE = BIT(4), FW_OPT_NOFALLBACK_SYSFS = BIT(5), FW_OPT_FALLBACK_PLATFORM = BIT(6), + FW_OPT_PARTIAL = BIT(7), }; enum fw_status { @@ -68,6 +71,7 @@ struct fw_priv { void *data; size_t size; size_t allocated_size; + size_t offset; u32 opt_flags; #ifdef CONFIG_FW_LOADER_PAGED_BUF bool is_paged_buf; diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 814a18cc51bd..7aa22bdc2f60 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -170,10 +170,19 @@ 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(sizeof(*fw_priv), GFP_ATOMIC); if (!fw_priv) return NULL; @@ -188,6 +197,7 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name, 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 @@ -216,12 +226,17 @@ static int alloc_lookup_fw_priv(const char *fw_name, struct fw_priv **fw_priv, void *dbuf, size_t size, + size_t offset, u32 opt_flags) { struct fw_priv *tmp; spin_lock(&fwc->lock); - if (!(opt_flags & FW_OPT_NOCACHE)) { + /* + * 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); @@ -232,7 +247,7 @@ static int alloc_lookup_fw_priv(const char *fw_name, } } - tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, opt_flags); + 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)) @@ -439,6 +454,12 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv, else return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer); } +#else +static inline int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv, + size_t in_size, const void *in_buffer) +{ + return -ENOENT; +} #endif /* CONFIG_FW_LOADER_COMPRESS */ /* direct firmware loading support */ @@ -485,6 +506,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, return -ENOMEM; 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; @@ -498,9 +522,18 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, 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, 0, &buffer, msize, - NULL, + rc = kernel_read_file_from_path_initns(path, fw_priv->offset, + &buffer, msize, + file_size_ptr, READING_FIRMWARE); if (rc < 0) { if (rc != -ENOENT) @@ -691,7 +724,7 @@ int assign_fw(struct firmware *fw, struct device *device) static int _request_firmware_prepare(struct firmware **firmware_p, const char *name, struct device *device, void *dbuf, size_t size, - u32 opt_flags) + size_t offset, u32 opt_flags) { struct firmware *firmware; struct fw_priv *fw_priv; @@ -710,7 +743,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, } ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size, - opt_flags); + offset, opt_flags); /* * bind with 'priv' now to avoid warning in failure path @@ -757,9 +790,10 @@ static void fw_abort_batch_reqs(struct firmware *fw) static int _request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size, - u32 opt_flags) + size_t offset, u32 opt_flags) { struct firmware *fw = NULL; + bool nondirect = false; int ret; if (!firmware_p) @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name, } ret = _request_firmware_prepare(&fw, name, device, buf, size, - opt_flags); + offset, opt_flags); if (ret <= 0) /* error or already assigned */ goto out; ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); -#ifdef CONFIG_FW_LOADER_COMPRESS - if (ret == -ENOENT) + + /* Only full reads can support decompression, platform, and sysfs. */ + if (!(opt_flags & FW_OPT_PARTIAL)) + nondirect = true; + + if (ret == -ENOENT && nondirect) ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", fw_decompress_xz); -#endif - - if (ret == -ENOENT) + if (ret == -ENOENT && nondirect) ret = firmware_fallback_platform(fw->priv); if (ret) { @@ -790,7 +826,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name, dev_warn(device, "Direct firmware load for %s failed with error %d\n", name, ret); - ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret); + if (nondirect) + ret = firmware_fallback_sysfs(fw, name, device, + opt_flags, ret); } else ret = assign_fw(fw, device); @@ -833,7 +871,7 @@ request_firmware(const struct firmware **firmware_p, const char *name, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, NULL, 0, + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, FW_OPT_UEVENT); module_put(THIS_MODULE); return ret; @@ -860,7 +898,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware, name, device, NULL, 0, + ret = _request_firmware(firmware, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_NO_WARN); module_put(THIS_MODULE); return ret; @@ -884,7 +922,7 @@ int request_firmware_direct(const struct firmware **firmware_p, int ret; __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, NULL, 0, + 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); @@ -909,7 +947,7 @@ int firmware_request_platform(const struct firmware **firmware, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware, name, device, NULL, 0, + ret = _request_firmware(firmware, name, device, NULL, 0, 0, FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); module_put(THIS_MODULE); return ret; @@ -965,13 +1003,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name, return -EOPNOTSUPP; __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, buf, size, + 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. + */ +int +request_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 @@ -1004,7 +1073,7 @@ static void request_firmware_work_func(struct work_struct *work) fw_work = container_of(work, struct firmware_work, work); - _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, fw_work->opt_flags); fw_work->cont(fw, fw_work->context); put_device(fw_work->device); /* taken in request_firmware_nowait() */ diff --git a/include/linux/firmware.h b/include/linux/firmware.h index cb3e2c06ed8a..c15acadc6cf4 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -53,6 +53,9 @@ int request_firmware_direct(const struct firmware **fw, const char *name, struct device *device); int request_firmware_into_buf(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size); +int request_partial_firmware_into_buf(const struct firmware **firmware_p, + const char *name, struct device *device, + void *buf, size_t size, size_t offset); void release_firmware(const struct firmware *fw); #else @@ -102,6 +105,15 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p, return -EINVAL; } +static inline int request_partial_firmware_into_buf + (const struct firmware **firmware_p, + const char *name, + struct device *device, + void *buf, size_t size, size_t offset) +{ + return -EINVAL; +} + #endif int firmware_request_cache(struct device *device, const char *name);