¿Cómo sabe el kernel de Linux dónde buscar el firmware del controlador?


Estoy compilando un kernel personalizado bajo Ubuntu y me estoy topando con el problema de que mi kernel no parece saber dónde buscar firmware. Bajo Ubuntu 8.04, el firmware está vinculado a la versión del kernel de la misma manera que los módulos de controladores. Por ejemplo, kernel 2.6.24-24-generic almacena sus módulos del kernel en:

/lib/modules/2.6.24-24-generic

Y su firmware en:

/lib/firmware/2.6.24-24-generic

Cuando compilo el kernel Ubuntu 2.6.24-24-genérico según el " Método Alternativo de Compilación: El Antiguo Debian Way" Obtengo el directorio de módulos apropiado y todos mis dispositivos funcionan excepto aquellos que requieren firmware, como mi tarjeta inalámbrica Intel (módulo ipw2200).

El registro del kernel muestra, por ejemplo, que cuando ipw2200 intenta cargar el firmware, el subsistema del kernel que controla la carga del firmware no puede localizarlo:

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

Errno-base.h define esto como:

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

(La función que devuelve ENOENT pone un menos delante de ella.)

Intenté crear un enlace simbólico en/lib / firmware donde el nombre de mi kernel apuntaba al directorio 2.6.24-24-generic, sin embargo esto resultó en el mismo error. Este firmware no es GPL, proporcionado por Intel y empaquetado por Ubuntu. No creo que tenga ningún vínculo real con una versión particular del kernel. cmp muestra que las versiones en los distintos directorios son idénticas.

Entonces, ¿cómo sabe el núcleo dónde buscar firmware?

Update

Encontré esta solución a la exacta problema que estoy teniendo, sin embargo, ya no funciona como Ubuntu ha eliminado /etc/hotplug.d y ya no almacena su firmware en /usr/lib/hotplug/firmware.

Update2

Algunas investigaciones más dieron lugar a algunas respuestas más. Hasta la versión 92 de udev, el programa firmware_helper fue la forma en que se cargó el firmware. A partir de udev 93 este programa fue reemplazado por un script llamado firmware.sh proporcionando idéntica funcionalidad por lo que puedo decir. Ambos codifican la ruta del firmware a /lib/firmware. Ubuntu todavía parece estar usando el binario /lib/udev/firmware_helper.

El nombre del archivo de firmware se pasa a firmware_helper en la variable de entorno $FIRMWARE que está concatenada a la ruta /lib/firmware y se utiliza para cargar el firmware.

La solicitud real para cargar el firmware es hecha por el controlador (ipw2200 en mi caso) a través de la llamada al sistema:

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

Ahora en algún lugar entre el controlador que llama a request_firmware y firmware_helper mirando la variable de entorno $FIRMWARE, el nombre del paquete del núcleo es se antepone al nombre del firmware.

Entonces, ¿quién lo está haciendo?

Author: wallyk, 2009-06-04

4 answers

Desde la perspectiva del núcleo, véase /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.

El núcleo no carga ningún firmware en absoluto. Simplemente informa al espacio de usuario, " Quiero un firmware con el nombre de xxx ", y espera a que el espacio de usuario canalice la imagen del firmware al núcleo.

Ahora, en Ubuntu 8.04,

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

Así que como has descubierto, udev está configurado para ejecutarse firmware_helper cuando el núcleo pide 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

Si lee la fuente, encontrará que Ubuntu escribió un firmware_helper que está codificado para buscar primero /lib/modules/$(uname -r)/$FIRMWARE, luego /lib/modules/$FIRMWARE, y ninguna otra ubicación. Traduciéndolo a sh, hace aproximadamente esto:

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

Que es exactamente el formato que el núcleo espera.


Para hacer una larga historia corta: El paquete udev de Ubuntu tiene personalizaciones que siempre se ven primero en /lib/firmware/$(uname -r). Esta política se está manejando en el espacio de usuario.

 40
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-04 22:15:23

Wow esta información es muy útil y me llevó a la solución para mi problema al hacer un módulo USB kernel personalizado para un dispositivo que requiere firmware.

Básicamente, cada Ubuntu trae un nuevo refrito de hal,sysfs,devfs,udev,y así sucesivamente...y las cosas simplemente cambian. De hecho leí que dejaron de usar hal.

Así que hagamos ingeniería inversa una vez más para que sea pertinente a los últimos sistemas [Ubuntu].

En Ubuntu Lucid (el último en el momento de escribir esto), se usa /lib/udev/rules.d/50-firmware.rules. Este archivo llama al binario /lib/udev/firmware, donde ocurre la magia.

Listado: /lib/udev/rules.d / 50-firmware.reglas

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

La magia debe ser algo en estas líneas (fuente: Linux Device Drivers, 3rd Ed., Ch. 14: El Modelo de Dispositivo Linux):

  • eco 1 a loading
  • copia el firmware a data
  • en caso de fallo, echo -1 a loading y detener el proceso de carga del firmware
  • echo 0 a loading (señal del núcleo)
  • entonces, un módulo específico del núcleo recibe los datos y los envía al dispositivo

Si nos fijamos en la página fuente de Lucid para udev, en udev-151/extras/firmware/firmware.c, la fuente para ese binario de firmware /lib/udev/firmware, eso es exactamente lo que sucede.

Extracto: Fuente lúcida, 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");

Además, muchos dispositivos usan un formato Intel HEX (archivos textish que contienen checksum y otras cosas) (wiki it no tengo reputación ni capacidad de enlace). El programa del núcleo ihex2fw (llamado desde Makefile en kernel_source/lib / firmware on .Archivos hexadecimales) convierte estos archivos hexadecimales a un formato binario arbitrario diseñado que el kernel de Linux luego recoge con request_ihex_firmware, porque pensaron que leer archivos de texto en el kernel era tonto (ralentizaría las cosas).

 11
Author: Andy Matteson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-10 07:04:55

En los sistemas Linux actuales, esto se maneja a través de udev y firmware.agent.

 1
Author: David Schmitt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-04 12:12:18

Linux 3.5.7 Gentoo, tengo el mismo problema. RESUELTO:

emerge ipw2200-firmware

Luego vaya a/usr/src / linux

make menucofig

En el controlador de dispositivo, elimine todos los controladores wirless que no necesite, configure Intell 2200 como módulo y recompile.

make
make modules_install
cp arch/x86/boot/bzImage /boot/kernel-yourdefault
 1
Author: MaxV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-26 10:54:56