Hacking initrd.img - 添加網卡驅動,網絡安裝 Linux

 
本文通過將網卡驅動加入到 initrd.img 中,使 Linux 內核在啓動的過程中能正確識別並加載網卡驅動,從而使網絡安裝得以進行。

前言

網 絡安裝 Linux 並不是一個新鮮的話題,其過程也不是一個輕鬆的體驗。爲了讓機器能通過網絡來安裝 Linux,如果還需要配合 kickstart 來自動化 Linux 的安裝過程的話,用戶需要做大量的配置工作。衆所周知,用戶需要挑選一臺機器作爲服務器,然後在這臺機器上配置 DHCP, TFTP, NFS/Http/Ftp, pxelinux, kickstart 等一系列的東西。

但是所有的這一切能成功運作都至少有一個前提條件:我們所安裝的 Linux 能正確的識別並驅動所有客戶機的網卡。如果網卡驅動不了,客戶機根本無法通過網絡從服務器取到所需要的東西,網絡安裝 Linux 就無從談起了。

本 文通過將網卡驅動加入到 initrd.img 中,使 Linux 內核在啓動的過程中能正確識別並加載網卡驅動,從而使網絡安裝得以進行。本文並不講述網絡安裝 Linux 的背景知識(如爲什麼需要網絡安裝,網絡安裝的好處等)、具體配置和操作步驟(也就是配置 DHCP,TFTP,pxelinux 等內容)。此外,本文需要讀者有熟練的 Linux 操作經驗和 Shell 編程的基本知識,而且對 Linux 啓動過程和驅動程序有基本的瞭解。

注:所有被安裝的機器我們稱之爲客戶機,提供網絡安裝服務的機器我們稱之爲服務器

 




回頁首

 

開始之前的建議

建議:如果您碰到了前言中所描述的問題的話,最好的解決方法是 – 找一個能驅動客戶機網卡的 Linux Distribution,這樣能省卻很多麻煩。

但在現實環境下,很多原因會導致我們無法選擇安裝一個新的 Linux 發行版。原因有很多,比如:

  • 客戶不同意我們選用其他的 Linux 版本,因爲客戶有大量的應用程序已經在某個Linux 版本上編譯,運行良好了,更換 Linux 發行版會帶來應用的移植問題
  • 客戶擁有一些特殊的硬件,而這些硬件只有基於某個 Linux 發行版的驅動。更換 Linux 發行版,會導致這些硬件無法正常工作
  • 沒有一個 Linux 發行版能驅動客戶機的網卡。網卡廠商只給我們提供了某個 Linux 發行版上的驅動,一切都要 DIY
  • 您有着強烈的DIY情感,面對問題不是尋求別人的解決方案而是一切都要自己克服 – 毫無疑問,您就是本文最適合的讀者

 




回頁首

 

解決思路

如 果熟悉 Linux 的啓動過程和驅動程序,那麼要解決本文的問題,基本上有兩條路可走。第一就是將網卡驅動編譯進內核(靜態鏈接進內核),第二種方法就是將網卡驅動做成模 塊,然後想辦法在 Linux 啓動的時候讓 Linux 內核能找到並掛載該驅動。面對這兩種方案,第二種方法有更好的可行性和擴展性。因爲首先有些網卡驅動本身就不能被靜態鏈接進入內核,而只能被編譯成一個模 塊,例如下文要舉的例子 - e1000 網卡驅動;其次,驅動做成模塊的方式,可以適應多個內核版本,用方法 1,更換一個內核版本就要重新編譯一次內核;最後,等會會看到,相比編譯內核,方法 2 更簡單和可操作。

方法 2 的實現手段就是定製 initrd.img,將我們的網卡驅動加進去。initrd.img 是一個小型的根文件系統,在 Linux 內核沒有掛載硬盤上的根分區的時候,initrd.img 將在內存中展開。一般情況下,initrd.img 中將包含一些必需的命令和驅動,如 insmod 命令和磁盤驅動。有了 insmod,才能將磁盤驅動掛載進內核,有了磁盤驅動,內核才能掛載位於磁盤上的根文件系統。

大 部分的 Linux 發行版都提供了用於網絡安裝 Linux 的 initrd.img,一般位於第一張安裝光盤的 images/pxeboot 目錄下。在一臺已經裝好 Linux 的機器中,在 /boot 目錄下我們也能找到 initrd.img,比較一下這兩個 initrd.img,會發現 pxeboot 目錄下的 initrd.img 會比 /boot 下的大很多,這是因爲在網絡安裝的情況下,Linux 不會嘗試去掛載位於磁盤上的根分區(事實上,在沒有安裝Linux的機器上,此時磁盤中可能什麼數據都沒有),所以此時的 initrd.img 需要包含大量的驅動,使 Linux 能識別大量的硬件。位於 /boot 下的 initrd.img,基本上唯一需要的東西就是磁盤驅動,只要內核能訪問磁盤,那麼其餘所需的東西都可以從磁盤取得而不需要依賴 initrd.img。

 




回頁首

 

具體操作和實例

從安裝光盤中取得 initrd.img 之後,就可以開始對其進行定製。這裏要感謝 Jeremy Mates,他寫的 initrd-util.sh 能很好的解開和生成一個 initrd.img。腳本可以在http://sial.org/howto/linux/initrd/initrd-util 下載到。

下 面我們以RedHat Enterprise Linux Advance Server 4 Update 2 x86_64,Intel e1000網卡驅動爲例,講述具體的操作過程(在本例中,服務器和客戶機擁有相同的Intel e1000網卡,而且我們已經手動在服務器上安裝完成了正確的e1000驅動):

首先從光盤取到initrd.img,登錄到服務器,然後用initrd-util.sh解開:


命令輸出 1. 解開initrd.img

                
[root@ericvm ~]# cd `./initrd-util.sh unpack initrd.img |tail -1`
info: initrd unpack expanded into: /var/tmp/initrd-util.workdir.DA29317
[root@ericvm initrd-util.workdir.DA29317]# pwd
/var/tmp/initrd-util.workdir.DA29317
[root@ericvm initrd-util.workdir.DA29317]# ls
2.6.9-22.EL bin dev etc linuxrc lost+found modules
proc sbin selinux sys tmp var

 

initrd- util.sh很簡單,利用gunzip, mount和cpio這些工具將initrd.img解開,其中驅動包位於modules目錄下,名爲modules.cgz,將這個文件解開後,生成了 2.6.9-22.EL目錄,進入該目錄,就能找到包含在initrd.img中的驅動。本例中,RedHat已經包含了一個e1000的驅動,但是這個 驅動不能驅動我們新的Intel e1000網卡。爲此,我們在e1000網站下載新版的驅動,然後在服務器上編譯完成,生成ko模塊文件,然後拷貝到2.6.9-22.EL目錄下,覆蓋 原文件即可。

驅動更新完畢後,現在我們需要將2.6.9-22.EL這個目錄重新製作成modules.cgz,這個功能initrd-util.sh不能爲我們完成,所以我們手動操作:


命令輸出 2. 加入驅動並重新打包

                
[root@ericvm initrd-util.workdir.DA29317]# find 2.6.9-22.EL | cpio -o -H crc > newmodules
16582 blocks
[root@ericvm initrd-util.workdir.DA29317]# gzip -n -9 newmodules
[root@ericvm initrd-util.workdir.DA29317]# mv newmodules.gz modules
[root@ericvm initrd-util.workdir.DA29317]# cd modules
[root@ericvm modules]# rm -f modules.cgz
[root@ericvm modules]# mv newmodules.gz modules.cgz
[root@ericvm modules]# pwd
/var/tmp/initrd-util.workdir.DA29317/modules

 

驅動包重新生成了並不意味着Linux就可以識別網卡了,因爲Linux必須依靠一種邏輯,將硬件設備和驅動模塊文件對應起來。這個邏輯就被定義在modules目錄下的除modules.cgz之外的文件中:


命令輸出 3. 設備驅動識別信息文件

                
[root@ericvm modules]# ls
module-info modules.cgz modules.dep modules.pcimap modules.usbmap pci.ids pcitable

 

如 上所示,pcitable, modules.pcimap中定義了PCI設備和驅動模塊之間的對應關係,modules.dep中定義了模塊和模塊之間的依賴關係(比如,各種 SCSI設備都會依賴一個基礎的SCSI驅動模塊),module-info中定義了驅動的靜態描述信息......

要填寫這些文本文件,也很簡單,首先我們必須要知道這塊e1000網卡的PCI設備信息,由於在服務器上e1000這塊網卡已經安裝完成了,所以我們可以在服務器上取到我們想要的信息:


命令輸出 4. 查看網卡硬件信息

                
[root@ericvm ~]# lspci
............ ignore some outputs
04:00.0 Ethernet controller: Intel Corporation Enterprise Southbridge DPT LAN Copper
04:00.1 Ethernet controller: Intel Corporation Enterprise Southbridge DPT LAN Copper
............ ignore some outputs

 

lspci列出了服務器上兩塊網卡的設備信息,根據網卡設備的ID號碼(04:00.0, 04:00.1),我們就可以在lspci –n的輸出中找到設備的vendor code和device code(請參考lspci的manual瞭解lspci):


命令輸出 5. 查看網卡code

                
[root@ericvm ~]# lspci –n
............ ignore some outputs
04:00.0 Class 0200: 8086:1096 (rev 01)
04:00.1 Class 0200: 8086:1096 (rev 01)
............ ignore some outputs

 

在lspci –n的輸出中,我們找到了兩塊網卡的vendor code和device code – 8086和1096。得到了vendor code和device code之後,就可以更新initrd.img中modules目錄下的pcitable, modules.pcimap等這些文件了。舉例來說,在pcitable中查找e1000,能發現很多設備和e1000這個驅動關聯,但是唯獨沒有 8086:1096的組合,這就是爲什麼Linux無法驅動這塊e1000網卡的原因了,我們需要手動將8086, 1096這兩個code加入到pcitable中,並將這個設備對應到e1000驅動上。照此方法,更新其餘的文件,如module-info, modules.pcimap等。

這樣我們就完成了對initrd.img的完全修改,用initrd-util.sh重新將目錄打包,生成一個新的initrd.img:


命令輸出 6. 重新生成initrd.img

                
[root@ericvm ~]# ./initrd-util.sh pack /var/tmp/initrd-util.workdir.DA29317/
notice: new initrd size: 6144K
6144+0 records in
6144+0 records out
mke2fs 1.35 (28-Feb-2004)
info: initrd packed into: /var/tmp/initrd-util.initrd-new.IV29439.gz
/var/tmp/initrd-util.initrd-new.IV29439.gz
[root@ericvm ~]# ls -lh /var/tmp
total 3.7M
-rw-r--r-- 1 root root 3.7M Jun 20 17:10 initrd-util.initrd-new.IV29439.gz
drwxr-xr-x 12 root root 4.0K Jun 20 17:10 initrd-util.workdir.DA29317
drwxr-xr-x 13 root root 4.0K Jun 20 15:53 initrd-util.workdir.ID29288

 

initrd- util.sh首先創建一個“空洞文件”,然後在這個文件中建立ext2 文件系統,然後將這個文件mount到一個目錄中,最後用rsync這種方式將我們更新過的文件“拷貝”到了mount的目錄下,這樣“空洞”文件中就有 了內容,最後對文件進行壓縮,生成最終的img文件。

將/var/tmp/initrd-util.initrd-new.IV29439.gz改名成initrd.img,放到tftp配置的目錄下,就可以讓客戶機在網絡啓動的時候取到新的initrd.img了,從而識別網卡開始網絡安裝。

 




回頁首

 

到此爲止了麼?

到 目前爲止,一切看起來都很好。客戶機通過網絡啓動,能把網卡驅動起來並從服務器上得到所有需要的東西,並開始安裝。但是,如果沒有做特殊處理的話,客戶機 上Linux安裝完成後,啓動進入Linux,會發現網卡依舊驅動不了,典型的出錯信息就是“無法成功掛載XXX驅動”,“ethX的MAC地址和預計的 不一樣”等。出現這樣問題的原因很簡單,這是因爲正確的網卡驅動只存在於服務器上的 initrd.img 中,而沒有體現到客戶機的硬盤上。客戶機在網絡啓動的時候得到了服務器上的 initrd.img,但 Linux 還沒有智能到能自動解開這個 initrd.img 並將裏面的驅動拷貝到客戶機的硬盤上。一旦客戶機完成安裝重啓,從硬盤啓動之後,所有的驅動文件和信息就都從硬盤讀取了。

還舉剛纔的例子,e1000 網卡驅動在 RedHat 中其實自帶就有一個,但不適用於我們的 Intel e1000 網卡,用 rpm 命令可以查到安裝在硬盤上的這個 e1000 驅動屬於哪個RPM包:


命令輸出 7. 查看驅動所在的 RPM 包

                
# rpm -qf /lib/modules/2.6.9-42.ELsmp/kernel/drivers/net/e1000/e1000.ko
kernel-smp-2.6.9-42.EL

 

所以,很明顯的就是,要解決這樣的問題,我們需要重新生成這個 kernel RPM 包。但是要在 RPM 包中替換一個文件,或是加入一個文件,可不像在 RAR 文件中用鼠標直接拖拽那麼簡單。有興趣的可以參考 RPM 的相關資料。

除了重新生成 RPM 之外,還有一些簡單的辦法也是可行的,但不如重新生成 RPM 來的中規中矩。有興趣的讀者可以和我交流,這裏就不贅述了。

 

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章