【问题标题】:How does Linux Kernel know where to look for driver firmware?Linux 内核如何知道在哪里寻找驱动程序固件?
【发布时间】:2010-10-31 07:25:54
【问题描述】:

我正在 Ubuntu 下编译一个自定义内核,但我遇到了一个问题,即我的内核似乎不知道去哪里寻找固件。在 Ubuntu 8.04 下,固件与内核版本的绑定方式与驱动程序模块的绑定方式相同。例如,内核 2.6.24-24-generic 将其内核模块存储在:

/lib/modules/2.6.24-24-generic

及其固件:

/lib/firmware/2.6.24-24-generic

当我根据“Alternate Build Method: The Old-Fashioned Debian Way”编译 2.6.24-24-generic Ubuntu 内核时,我得到了相应的模块目录,我的所有设备都可以工作,除了那些需要固件的设备,例如我的英特尔无线网卡(ipw2200 模块)。

内核日志显示,例如,当 ipw2200 尝试加载固件时,控制固件加载的内核子系统无法找到它:

ipw2200: Detected Intel PRO/Wireless 2200BG Network Connection
ipw2200: ipw2200-bss.fw request_firmware failed: Reason -2

errno-base.h 将其定义为:

#define ENOENT       2  /* No such file or directory */

(返回 ENOENT 的函数在其前面加上一个减号。)

我尝试在 /lib/firmware 中创建一个符号链接,其中我的内核名称指向 2.6.24-24-generic 目录,但是这导致了同样的错误。此固件是非 GPL,由 Intel 提供并由 Ubuntu 打包。我不相信它与特定内核版本有任何实际联系。 cmp表示各个目录下的版本是一样的。

那么内核是如何知道去哪里寻找固件的呢?

更新

我找到了this solution 来解决我遇到的确切问题,但是它不再有效,因为 Ubuntu 已经消除了 /etc/hotplug.d 并且不再将其固件存储在 /usr/lib/hotplug/firmware 中。

更新2

更多的研究发现了更多的答案。在udev 的第92 版之前,程序firmware_helper 是加载固件的方式。从udev93 开始,这个程序被一个名为firmware.sh 的脚本取代,据我所知,它提供了相同的功能。这两个都将固件路径硬编码到/lib/firmware。 Ubuntu 似乎仍在使用 /lib/udev/firmware_helper 二进制文件。

固件文件的名称被传递给环境变量$FIRMWARE中的firmware_helper,该变量与路径/lib/firmware连接并用于加载固件。

加载固件的实际请求是由驱动程序(在我的例子中为 ipw2200)通过系统调用发出的:

request_firmware(..., "ipw2200-bss.fw", ...);

现在在调用request_firmware 和firmware_helper 之间的驱动程序查看$FIRMWARE 环境变量,内核包名称被添加到固件名称之前。

那么谁在做呢?

【问题讨论】:

  • 有人可以添加评论,解释他们为什么投票结束这个问题吗?非常感谢您的反馈。
  • 它被投票关闭,因为它“属于 serverfault.com”。由于问题不是关于编程,而是更多关于管理,这符合两个站点的使命宣言。
  • 感谢您的反馈。我认为这是一个编程问题,因为它是在尝试构建自定义内核时遇到的问题。我想知道为什么有人会认为这与编程无关,尽管我想我有一些很好的猜测。

标签: linux-kernel linux-device-driver firmware


【解决方案1】:

从内核角度看/usr/src/linux/Documentation/firmware_class/README:

内核(驱动程序):调用 request_firmware(&fw_entry, $FIRMWARE, device) 用户空间: - /sys/class/firmware/xxx/{loading,data} 出现。 - hotplug 使用 $FIRMWARE 中的固件标识符调用 和通常的热插拔环境。 - 热插拔:echo 1 > /sys/class/firmware/xxx/loading 内核:丢弃任何以前的部分加载。 用户空间: - 热插拔:cat proper_firmware_image > \ /sys/类/固件/xxx/数据 内核:以 PAGE_SIZE 为增量增长一个缓冲区来保存图像 进来。 用户空间: - 热插拔:echo 0 > /sys/class/firmware/xxx/loading 内核:request_firmware() 返回并且驱动程序有固件 fw_entry->{data,size} 中的图像。如果出现问题 request_firmware() 返回非零并且 fw_entry 设置为 空值。 kernel(driver):驱动代码调用release_firmware(fw_entry)释放 固件映像和任何相关资源。

内核实际上根本不加载任何固件。它只是通知用户空间,“我想要一个名为 xxx 的固件”,并等待用户空间将固件映像通过管道传回内核。

现在,在 Ubuntu 8.04 上,

$ grep 固件 /etc/udev/rules.d/80-program.rules # 按需加载固件 SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"

如您所见,udev 配置为在内核请求固件时运行firmware_helper。

$ apt-get 源代码 udev 阅读包裹清单...完成 构建依赖树 读取状态信息...完成 需要获取 312kB 的源文件。 获取:1 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (dsc) [716B] 获取:2 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (tar) [245kB] 获取:3 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (diff) [65.7kB] 在 1 秒内获取 312kB (223kB/s) gpg:签名于 2009 年 4 月 14 日星期二 05:31:34 PM EDT 使用 DSA 密钥 ID 17063E6D gpg:无法检查签名:找不到公钥 dpkg-source:在 udev-117 中提取 udev dpkg-source:解压 udev_117.orig.tar.gz dpkg-source:应用 ./udev_117-8ubuntu0.2.diff.gz $ cd udev-117/ $ cat debian/patches/80-extras-firmware.patch

如果您阅读源代码,您会发现 Ubuntu 编写了一个firmware_helper,它被硬编码为首先查找/lib/modules/$(uname -r)/$FIRMWARE,然后是/lib/modules/$FIRMWARE,并且没有其他位置。把它翻译成sh,它大致是这样的:

echo -n 1 > /sys/$DEVPATH/loading
cat /lib/firmware/$(uname -r)/$FIRMWARE > /sys/$DEVPATH/data \
    || cat /lib/firmware/$FIRMWARE      > /sys/$DEVPATH/data
if [ $? = 0 ]; then
    echo -n  1 > /sys/$DEVPATH/loading
    echo -n -1 > /sys/$DEVPATH/loading
fi

这正是内核所期望的格式。


长话短说:Ubuntu 的udev 软件包具有始终首先查看/lib/firmware/$(uname -r) 的自定义项。此策略正在用户空间中处理。

【讨论】:

  • 精彩的回答,谢谢!我正在查看 udev 源,试图找出 Ubuntu 的 firmware_helper 来自哪里。应该刚刚对 udev 源代码树中的固件进行了递归 grep。
  • 它实际上可能来自 udev-extras 树,Ubuntu 已决定修改其中的某些部分并与他们的 udev 包捆绑。
  • apt-get source 下载并解压软件包的 Debian(或 Ubuntu)源代码树,其中包括一个 debian 目录,其中包含打包信息和特定于发行版的设置,其中有时包括不属于在上游找到。这是一个例子。
  • 如果不清楚,Hotplug 已被 Udev 取代,内核树中的文档已过期。 Arch Udev Wiki 和 Suse udev wiki 解释这些变化。 Andy Matteson 的回答比这更胜一筹。
  • 给定的 README 链接指向当前的 Linux 行为,自发布此响应以来,该行为已发生变化。现在,Linux 会在几个目录中查找,包括 /lib/firmware/$(uname -r)before 询问 udev。另见 lwn 文章:Udev and firmware。
【解决方案2】:

哇,这是非常有用的信息,它引导我解决了我在为需要固件的设备制作自定义 USB 内核模块时遇到的问题。

基本上,每个 Ubuntu 都会带来 hal、sysfs、devfs、udev 等的新改版……而事情只是发生了变化。事实上,我读到他们停止使用 hal。

让我们再次对其进行逆向工程,使其与最新的 [Ubuntu] 系统相关。

在 Ubuntu Lucid(撰写本文时的最新版本)上,使用了 /lib/udev/rules.d/50-firmware.rules。这个文件调用二进制文件/lib/udev/firmware,神奇的地方发生了。

清单:/lib/udev/rules.d/50-firmware.rules

# firmware-class requests, copies files into the kernel
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware --firmware=$env{FIRMWARE} --devpath=$env{DEVPATH}"

魔法应该是这样的(来源:Linux Device Drivers, 3rd Ed., Ch. 14: The Linux Device Model):

  • 回显 1 到 loading
  • 将固件复制到data
  • 失败时,将 -1 回显到 loading 并暂停固件加载过程
  • echo 0 到 loading(向内核发送信号)
  • 然后,特定的内核模块接收数据并将其推送到设备

如果您查看 Lucid 的 udev 源页面,在 udev-151/extras/firmware/firmware.c 中,该固件 /lib/udev/firmware 二进制文件的源,这正是发生的事情。

摘录:清晰的源代码,udev-151/extras/firmware/firmware.c

    util_strscpyl(datapath, sizeof(datapath), udev_get_sys_path(udev), devpath, "/data", NULL);
    if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) {
            err(udev, "error sending firmware '%s' to device\n", firmware);
            set_loading(udev, loadpath, "-1");
            rc = 4;
            goto exit;
    };

    set_loading(udev, loadpath, "0");

此外,许多设备使用英特尔 HEX 格式(包含校验和和其他内容的文本文件)(维基它我没有声誉,也无法链接)。内核程序 ihex2fw(从 .HEX 文件的 kernel_source/lib/firmware 中的 Makefile 调用)将这些 HEX 文件转换为任意设计的二进制格式,然后 Linux 内核使用 request_ihex_firmware内核很傻(它会减慢速度)。

【讨论】:

  • +1 现在你已经有了一些代表并且可以链接。如果您发现 OP 中的信息或其他人的回答有用,请投票给他们。
【解决方案3】:

在当前的 Linux 系统上,这是通过 udev 和 firmware.agent 处理的。

【讨论】:

  • 显然 Ubuntu 8.04 在 udev 中没有firmware.agent,它还有一个叫做firmware_helper 的东西。无论如何,这如何告诉内核在哪里可以找到固件?
  • 无论udev调用什么命令,它都负责将固件加载到内核中。根据我对 Debian 的固件代理的阅读,它将固件写入 /sys 中的一个特殊文件
【解决方案4】:

Linux 3.5.7 Gentoo,我也有同样的问题。 已解决:

emerge ipw2200-firmware

然后转到/usr/src/linux

make menucofig

在设备驱动上,删除所有不需要的无线驱动,将 Intell 2200 设置为模块并重新编译。

make
make modules_install
cp arch/x86/boot/bzImage /boot/kernel-yourdefault

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 2023-03-07
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2014-02-05
    • 2015-12-31
    相关资源
    最近更新 更多