Como o Linux Kernel sabe onde procurar firmware do motorista?
-
09-09-2019 - |
Pergunta
Estou compilando um kernel personalizado no Ubuntu e estou enfrentando o problema que meu kernel parece não saber onde procurar firmware. No Ubuntu 8.04, o firmware está vinculado à versão do kernel da mesma maneira que os módulos de driver. Por exemplo, o kernel 2.6.24-24-genérico armazena seus módulos de kernel em:
/lib/modules/2.6.24-24-generic
e seu firmware em:
/lib/firmware/2.6.24-24-generic
Quando compilar o kernel do Ubuntu 2.6.24-24-genérico de acordo com o "Método de construção alternativa: a maneira antiga do Debian"Eu recebo o diretório de módulos apropriado e todos os meus dispositivos funcionam, exceto aqueles que exigem firmware como meu cartão sem fio Intel (módulo IPW2200).
O log do kernel mostra, por exemplo, que quando o IPW2200 tenta carregar o firmware, o subsistema do kernel que controla o carregamento do firmware não consegue localizá -lo:
ipw2200: Detected Intel PRO/Wireless 2200BG Network Connection
ipw2200: ipw2200-bss.fw request_firmware failed: Reason -2
errno-Base.h define isso como:
#define ENOENT 2 /* No such file or directory */
(A função que retorna a Enoent coloca um menos na frente dela.)
Tentei criar um link simbólico em /lib /firmware, onde o nome do meu kernel apontou para o diretório genérico 2.6.24-24, no entanto, isso resultou no mesmo erro. Este firmware não é GPL, fornecido pela Intel e embalado pelo Ubuntu. Não acredito que tenha algum vínculo real com uma versão específica do kernel. cmp
mostra que as versões nos vários diretórios são idênticas.
Então, como o kernel sabe onde procurar firmware?
Atualizar
eu encontrei esta solução Para o problema exato que estou tendo, no entanto, não funciona mais, pois o Ubuntu eliminou /etc/hotplug.d
e não armazena mais seu firmware em /usr/lib/hotplug/firmware
.
Atualização2
Algumas pesquisas revelaram mais algumas respostas. Até a versão 92 de udev
, o programa firmware_helper
Foi a maneira como o firmware foi carregado. Começando com udev
93 Este programa foi substituído por um script chamado Firmware.sh, fornecendo funcionalidade idêntica, tanto quanto eu sei. Ambos os codificam o caminho do firmware para /lib/firmware
. Ubuntu ainda parece estar usando o /lib/udev/firmware_helper
binário.
O nome do arquivo de firmware é passado para firmware_helper
na variável do ambiente $FIRMWARE
que é concatenado para o caminho /lib/firmware
e usado para carregar o firmware.
A solicitação real para carregar o firmware é feita pelo driver (IPW2200 no meu caso) através da chamada do sistema:
request_firmware(..., "ipw2200-bss.fw", ...);
Agora em algum lugar entre o motorista chamando request_firmware
e firmware_helper
Olhando para o $FIRMWARE
Variável de ambiente, o nome do pacote do kernel está sendo preso ao nome do firmware.
Então, quem está fazendo isso?
Solução
Da perspectiva do kernel, veja /usr/src/linux/documentação/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.
O kernel não carrega nenhum firmware. Simplesmente informa o espaço do usuário: "Eu quero um firmware pelo nome de xxx", e aguarda o espaço para os usuários colocar a imagem do firmware de volta ao kernel.
Agora, no Ubuntu 8.04,
$ grep firmware /etc/udev/rules.d/80-program.rules # Load firmware on demand SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"
Então, como você descobriu, udev
está configurado para correr firmware_helper
Quando o kernel pede 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
Se você ler a fonte, você descobrirá que o Ubuntu escreveu um firmware_helper
que é codificado para primeiro procurar /lib/modules/$(uname -r)/$FIRMWARE
, então /lib/modules/$FIRMWARE
, e nenhum outro local. Traduzindo para sh
, faz aproximadamente isso:
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 é exatamente o formato que o kernel espera.
Para encurtar uma longa história: Ubuntu's udev
o pacote tem personalizações que sempre olham /lib/firmware/$(uname -r)
primeiro. Esta política está sendo tratada no espaço do usuário.
Outras dicas
Uau, isso é uma informação muito útil e me levou à solução para o meu problema ao fazer um módulo de kernel USB personalizado para um dispositivo que requer firmware.
Basicamente, todo Ubuntu traz uma nova rehash de hal, sysfs, devfs, udev e assim por diante ... e as coisas simplesmente mudam. Na verdade, eu li que eles pararam de usar o HAL.
Então, vamos engenharia reversa mais uma vez, para que seja pertinente aos mais recentes sistemas [Ubuntu].
No Ubuntu Lucid (o mais recente no momento da redação), /lib/udev/rules.d/50-firmware.rules
é usado. Este arquivo chama o binário /lib/udev/firmware
, onde a magia acontece.
Listagem: /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}"
A magia deve ser algo nesse sentido (fonte: Drivers de dispositivo Linux, 3ª ed., Ch. 14: O modelo de dispositivo Linux):
- eco 1 para
loading
- Copie o firmware para
data
- no fracasso, eco -1 para
loading
e interromper o processo de carregamento de firmware - eco 0 para
loading
(sinalize o kernel) - Em seguida, um módulo de kernel específico recebe os dados e o empurra para o dispositivo
Se você olhar para a página de origem de Lucid para Udev, em udev-151/extras/firmware/firmware.c
, a fonte para esse binário de firmware/lib/udev/firmware, é exatamente o que acontece.
Trecho: 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");
Além disso, muitos dispositivos usam um formato Intel Hex (arquivos textish contendo soma de verificação e outras coisas) (Wiki It Não tenho reputação e nenhuma capacidade de vincular). O Programa Kernel IHEX2FW (chamado From Makefile em Kernel_source/lib/firmware em arquivos .hex) converte esses arquivos hexadecimais em um formato binário projetado por arbitrário que o kernel Linux então pega com com request_ihex_firmware
, porque eles pensavam que ler arquivos de texto no kernel era bobo (diminuiria as coisas).
On current Linux systems, this is handled via udev
and the firmware.agent
.
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