Linux 内核如何知道在哪里寻找驱动程序固件?

发布于 2024-07-23 01:18:32 字数 2094 浏览 12 评论 0原文

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

/lib/modules/2.6.24-24-generic

并将其固件存储在:

/lib/firmware/2.6.24-24-generic

当我根据“替代构建方法:老式 Debian 方式" 我获得了适当的模块目录,并且我的所有设备都可以工作,除了需要固件的设备(例如我的设备)英特尔无线网卡(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 表明各个目录中的版本是相同的。

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

更新

我找到了这个解决方案来解决我所遇到的确切问题但是它不再起作用,因为 Ubuntu 已经消除了 /etc/hotplug.d 并且不再将其固件存储在 /usr/lib/hotplug/firmware 中。

更新2

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

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

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

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

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

那么是谁在做呢?

I'm compiling a custom kernel under Ubuntu and I'm running into the problem that my kernel doesn't seem to know where to look for firmware. Under Ubuntu 8.04, firmware is tied to kernel version the same way driver modules are. For example, kernel 2.6.24-24-generic stores its kernel modules in:

/lib/modules/2.6.24-24-generic

and its firmware in:

/lib/firmware/2.6.24-24-generic

When I compile the 2.6.24-24-generic Ubuntu kernel according the "Alternate Build Method: The Old-Fashioned Debian Way" I get the appropriate modules directory and all my devices work except those requiring firmware such as my Intel wireless card (ipw2200 module).

The kernel log shows for example that when ipw2200 tries to load the firmware the kernel subsystem controlling the loading of firmware is unable to locate it:

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

errno-base.h defines this as:

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

(The function returning ENOENT puts a minus in front of it.)

I tried creating a symlink in /lib/firmware where my kernel's name pointed to the 2.6.24-24-generic directory, however this resulted in the same error. This firmware is non-GPL, provided by Intel and packed by Ubuntu. I don't believe it has any actual tie to a particular kernel version. cmp shows that the versions in the various directories are identical.

So how does the kernel know where to look for firmware?

Update

I found this solution to the exact problem I'm having, however it no longer works as Ubuntu has eliminated /etc/hotplug.d and no longer stores its firmware in /usr/lib/hotplug/firmware.

Update2

Some more research turned up some more answers. Up until version 92 of udev, the program firmware_helper was the way firmware got loaded. Starting with udev 93 this program was replaced with a script named firmware.sh providing identical functionality as far as I can tell. Both of these hardcode the firmware path to /lib/firmware. Ubuntu still seems to be using the /lib/udev/firmware_helper binary.

The name of the firmware file is passed to firmware_helper in the environment variable $FIRMWARE which is concatenated to the path /lib/firmware and used to load the firmware.

The actual request to load the firmware is made by the driver (ipw2200 in my case) via the system call:

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

Now somewhere in between the driver calling request_firmware and firmware_helper looking at the $FIRMWARE environment variable, the kernel package name is getting prepended to the firmware name.

So who's doing it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦毁影碎の 2024-07-30 01:18:33

从内核的角度来看,请参阅 /usr/src/linux/Documentation/firmware_class/README

 kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device)

 userspace:
        - /sys/class/firmware/xxx/{loading,data} appear.
        - hotplug gets called with a firmware identifier in $FIRMWARE
          and the usual hotplug environment.
                - hotplug: echo 1 > /sys/class/firmware/xxx/loading

 kernel: Discard any previous partial load.

 userspace:
                - hotplug: cat appropriate_firmware_image > \
                                        /sys/class/firmware/xxx/data

 kernel: grows a buffer in PAGE_SIZE increments to hold the image as it
         comes in.

 userspace:
                - hotplug: echo 0 > /sys/class/firmware/xxx/loading

 kernel: request_firmware() returns and the driver has the firmware
         image in fw_entry->{data,size}. If something went wrong
         request_firmware() returns non-zero and fw_entry is set to
         NULL.

 kernel(driver): Driver code calls release_firmware(fw_entry) releasing
                 the firmware image and any related resource.

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

现在,在 Ubuntu 8.04 上,

$ grep firmware /etc/udev/rules.d/80-program.rules
# Load firmware on demand
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"

正如您所发现的,udev 配置为在内核请求固件时运行 firmware_helper

$ apt-get source udev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Need to get 312kB of source archives.
Get:1 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (dsc) [716B]
Get:2 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (tar) [245kB]
Get:3 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (diff) [65.7kB]
Fetched 312kB in 1s (223kB/s)
gpg: Signature made Tue 14 Apr 2009 05:31:34 PM EDT using DSA key ID 17063E6D
gpg: Can't check signature: public key not found
dpkg-source: extracting udev in udev-117
dpkg-source: unpacking udev_117.orig.tar.gz
dpkg-source: applying ./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) 中查找的自定义项。 该策略是在用户空间中处理的。

From the kernel's perspective, see /usr/src/linux/Documentation/firmware_class/README:

 kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device)

 userspace:
        - /sys/class/firmware/xxx/{loading,data} appear.
        - hotplug gets called with a firmware identifier in $FIRMWARE
          and the usual hotplug environment.
                - hotplug: echo 1 > /sys/class/firmware/xxx/loading

 kernel: Discard any previous partial load.

 userspace:
                - hotplug: cat appropriate_firmware_image > \
                                        /sys/class/firmware/xxx/data

 kernel: grows a buffer in PAGE_SIZE increments to hold the image as it
         comes in.

 userspace:
                - hotplug: echo 0 > /sys/class/firmware/xxx/loading

 kernel: request_firmware() returns and the driver has the firmware
         image in fw_entry->{data,size}. If something went wrong
         request_firmware() returns non-zero and fw_entry is set to
         NULL.

 kernel(driver): Driver code calls release_firmware(fw_entry) releasing
                 the firmware image and any related resource.

The kernel doesn't actually load any firmware at all. It simply informs userspace, "I want a firmware by the name of xxx", and waits for userspace to pipe the firmware image back to the kernel.

Now, on Ubuntu 8.04,

$ grep firmware /etc/udev/rules.d/80-program.rules
# Load firmware on demand
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"

so as you've discovered, udev is configured to run firmware_helper when the kernel asks for firmware.

$ apt-get source udev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Need to get 312kB of source archives.
Get:1 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (dsc) [716B]
Get:2 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (tar) [245kB]
Get:3 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (diff) [65.7kB]
Fetched 312kB in 1s (223kB/s)
gpg: Signature made Tue 14 Apr 2009 05:31:34 PM EDT using DSA key ID 17063E6D
gpg: Can't check signature: public key not found
dpkg-source: extracting udev in udev-117
dpkg-source: unpacking udev_117.orig.tar.gz
dpkg-source: applying ./udev_117-8ubuntu0.2.diff.gz
$ cd udev-117/
$ cat debian/patches/80-extras-firmware.patch

If you read the source, you'll find that Ubuntu wrote a firmware_helper which is hard-coded to first look for /lib/modules/$(uname -r)/$FIRMWARE, then /lib/modules/$FIRMWARE, and no other locations. Translating it to sh, it does approximately this:

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

which is exactly the format the kernel expects.


To make a long story short: Ubuntu's udev package has customizations that always look in /lib/firmware/$(uname -r) first. This policy is being handled in userspace.

梦里寻她 2024-07-30 01:18:33

哇,这是非常有用的信息,它让我在为需要固件的设备制作自定义 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 设备驱动程序,第 3 版,第 14 章:Linux 设备模型):

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

如果您查看 Lucid 的 udev 源代码页面,位于 udev-151/extras/firmware/firmware.c 中,即该固件 /lib/udev/firmware 二进制文件的源代码,那么您就会发现 在。

摘录:Lucid 来源,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");

此外,许多设备使用 Intel HEX 格式(包含校验和和其他内容的文本文件)(wiki 它我没有声誉,也无法链接)。 内核程序 ihex2fw(从 kernel_source/lib/firmware 中的 .HEX 文件上的 Makefile 调用)将这些 HEX 文件转换为任意设计的二进制格式,然后 Linux 内核通过 request_ihex_firmware 获取该格式,因为他们认为在内核中读取文本文件是愚蠢的(它会减慢速度)。

Wow this is very useful information and it led me to the solution for my problem when making a custom USB kernel module for a device requiring firmware.

Basically, every Ubuntu brings a new rehash of hal,sysfs,devfs,udev,and so on...and things just change. In fact I read they stopped using hal.

So let's reverse engineer this yet again so it's pertinent to the latest [Ubuntu] systems.

On Ubuntu Lucid (the latest at time of writing), /lib/udev/rules.d/50-firmware.rules is used. This file calls the binary /lib/udev/firmware, where magic happens.

Listing: /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}"

The magic should be something along these lines (source: Linux Device Drivers, 3rd Ed., Ch. 14: The Linux Device Model):

  • echo 1 to loading
  • copy firmware to data
  • on failure, echo -1 to loading and halt firmware loading process
  • echo 0 to loading (signal the kernel)
  • then, a specific kernel module receives the data and pushes it to the device

If you look at Lucid's source page for udev, in udev-151/extras/firmware/firmware.c, the source for that firmware /lib/udev/firmware binary, that's exactly what goes on.

Excerpt: Lucid source, 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");

Additionally, many devices use an Intel HEX format (textish files containing checksum and other stuff) (wiki it i have no reputation and no ability to link). The kernel program ihex2fw (called from Makefile in kernel_source/lib/firmware on .HEX files) converts these HEX files to an arbitrary-designed binary format that the Linux kernel then picks up with request_ihex_firmware, because they thought reading text files in the kernel was silly (it would slow things down).

风向决定发型 2024-07-30 01:18:33

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

On current Linux systems, this is handled via udev and the firmware.agent.

蛮可爱 2024-07-30 01:18:33

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

emerge ipw2200-firmware

然后转到

make menucofig

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

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

Linux 3.5.7 Gentoo, I have the same issue.
SOLVED:

emerge ipw2200-firmware

Then go to /usr/src/linux

make menucofig

on device driver, remove all wirless drivers don't needed, set Intell 2200 as module and recompile.

make
make modules_install
cp arch/x86/boot/bzImage /boot/kernel-yourdefault
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文