Skip to content

Commit 59cdb23

Browse files
sbrandengregkh
authored andcommitted
firmware: Add request_partial_firmware_into_buf()
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 <[email protected]> Co-developed-by: Kees Cook <[email protected]> Signed-off-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 89287c1 commit 59cdb23

File tree

3 files changed

+99
-18
lines changed

3 files changed

+99
-18
lines changed

drivers/base/firmware_loader/firmware.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
* @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in
3333
* the platform's main firmware. If both this fallback and the sysfs
3434
* fallback are enabled, then this fallback will be tried first.
35+
* @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read
36+
* entire file.
3537
*/
3638
enum fw_opt {
3739
FW_OPT_UEVENT = BIT(0),
@@ -41,6 +43,7 @@ enum fw_opt {
4143
FW_OPT_NOCACHE = BIT(4),
4244
FW_OPT_NOFALLBACK_SYSFS = BIT(5),
4345
FW_OPT_FALLBACK_PLATFORM = BIT(6),
46+
FW_OPT_PARTIAL = BIT(7),
4447
};
4548

4649
enum fw_status {
@@ -68,6 +71,7 @@ struct fw_priv {
6871
void *data;
6972
size_t size;
7073
size_t allocated_size;
74+
size_t offset;
7175
u32 opt_flags;
7276
#ifdef CONFIG_FW_LOADER_PAGED_BUF
7377
bool is_paged_buf;

drivers/base/firmware_loader/main.c

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,19 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
170170
struct firmware_cache *fwc,
171171
void *dbuf,
172172
size_t size,
173+
size_t offset,
173174
u32 opt_flags)
174175
{
175176
struct fw_priv *fw_priv;
176177

178+
/* For a partial read, the buffer must be preallocated. */
179+
if ((opt_flags & FW_OPT_PARTIAL) && !dbuf)
180+
return NULL;
181+
182+
/* Only partial reads are allowed to use an offset. */
183+
if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL))
184+
return NULL;
185+
177186
fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
178187
if (!fw_priv)
179188
return NULL;
@@ -188,6 +197,7 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
188197
fw_priv->fwc = fwc;
189198
fw_priv->data = dbuf;
190199
fw_priv->allocated_size = size;
200+
fw_priv->offset = offset;
191201
fw_priv->opt_flags = opt_flags;
192202
fw_state_init(fw_priv);
193203
#ifdef CONFIG_FW_LOADER_USER_HELPER
@@ -216,12 +226,17 @@ static int alloc_lookup_fw_priv(const char *fw_name,
216226
struct fw_priv **fw_priv,
217227
void *dbuf,
218228
size_t size,
229+
size_t offset,
219230
u32 opt_flags)
220231
{
221232
struct fw_priv *tmp;
222233

223234
spin_lock(&fwc->lock);
224-
if (!(opt_flags & FW_OPT_NOCACHE)) {
235+
/*
236+
* Do not merge requests that are marked to be non-cached or
237+
* are performing partial reads.
238+
*/
239+
if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) {
225240
tmp = __lookup_fw_priv(fw_name);
226241
if (tmp) {
227242
kref_get(&tmp->ref);
@@ -232,7 +247,7 @@ static int alloc_lookup_fw_priv(const char *fw_name,
232247
}
233248
}
234249

235-
tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, opt_flags);
250+
tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags);
236251
if (tmp) {
237252
INIT_LIST_HEAD(&tmp->list);
238253
if (!(opt_flags & FW_OPT_NOCACHE))
@@ -490,6 +505,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
490505
return -ENOMEM;
491506

492507
for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
508+
size_t file_size = 0;
509+
size_t *file_size_ptr = NULL;
510+
493511
/* skip the unset customized path */
494512
if (!fw_path[i][0])
495513
continue;
@@ -503,9 +521,18 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
503521

504522
fw_priv->size = 0;
505523

524+
/*
525+
* The total file size is only examined when doing a partial
526+
* read; the "full read" case needs to fail if the whole
527+
* firmware was not completely loaded.
528+
*/
529+
if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer)
530+
file_size_ptr = &file_size;
531+
506532
/* load firmware files from the mount namespace of init */
507-
rc = kernel_read_file_from_path_initns(path, 0, &buffer, msize,
508-
NULL,
533+
rc = kernel_read_file_from_path_initns(path, fw_priv->offset,
534+
&buffer, msize,
535+
file_size_ptr,
509536
READING_FIRMWARE);
510537
if (rc < 0) {
511538
if (rc != -ENOENT)
@@ -696,7 +723,7 @@ int assign_fw(struct firmware *fw, struct device *device)
696723
static int
697724
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
698725
struct device *device, void *dbuf, size_t size,
699-
u32 opt_flags)
726+
size_t offset, u32 opt_flags)
700727
{
701728
struct firmware *firmware;
702729
struct fw_priv *fw_priv;
@@ -715,7 +742,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
715742
}
716743

717744
ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size,
718-
opt_flags);
745+
offset, opt_flags);
719746

720747
/*
721748
* bind with 'priv' now to avoid warning in failure path
@@ -762,9 +789,10 @@ static void fw_abort_batch_reqs(struct firmware *fw)
762789
static int
763790
_request_firmware(const struct firmware **firmware_p, const char *name,
764791
struct device *device, void *buf, size_t size,
765-
u32 opt_flags)
792+
size_t offset, u32 opt_flags)
766793
{
767794
struct firmware *fw = NULL;
795+
bool nondirect = false;
768796
int ret;
769797

770798
if (!firmware_p)
@@ -776,26 +804,32 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
776804
}
777805

778806
ret = _request_firmware_prepare(&fw, name, device, buf, size,
779-
opt_flags);
807+
offset, opt_flags);
780808
if (ret <= 0) /* error or already assigned */
781809
goto out;
782810

783811
ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
812+
813+
/* Only full reads can support decompression, platform, and sysfs. */
814+
if (!(opt_flags & FW_OPT_PARTIAL))
815+
nondirect = true;
816+
784817
#ifdef CONFIG_FW_LOADER_COMPRESS
785-
if (ret == -ENOENT)
818+
if (ret == -ENOENT && nondirect)
786819
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
787820
fw_decompress_xz);
788821
#endif
789-
790-
if (ret == -ENOENT)
822+
if (ret == -ENOENT && nondirect)
791823
ret = firmware_fallback_platform(fw->priv);
792824

793825
if (ret) {
794826
if (!(opt_flags & FW_OPT_NO_WARN))
795827
dev_warn(device,
796828
"Direct firmware load for %s failed with error %d\n",
797829
name, ret);
798-
ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret);
830+
if (nondirect)
831+
ret = firmware_fallback_sysfs(fw, name, device,
832+
opt_flags, ret);
799833
} else
800834
ret = assign_fw(fw, device);
801835

@@ -838,7 +872,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
838872

839873
/* Need to pin this module until return */
840874
__module_get(THIS_MODULE);
841-
ret = _request_firmware(firmware_p, name, device, NULL, 0,
875+
ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
842876
FW_OPT_UEVENT);
843877
module_put(THIS_MODULE);
844878
return ret;
@@ -865,7 +899,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name,
865899

866900
/* Need to pin this module until return */
867901
__module_get(THIS_MODULE);
868-
ret = _request_firmware(firmware, name, device, NULL, 0,
902+
ret = _request_firmware(firmware, name, device, NULL, 0, 0,
869903
FW_OPT_UEVENT | FW_OPT_NO_WARN);
870904
module_put(THIS_MODULE);
871905
return ret;
@@ -889,7 +923,7 @@ int request_firmware_direct(const struct firmware **firmware_p,
889923
int ret;
890924

891925
__module_get(THIS_MODULE);
892-
ret = _request_firmware(firmware_p, name, device, NULL, 0,
926+
ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
893927
FW_OPT_UEVENT | FW_OPT_NO_WARN |
894928
FW_OPT_NOFALLBACK_SYSFS);
895929
module_put(THIS_MODULE);
@@ -914,7 +948,7 @@ int firmware_request_platform(const struct firmware **firmware,
914948

915949
/* Need to pin this module until return */
916950
__module_get(THIS_MODULE);
917-
ret = _request_firmware(firmware, name, device, NULL, 0,
951+
ret = _request_firmware(firmware, name, device, NULL, 0, 0,
918952
FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM);
919953
module_put(THIS_MODULE);
920954
return ret;
@@ -970,13 +1004,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
9701004
return -EOPNOTSUPP;
9711005

9721006
__module_get(THIS_MODULE);
973-
ret = _request_firmware(firmware_p, name, device, buf, size,
1007+
ret = _request_firmware(firmware_p, name, device, buf, size, 0,
9741008
FW_OPT_UEVENT | FW_OPT_NOCACHE);
9751009
module_put(THIS_MODULE);
9761010
return ret;
9771011
}
9781012
EXPORT_SYMBOL(request_firmware_into_buf);
9791013

1014+
/**
1015+
* request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer
1016+
* @firmware_p: pointer to firmware image
1017+
* @name: name of firmware file
1018+
* @device: device for which firmware is being loaded and DMA region allocated
1019+
* @buf: address of buffer to load firmware into
1020+
* @size: size of buffer
1021+
* @offset: offset into file to read
1022+
*
1023+
* This function works pretty much like request_firmware_into_buf except
1024+
* it allows a partial read of the file.
1025+
*/
1026+
int
1027+
request_partial_firmware_into_buf(const struct firmware **firmware_p,
1028+
const char *name, struct device *device,
1029+
void *buf, size_t size, size_t offset)
1030+
{
1031+
int ret;
1032+
1033+
if (fw_cache_is_setup(device, name))
1034+
return -EOPNOTSUPP;
1035+
1036+
__module_get(THIS_MODULE);
1037+
ret = _request_firmware(firmware_p, name, device, buf, size, offset,
1038+
FW_OPT_UEVENT | FW_OPT_NOCACHE |
1039+
FW_OPT_PARTIAL);
1040+
module_put(THIS_MODULE);
1041+
return ret;
1042+
}
1043+
EXPORT_SYMBOL(request_partial_firmware_into_buf);
1044+
9801045
/**
9811046
* release_firmware() - release the resource associated with a firmware image
9821047
* @fw: firmware resource to release
@@ -1009,7 +1074,7 @@ static void request_firmware_work_func(struct work_struct *work)
10091074

10101075
fw_work = container_of(work, struct firmware_work, work);
10111076

1012-
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
1077+
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
10131078
fw_work->opt_flags);
10141079
fw_work->cont(fw, fw_work->context);
10151080
put_device(fw_work->device); /* taken in request_firmware_nowait() */

include/linux/firmware.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ int request_firmware_direct(const struct firmware **fw, const char *name,
5353
struct device *device);
5454
int request_firmware_into_buf(const struct firmware **firmware_p,
5555
const char *name, struct device *device, void *buf, size_t size);
56+
int request_partial_firmware_into_buf(const struct firmware **firmware_p,
57+
const char *name, struct device *device,
58+
void *buf, size_t size, size_t offset);
5659

5760
void release_firmware(const struct firmware *fw);
5861
#else
@@ -102,6 +105,15 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p,
102105
return -EINVAL;
103106
}
104107

108+
static inline int request_partial_firmware_into_buf
109+
(const struct firmware **firmware_p,
110+
const char *name,
111+
struct device *device,
112+
void *buf, size_t size, size_t offset)
113+
{
114+
return -EINVAL;
115+
}
116+
105117
#endif
106118

107119
int firmware_request_cache(struct device *device, const char *name);

0 commit comments

Comments
 (0)