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

时间:2021-02-25 08:12:52

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:

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

/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).

当我按照“备用构建方法:老式Debian方式”编译2.6.24-24通用Ubuntu内核时,我得到了相应的模块目录,除了需要固件的设备外,我的所有设备都工作,例如我的英特尔无线网卡(ipw2200)模块)。

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尝试加载固件时,控制固件加载的内核子系统无法找到它:

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

errno-base.h defines this as:

errno-base.h将其定义为:

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

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

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

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.

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

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.

我找到了解决我遇到的确切问题的解决方案,但是它不再有效,因为Ubuntu已经取消了/etc/hotplug.d并且不再将其固件存储在/ 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.

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

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.

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

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

加载固件的实际请求是由驱动程序(在我的情况下为ipw2200)通过系统调用完成的:

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.

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

So who's doing it?

那么谁在做呢?

4 个解决方案

#1


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

从内核的角度来看,请参阅/ 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.

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

Now, on Ubuntu 8.04,

现在,在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.

正如您所发现的那样,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

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:

如果您阅读了源代码,您会发现Ubuntu编写了一个solid_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

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.

总而言之:Ubuntu的udev软件包具有首先在/ lib / firmware / $(uname -r)中查找的自定义。此策略正在用户空间中处理。

#2


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.

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

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.

基本上,每个Ubuntu都会带来hal,sysfs,devfs,udev等的新版本......而且事情只会改变。事实上我读过他们停止使用hal。

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

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

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.

在Ubuntu Lucid(编写本文时),使用/lib/udev/rules.d/50-firmware.rules。这个文件调用二进制/ lib / udev / firmware,魔术发生在那里。

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):

魔术应该是这些方面的东西(来源:Linux设备驱动程序,第3版,第14章:Linux设备模型):

  • echo 1 to loading
  • 回显1加载

  • copy firmware to data
  • 将固件复制到数据

  • on failure, echo -1 to loading and halt firmware loading process
  • 失败时,回显-1加载并停止固件加载过程

  • echo 0 to loading (signal the kernel)
  • echo 0加载(发信号内核)

  • 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.

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

Excerpt: Lucid source, udev-151/extras/firmware/firmware.c

摘录: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).

此外,许多设备使用英特尔HEX格式(包含校验和和其他东西的文本文件)(维基,我没有声誉,没有链接能力)。内核程序ihex2fw(从.sEX文件中的kernel_source / lib / firmware中的Makefile调用)将这些HEX文件转换为任意设计的二进制格式,然后Linux内核将其与request_ihex_firmware一起获取,因为他们认为在内核中读取文本文件是愚蠢(这会减慢事情)。

#3


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

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

#4


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

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

emerge ipw2200-firmware

Then go to /usr/src/linux

然后转到/ usr / src / linux

make menucofig

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

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

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

#1


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

从内核的角度来看,请参阅/ 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.

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

Now, on Ubuntu 8.04,

现在,在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.

正如您所发现的那样,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

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:

如果您阅读了源代码,您会发现Ubuntu编写了一个solid_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

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.

总而言之:Ubuntu的udev软件包具有首先在/ lib / firmware / $(uname -r)中查找的自定义。此策略正在用户空间中处理。

#2


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.

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

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.

基本上,每个Ubuntu都会带来hal,sysfs,devfs,udev等的新版本......而且事情只会改变。事实上我读过他们停止使用hal。

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

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

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.

在Ubuntu Lucid(编写本文时),使用/lib/udev/rules.d/50-firmware.rules。这个文件调用二进制/ lib / udev / firmware,魔术发生在那里。

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):

魔术应该是这些方面的东西(来源:Linux设备驱动程序,第3版,第14章:Linux设备模型):

  • echo 1 to loading
  • 回显1加载

  • copy firmware to data
  • 将固件复制到数据

  • on failure, echo -1 to loading and halt firmware loading process
  • 失败时,回显-1加载并停止固件加载过程

  • echo 0 to loading (signal the kernel)
  • echo 0加载(发信号内核)

  • 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.

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

Excerpt: Lucid source, udev-151/extras/firmware/firmware.c

摘录: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).

此外,许多设备使用英特尔HEX格式(包含校验和和其他东西的文本文件)(维基,我没有声誉,没有链接能力)。内核程序ihex2fw(从.sEX文件中的kernel_source / lib / firmware中的Makefile调用)将这些HEX文件转换为任意设计的二进制格式,然后Linux内核将其与request_ihex_firmware一起获取,因为他们认为在内核中读取文本文件是愚蠢(这会减慢事情)。

#3


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

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

#4


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

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

emerge ipw2200-firmware

Then go to /usr/src/linux

然后转到/ usr / src / linux

make menucofig

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

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

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