Showing posts with label link script. Show all posts
Showing posts with label link script. Show all posts

Wednesday, June 24, 2009

一千零一夜之 Kernel and Firmware

這個系列是讀書筆記,作者可能沒有跟主題有關的開發經驗。

Driver 需要 firmware 時會呼叫 request_firmware,這個函數會在 /sys 底下產生適當的裝置讓 userspace 上傳 firmware 給 driver。它會等到 firmware 上傳完成 (或發生錯誤) 才回傳。除了建立適當的裝置,它也會送一個 uevent,內容類似

KERNEL[1245809267.000297] add      /devices/pci0000:00/0000:00:1c.2/0000:02:00.0/firmware/0000:02:00.0 (firmware)
UDEV_LOG=3
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1c.2/0000:02:00.0/firmware/0000:02:00.0
SUBSYSTEM=firmware
FIRMWARE=iwlwifi-3945-2.ucode
SEQNUM=1226


uevent 事件通常是 udev 在處理,在遇到 firmware 事件,也就是 SUBSYSTEM 是 firmware 的事件時,udev 預設會執行 /lib/udev/firmware.agent (在 Debian 上) 來上傳 firmware。簡化過的 firmware.agent 其實只做了

echo 1 > /sys/$DEVPATH/loading # 準備上傳
cat $firmwaredir/$FIRMWARE > /sys/$DEVPATH/data
echo 0 > /sys/$DEVPATH/loading # 上傳完畢


注意到上面大寫的變數內容都是 uevent 提供,像是 $DEVPATH 就是 kernel 剛產生用來接受 firmware 上傳的虛擬裝置。

這是 firmware 擺在 filesystem 上的情形。當 CONFIG_FIRMWARE_IN_KERNEL 打開時,如果 driver 被編進 kernel,firmware 也會一起被編進去。在 request_firmware 中有一段

for (builtin = __start_builtin_fw; builtin != __end_builtin_fw;
builtin++) {
if (strcmp(name, builtin->name))
continue;
dev_info(device, "firmware: using built-in firmware %s\n",
name);
firmware->size = builtin->size;
firmware->data = builtin->data;
return 0;
}


會先檢查 driver 要求的 firmware 是否有內建。這邊的 __start_builtin_fw 與 __end_builtin_fw 是 link time 產生的 symbol,所以不會在任何 C code 找到它們的定義。在 vmlinux.lds (或 include/asm-generic/vmlinux.lds.h) 可以看到,它們的值會是所有 .builtin_fw section 內容的開始位址與結束位址。

.builtin_fw 的內容要再找到 firmware/Makefile。在那份 Makefile 可以看到 kbuild 會為每一個要內建的 firmware 產生一份 assembly code。這些 assembly code 編出來的 object code 會跟 kernel link 在一起,達成 firmware 內建的目的。從 assembly code 可以看出,這些 object code 的內容會是 firmware 本身,以及一個放在 .builtin_fw section 的資料結構,給出 firmware 的檔名、位置、還有大小。

這邊看到內建 firmware 的做法其實是 kernel 常見的手法之一。寫 driver 最開始會加的 module_init(entry_point) 是另一個例子。這些 macro 會產生 initcall,而所有的 initcall 在 link time 也是用類似的方式被收集。在電腦剛開機進 userspace 前它們會依照被收集的順序依序被執行。這邊可以看到一個要注意的小地方。在每個 subsystem (rtc, scsi, ...) 的 Makefile 中,核心的部份必須放在 driver 之前。