Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Linux Kernel know where to look for driver 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?

like image 549
Robert S. Barnes Avatar asked Jun 04 '09 12:06

Robert S. Barnes


People also ask

Where does Linux look for firmware?

Note that the linux-firmware-nonfree package is not installed by default. The firmware files are placed into /lib/firmware.

How does Linux know which driver to load?

How does the Linux kernel know which drivers to load at boot? The kernel generates events for devices on e.g. the PCI bus when they are plugged (either hot or cold; events are queued until userspace runs AFAIR).

How does the Linux kernel load firmware?

The kernel's firmware loading API is generally accessed by calls to request_firmware() or request_firmware_nowait(). A driver calls one of these functions with the name of the firmware file it wants, and the kernel attempts to load it using this process: Check for firmware in the kernel image, load from there if found.

What is firmware for Linux kernel drivers?

Linux firmware is a package distributed alongside the Linux kernel that contains firmware binary blobs necessary for partial or full functionality of certain hardware devices.


1 Answers

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.

like image 200
ephemient Avatar answered Oct 06 '22 10:10

ephemient