162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Builtin firmware support */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/firmware.h>
562306a36Sopenharmony_ci#include "../firmware.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/* Only if FW_LOADER=y */
862306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistruct builtin_fw {
1162306a36Sopenharmony_ci	char *name;
1262306a36Sopenharmony_ci	void *data;
1362306a36Sopenharmony_ci	unsigned long size;
1462306a36Sopenharmony_ci};
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciextern struct builtin_fw __start_builtin_fw[];
1762306a36Sopenharmony_ciextern struct builtin_fw __end_builtin_fw[];
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic bool fw_copy_to_prealloc_buf(struct firmware *fw,
2062306a36Sopenharmony_ci				    void *buf, size_t size)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	if (!buf)
2362306a36Sopenharmony_ci		return true;
2462306a36Sopenharmony_ci	if (size < fw->size)
2562306a36Sopenharmony_ci		return false;
2662306a36Sopenharmony_ci	memcpy(buf, fw->data, fw->size);
2762306a36Sopenharmony_ci	return true;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/**
3162306a36Sopenharmony_ci * firmware_request_builtin() - load builtin firmware
3262306a36Sopenharmony_ci * @fw: pointer to firmware struct
3362306a36Sopenharmony_ci * @name: name of firmware file
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Some use cases in the kernel have a requirement so that no memory allocator
3662306a36Sopenharmony_ci * is involved as these calls take place early in boot process. An example is
3762306a36Sopenharmony_ci * the x86 CPU microcode loader. In these cases all the caller wants is to see
3862306a36Sopenharmony_ci * if the firmware was built-in and if so use it right away. This can be used
3962306a36Sopenharmony_ci * for such cases.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * This looks for the firmware in the built-in kernel. Only if the kernel was
4262306a36Sopenharmony_ci * built-in with the firmware you are looking for will this return successfully.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * Callers of this API do not need to use release_firmware() as the pointer to
4562306a36Sopenharmony_ci * the firmware is expected to be provided locally on the stack of the caller.
4662306a36Sopenharmony_ci **/
4762306a36Sopenharmony_cibool firmware_request_builtin(struct firmware *fw, const char *name)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct builtin_fw *b_fw;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!fw)
5262306a36Sopenharmony_ci		return false;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
5562306a36Sopenharmony_ci		if (strcmp(name, b_fw->name) == 0) {
5662306a36Sopenharmony_ci			fw->size = b_fw->size;
5762306a36Sopenharmony_ci			fw->data = b_fw->data;
5862306a36Sopenharmony_ci			return true;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return false;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(firmware_request_builtin, TEST_FIRMWARE);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/**
6762306a36Sopenharmony_ci * firmware_request_builtin_buf() - load builtin firmware into optional buffer
6862306a36Sopenharmony_ci * @fw: pointer to firmware struct
6962306a36Sopenharmony_ci * @name: name of firmware file
7062306a36Sopenharmony_ci * @buf: If set this lets you use a pre-allocated buffer so that the built-in
7162306a36Sopenharmony_ci *	firmware into is copied into. This field can be NULL. It is used by
7262306a36Sopenharmony_ci *	callers such as request_firmware_into_buf() and
7362306a36Sopenharmony_ci *	request_partial_firmware_into_buf()
7462306a36Sopenharmony_ci * @size: if buf was provided, the max size of the allocated buffer available.
7562306a36Sopenharmony_ci *	If the built-in firmware does not fit into the pre-allocated @buf this
7662306a36Sopenharmony_ci *	call will fail.
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * This looks for the firmware in the built-in kernel. Only if the kernel was
7962306a36Sopenharmony_ci * built-in with the firmware you are looking for will this call possibly
8062306a36Sopenharmony_ci * succeed. If you passed a @buf the firmware will be copied into it *iff* the
8162306a36Sopenharmony_ci * built-in firmware fits into the pre-allocated buffer size specified in
8262306a36Sopenharmony_ci * @size.
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * This caller is to be used internally by the firmware_loader only.
8562306a36Sopenharmony_ci **/
8662306a36Sopenharmony_cibool firmware_request_builtin_buf(struct firmware *fw, const char *name,
8762306a36Sopenharmony_ci				  void *buf, size_t size)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (!firmware_request_builtin(fw, name))
9062306a36Sopenharmony_ci		return false;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return fw_copy_to_prealloc_buf(fw, buf, size);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cibool firmware_is_builtin(const struct firmware *fw)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct builtin_fw *b_fw;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
10062306a36Sopenharmony_ci		if (fw->data == b_fw->data)
10162306a36Sopenharmony_ci			return true;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return false;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#endif
107