Skip to content

Commit

Permalink
firmware_loader: formalize built-in firmware API
Browse files Browse the repository at this point in the history
Formalize the built-in firmware with a proper API. This can later
be used by other callers where all they need is built-in firmware.

We export the firmware_request_builtin() call for now only
under the TEST_FIRMWARE symbol namespace as there are no
direct modular users for it. If they pop up they are free
to export it generally. Built-in code always gets access to
the callers and we'll demonstrate a hidden user which has been
lurking in the kernel for a while and the reason why using a
proper API was better long term.

Reviewed-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
Link: https://lore.kernel.org/r/20211021155843.1969401-2-mcgrof@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Luis Chamberlain authored and Greg Kroah-Hartman committed Oct 22, 2021
1 parent c87761d commit 48d09e9
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 79 deletions.
6 changes: 4 additions & 2 deletions drivers/base/firmware_loader/builtin/Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += main.o

# Create $(fwdir) from $(CONFIG_EXTRA_FIRMWARE_DIR) -- if it doesn't have a
# leading /, it's relative to $(srctree).
fwdir := $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE_DIR))
fwdir := $(addprefix $(srctree)/,$(filter-out /%,$(fwdir)))$(filter /%,$(fwdir))

obj-y := $(addsuffix .gen.o, $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE)))
firmware := $(addsuffix .gen.o, $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE)))
obj-y += $(firmware)

FWNAME = $(patsubst $(obj)/%.gen.S,%,$@)
FWSTR = $(subst $(comma),_,$(subst /,_,$(subst .,_,$(subst -,_,$(FWNAME)))))
Expand Down Expand Up @@ -34,7 +36,7 @@ $(obj)/%.gen.S: FORCE
$(call filechk,fwbin)

# The .o files depend on the binaries directly; the .S files don't.
$(addprefix $(obj)/, $(obj-y)): $(obj)/%.gen.o: $(fwdir)/%
$(addprefix $(obj)/, $(firmware)): $(obj)/%.gen.o: $(fwdir)/%

targets := $(patsubst $(obj)/%,%, \
$(shell find $(obj) -name \*.gen.S 2>/dev/null))
100 changes: 100 additions & 0 deletions drivers/base/firmware_loader/builtin/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0
/* Builtin firmware support */

#include <linux/firmware.h>
#include "../firmware.h"

/* Only if FW_LOADER=y */
#ifdef CONFIG_FW_LOADER

extern struct builtin_fw __start_builtin_fw[];
extern struct builtin_fw __end_builtin_fw[];

static bool fw_copy_to_prealloc_buf(struct firmware *fw,
void *buf, size_t size)
{
if (!buf)
return true;
if (size < fw->size)
return false;
memcpy(buf, fw->data, fw->size);
return true;
}

/**
* firmware_request_builtin() - load builtin firmware
* @fw: pointer to firmware struct
* @name: name of firmware file
*
* Some use cases in the kernel have a requirement so that no memory allocator
* is involved as these calls take place early in boot process. An example is
* the x86 CPU microcode loader. In these cases all the caller wants is to see
* if the firmware was built-in and if so use it right away. This can be used
* for such cases.
*
* This looks for the firmware in the built-in kernel. Only if the kernel was
* built-in with the firmware you are looking for will this return successfully.
*
* Callers of this API do not need to use release_firmware() as the pointer to
* the firmware is expected to be provided locally on the stack of the caller.
**/
bool firmware_request_builtin(struct firmware *fw, const char *name)
{
struct builtin_fw *b_fw;

if (!fw)
return false;

for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
if (strcmp(name, b_fw->name) == 0) {
fw->size = b_fw->size;
fw->data = b_fw->data;
return true;
}
}

return false;
}
EXPORT_SYMBOL_NS_GPL(firmware_request_builtin, TEST_FIRMWARE);

/**
* firmware_request_builtin_buf() - load builtin firmware into optional buffer
* @fw: pointer to firmware struct
* @name: name of firmware file
* @buf: If set this lets you use a pre-allocated buffer so that the built-in
* firmware into is copied into. This field can be NULL. It is used by
* callers such as request_firmware_into_buf() and
* request_partial_firmware_into_buf()
* @size: if buf was provided, the max size of the allocated buffer available.
* If the built-in firmware does not fit into the pre-allocated @buf this
* call will fail.
*
* This looks for the firmware in the built-in kernel. Only if the kernel was
* built-in with the firmware you are looking for will this call possibly
* succeed. If you passed a @buf the firmware will be copied into it *iff* the
* built-in firmware fits into the pre-allocated buffer size specified in
* @size.
*
* This caller is to be used internally by the firmware_loader only.
**/
bool firmware_request_builtin_buf(struct firmware *fw, const char *name,
void *buf, size_t size)
{
if (!firmware_request_builtin(fw, name))
return false;

return fw_copy_to_prealloc_buf(fw, buf, size);
}

bool firmware_is_builtin(const struct firmware *fw)
{
struct builtin_fw *b_fw;

for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
if (fw->data == b_fw->data)
return true;

return false;
}

#endif
17 changes: 17 additions & 0 deletions drivers/base/firmware_loader/firmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,23 @@ static inline void fw_state_done(struct fw_priv *fw_priv)

int assign_fw(struct firmware *fw, struct device *device);

#ifdef CONFIG_FW_LOADER
bool firmware_is_builtin(const struct firmware *fw);
bool firmware_request_builtin_buf(struct firmware *fw, const char *name,
void *buf, size_t size);
#else /* module case */
static inline bool firmware_is_builtin(const struct firmware *fw)
{
return false;
}
static inline bool firmware_request_builtin_buf(struct firmware *fw,
const char *name,
void *buf, size_t size)
{
return false;
}
#endif

#ifdef CONFIG_FW_LOADER_PAGED_BUF
void fw_free_paged_buf(struct fw_priv *fw_priv);
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
Expand Down
78 changes: 1 addition & 77 deletions drivers/base/firmware_loader/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,82 +93,6 @@ DEFINE_MUTEX(fw_lock);

static struct firmware_cache fw_cache;

/* Builtin firmware support */

#ifdef CONFIG_FW_LOADER

extern struct builtin_fw __start_builtin_fw[];
extern struct builtin_fw __end_builtin_fw[];

static bool fw_copy_to_prealloc_buf(struct firmware *fw,
void *buf, size_t size)
{
if (!buf)
return true;
if (size < fw->size)
return false;
memcpy(buf, fw->data, fw->size);
return true;
}

static bool firmware_request_builtin(struct firmware *fw, const char *name)
{
struct builtin_fw *b_fw;

if (!fw)
return false;

for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
if (strcmp(name, b_fw->name) == 0) {
fw->size = b_fw->size;
fw->data = b_fw->data;
return true;
}
}

return false;
}

static bool firmware_request_builtin_buf(struct firmware *fw, const char *name,
void *buf, size_t size)
{
if (!firmware_request_builtin(fw, name))
return false;
return fw_copy_to_prealloc_buf(fw, buf, size);
}

static bool fw_is_builtin_firmware(const struct firmware *fw)
{
struct builtin_fw *b_fw;

for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
if (fw->data == b_fw->data)
return true;

return false;
}

#else /* Module case - no builtin firmware support */

static inline bool firmware_request_builtin(struct firmware *fw,
const char *name)
{
return false;
}

static inline bool firmware_request_builtin_buf(struct firmware *fw,
const char *name, void *buf,
size_t size)
{
return false;
}

static inline bool fw_is_builtin_firmware(const struct firmware *fw)
{
return false;
}
#endif

static void fw_state_init(struct fw_priv *fw_priv)
{
struct fw_state *fw_st = &fw_priv->fw_st;
Expand Down Expand Up @@ -1068,7 +992,7 @@ EXPORT_SYMBOL(request_partial_firmware_into_buf);
void release_firmware(const struct firmware *fw)
{
if (fw) {
if (!fw_is_builtin_firmware(fw))
if (!firmware_is_builtin(fw))
firmware_free_data(fw);
kfree(fw);
}
Expand Down
15 changes: 15 additions & 0 deletions include/linux/firmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ struct firmware {
struct module;
struct device;

/*
* Built-in firmware functionality is only available if FW_LOADER=y, but not
* FW_LOADER=m
*/
#ifdef CONFIG_FW_LOADER
struct builtin_fw {
char *name;
void *data;
unsigned long size;
};

bool firmware_request_builtin(struct firmware *fw, const char *name);

/* We have to play tricks here much like stringify() to get the
__COUNTER__ macro to be expanded as we want it */
#define __fw_concat1(x, y) x##y
Expand All @@ -38,6 +45,14 @@ struct builtin_fw {
static const struct builtin_fw __fw_concat(__builtin_fw,__COUNTER__) \
__used __section(".builtin_fw") = { name, blob, size }

#else
static inline bool firmware_request_builtin(struct firmware *fw,
const char *name)
{
return false;
}
#endif

#if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE))
int request_firmware(const struct firmware **fw, const char *name,
struct device *device);
Expand Down

0 comments on commit 48d09e9

Please sign in to comment.