> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/man/man9/firmware.9.md).

# firmware.9

`firmware_register` — 固件镜像加载与管理

## 名称

`firmware_register`, `firmware_unregister`, `firmware_get`, `firmware_get_flags`, `firmware_put`

## 概要

```c
#include <sys/param.h>

#include <sys/systm.h>

#include <sys/linker.h>

#include <sys/firmware.h>

struct firmware {
	const char	*name;		/* 系统范围的名称 */
	const void	*data;		/* 镜像位置 */
	size_t		datasize;	/* 镜像大小（字节） */
	unsigned int	version;	/* 镜像版本 */
};

const struct firmware *
firmware_register(const char *imagename, const void *data,
    size_t datasize, unsigned int version, const struct firmware *parent)

int
firmware_unregister(const char *imagename)

const struct firmware *
firmware_get(const char *imagename)

const struct firmware *
firmware_get_flags(const char *imagename, uint32_t flags)

void
firmware_put(const struct firmware *fp, int flags)
```

## 描述

`firmware` 抽象层为将 `firmware` 加载到内核以及从内核组件访问此类镜像提供了便捷的接口。

一个 `firmware`（简称 `image`）是驻留在内核内存中的不透明数据块。它与一个作为搜索键的唯一 `imagename` 以及一个整数 `version` 版本号关联，版本号对于固件子系统而言也是不透明信息。

镜像通过调用 `firmware_register` 函数向 `firmware` 子系统注册，并通过调用 `firmware_unregister` 注销。这些函数通常（但不仅限于）由包含固件镜像的专门构造的内核模块调用。这些模块可静态编译进内核，或由 **/boot/loader** 加载，或在运行时手动加载，或由固件子系统按需加载。

固件二进制文件也可直接加载，而无需嵌入到内核模块中。

固件子系统的 `Clients` 可以通过以所需的 `imagename` 为参数调用 `firmware_get`，或以 `imagename` 和 `flags` 为参数调用 `firmware_get_flags` 来请求访问给定镜像。如果尚未注册匹配的镜像，固件子系统将尝试使用下文指定的机制加载它（通常是名称与镜像相同的内核模块，带 `flags`）。

## API 描述

内核固件 API 由以下函数组成：

`firmware_register` 向内核注册一个大小为 `datasize`、位于地址 `data` 的镜像，名称为 `imagename`。

该函数出错时返回 `NULL`（例如由于已存在同名镜像，或镜像表已满），或返回指向所请求镜像的 `const struct firmware *` 指针。

`firmware_unregister` 尝试从系统中注销固件镜像 `imagename`。如果没有对该镜像的待处理引用，则函数成功并返回 0；否则不注销镜像并返回 `EBUSY`。

`firmware_get` 和 `firmware_get_flags` 返回所请求的固件镜像。`flags` 参数可设置为 `FIRMWARE_GET_NOWARN`，指示固件加载或注册错误仅在 `booverbose` 时才记录日志。如果镜像尚未在系统中注册，这些函数会尝试加载它。这涉及链接器子系统和磁盘访问，因此 `firmware_get` 或 `firmware_get_flags` 不得在持有任何锁时调用（`Giant` 除外）。还需注意，如果固件镜像从文件系统加载，该文件系统必须已挂载。特别地，这意味着可能需要延迟来自驱动程序附加方法的请求，除非已知根文件系统已挂载。

成功时，`firmware_get` 和 `firmware_get_flags` 返回指向镜像描述的指针，并增加该镜像的引用计数。失败时，这些函数返回 `NULL`。

`firmware_put` 释放对固件镜像的引用。`flags` 参数可设置为 `FIRMWARE_UNLOAD`，指示如果这是最后一个引用，`firmware_put` 可自由回收与固件镜像关联的资源。默认情况下，固件镜像会被推迟到 [taskqueue(9)](/man/man9/taskqueue.9.md) 线程处理，因此可在持有锁时调用。在某些情况下，例如驱动程序分离时，这是不允许的。

## 通过模块加载固件

如前所述，系统的任何组件都可以通过简单调用 `firmware_register` 在任何时候注册固件镜像。

这通常在包含固件镜像的模块获得控制权时完成，无论是编译进内核、由 **/boot/loader** 预加载，还是通过 [kldload(8)](/man/man8/kldload.8.md) 手动加载。然而，系统可以实现额外的机制，在调用 `firmware_register` 之前将这些镜像带入内存。

当 `firmware_get` 或 `firmware_get_flags` 找不到所请求的镜像时，它会尝试使用可用的加载机制之一加载它。目前只有一种，即 `Loadable`。

名为 `foo` 的固件镜像通过尝试加载名为 `foo.ko` 的模块来查找，使用 [kld(4)](/man/man4/kld.4.md) 中描述的设施。特别地，镜像在 sysctl 变量 `kern.module_path` 指定的目录中查找，在大多数系统上默认为 **/boot/kernel;/boot/modules**。

注意，如果一个模块包含多个镜像，调用者应先对该模块中包含的第一个镜像请求 `firmware_get` 或 `firmware_get_flags`，然后再请求其他镜像。

## 构建可加载固件模块

固件模块通过将 `firmware` 嵌入到合适的可加载内核模块中构建，该模块在加载时调用 `firmware_register`，在卸载时调用 `firmware_unregister`。

各种系统脚本和 makefile 允许你通过简单地编写包含以下条目的 Makefile 来构建模块：

```sh
        KMOD=   imagename
        FIRMWS= image_file:imagename[:version]
        .include <bsd.kmod.mk>
```

其中 KMOD 是模块的基本名；FIRMWS 是以冒号分隔的元组列表，指示要嵌入模块的 image\_file、每个固件镜像的 imagename 和 version。

如果需要将固件镜像嵌入到系统中，应在 \<files.arch> 或 文件中编写适当的条目，例如以下示例来自 **sys/conf/files**：

```sh
iwn1000fw.c			optional iwn1000fw | iwnfw		\
	compile-with	"${AWK} -f $S/tools/fw_stub.awk iwn1000.fw:iwn1000fw -miwn1000fw -c${.TARGET}" \
	no-ctfconvert no-implicit-rule before-depend local		\
	clean		"iwn1000fw.c"
#
# 注意：ld 将为固件镜像生成的二进制符号中的路径编码，
#     因此将文件链接到目标目录以获取在 _fw.c 文件中引用的已知值。
#
iwn1000fw.fwo			optional iwn1000fw | iwnfw		\
	dependency	"iwn1000.fw"					\
	compile-with	"${NORMAL_FWO}"					\
	no-implicit-rule						\
	clean		"iwn1000fw.fwo"
```

固件以前以 uuencoded 文件形式提交到源代码树中，但现在不再需要；二进制固件文件应按供应商提供的形式提交到树中。

注意，以这种方式生成固件模块需要以下工具可用：[awk(1)](/man/man1/awk.1.md)、[make(1)](/man/man1/make.1.md)、编译器和链接器。

## 加载二进制固件文件

### 二进制固件格式

二进制固件文件也可以加载，从 **/boot/loader** 加载，或当 `firmware_get` 无法从内核模块找到已注册的固件时加载。二进制固件文件是固件创建者制作的原始二进制文件。它们提供了一种更简单的固件加载方式，但缺少内核模块的完全灵活性和通用性，具有以下限制：

* 二进制固件文件仅保存一组固件。
* 它们不提供内核模块依赖项，无法确保由引导加载程序自动加载。
* 它们不能编译进内核。
* `imagename` 与用于加载模块的完整路径名相同。
* 版本号假定为零。

### 从 /boot/loader 加载

二进制固件文件可以从命令行使用 “load -t firmware /boot/firmware/filename” 加载，或使用 loader.conf(5) 机制加载类型为 “firmware” 的模块。例如：

```sh
wififw_load="YES"
wififw_name="/boot/firmware/wifi2034_fw.bin"
wififw_type="firmware"
```

### 从 firmware\_get 按需加载

如果没有加载名为 `imagename` 的内嵌固件镜像内核模块，则 `imagename` 将附加到模块路径（默认为 **/boot/firmware/**），如果该文件存在，将使用 `firmware_register` 加载并注册，以文件的完整路径作为 `imagename`。

### 搜索 imagename

`firmware_get` 使用以下算法查找固件镜像：

* 如果已为 `imagename` 注册了现有固件镜像，则返回该镜像。
* 如果 `imagename` 与某个具有完整路径的已注册镜像的尾部子路径匹配，则返回该镜像。
* 内核链接器搜索名为 `imagename` 的内核模块。如果找到内核模块，则加载它，并再次搜索已注册的固件镜像列表。如果找到匹配项，则返回匹配的镜像。
* 内核在固件镜像路径（默认为 **/boot/firmware/**）中搜索名为 `imagename` 的文件。如果该文件存在且可读，其内容将以完整路径作为 `imagename` 注册为固件镜像，并返回该固件。目前，固件镜像的大小限制为 8MB。这可通过 sysctl 变量 `debug.max_firmware_size` 更改。

## 参见

[kld(4)](/man/man4/kld.4.md), [module(9)](/man/man9/module.9.md)

**/boot/firmware**

**/usr/share/examples/kld/firmware**

## 历史

`firmware` 系统引入于 FreeBSD 6.1。二进制固件加载引入于 FreeBSD 15.0。

## 作者

本手册页由 Max Laier <mlaier@FreeBSD.org> 编写。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/man/man9/firmware.9.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
