57

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?

wallyk
  • 56,922
  • 16
  • 83
  • 148
Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
  • Could someone please add a comment explaining why they are voting to close this question? I'd appreciate the feedback. – Robert S. Barnes Jun 04 '09 at 13:44
  • 2
    It's being voted for closing because it "belongs on serverfault.com". Since it the question is not about programming, and rather more about administration, this is in line with the mission statements of both sites. – ephemient Jun 04 '09 at 22:21
  • Thanks for the feedback. I had considered this a programming question since it was a problem encountered while trying to build a custom kernel. I would like to know why specifically someone would consider this not programming related, although I think I have some good guesses why. – Robert S. Barnes Jun 05 '09 at 14:18

4 Answers4

44

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.

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • 1
    Wonderful answer, thanks! I was looking through the udev source pulling my hair out trying to figure out where Ubuntu's `firmware_helper` was coming from. Should have just done a recursive grep for firmware in the udev source tree. – Robert S. Barnes Jun 05 '09 at 08:20
  • It's actually probably from the udev-extras tree, some parts of which Ubuntu has decided to modify and bundle with their udev package. – ephemient Jun 05 '09 at 13:41
  • `apt-get source` downloads and unpacks the Debian (or Ubuntu) source tree for the package, which includes a `debian` directory with packaging information and distribution-specific setup, which sometimes includes patches which aren't found upstream. This is a case of that. – ephemient Jun 05 '09 at 13:45
  • 1
    In case its not clear, Hotplug has been replaced by Udev, that documentation in the Kernel tree is out of date. [Arch Udev Wiki](https://wiki.archlinux.org/index.php/Udev) and [Suse udev wiki](http://www.mpipks-dresden.mpg.de/~mueller/docs/suse10.2/html/opensuse-manual_en/manual/sec.udev.hplug.html) explain the changes. Andy Matteson's answer is superior to this. – Kevin Jul 19 '13 at 14:12
  • @kevinf Andy's answer is the same as this. On a kernel "hotplug" uevent, some userspace program loads data. This is typically packaged with udev. – ephemient Jul 19 '13 at 23:54
  • 3
    The given README link points to the current Linux behavior, which has changed since this response was posted. Now, Linux looks in several directories including `/lib/firmware/$(uname -r)` **before** asking udev. See also lwn article: [Udev and firmware](https://lwn.net/Articles/518942/). – yonran Sep 06 '13 at 21:02
  • The quoted doc says you have to write 0 to "/sys/$DEVPATH/loading" when you're done, however your sample code echos 1 and then -1 to it, and only if some firmware was actually found. Simple mistake or do you know something I don't? – Simon Apr 11 '19 at 10:36
14

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

Andy Matteson
  • 368
  • 3
  • 11
1

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
MaxV
  • 588
  • 1
  • 4
  • 13
1

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

David Schmitt
  • 58,259
  • 26
  • 121
  • 165
  • Apparently Ubuntu 8.04 doesn't have firmware.agent in udev, it has something else called firmware_helper. In any case, how does this tell the kernel where to find firmware? – Robert S. Barnes Jun 04 '09 at 13:02
  • Whatever command is called by udev, it is responsible for loading the firmware into the kernel. From my reading of Debian's firmware.agent, it writes the firmware into a special fil in /sys – David Schmitt Jun 05 '09 at 06:29