对PXE 来说:
vmlinuz:就是引导内核文件
initr.img:就是驱动文件
如果遇到机器网卡不被PXE支持怎么办?
解决思路
如果熟悉 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。脚本可以在下载到。
下面我们以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 来的中规中矩。有兴趣的读者可以和我交流,这里就不赘述了。
参考资料
Jeremy Mates的initrd-utils, ,帮助我们创建/解开 initrd.img
Adding driver modules to a Fedora Core 2 CD for kickstart install:
实际制作案例:
网络安装 RedHat 4.8 支持82578DM网卡芯片
一 编译网卡驱动
使用2.6.9-89.EL 内核进入系统,
下载INTEL e1000e-1.1.2源码安装包 (或者更高版本)
yum groupinstall “Development Tools” -y
yum install kernel-devel -y
tar vxf e1000e-1.1.2.tar.gz
cd e1000e-1.1.2/src
make install
安装结束之后会新建一个/lib/modules/2.6.18-53.el5/kernel/drivers/net/e1000e/e1000e.ko模块文件,这个文件做为initrd.img的e1000e.ko网卡驱动(与平时使用的2.6.9-89.ELsmp内核系统的e1000e.ko不同)。
二从光盘取到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了,从而识别网卡开始网络安装。
三 为RedHat 4.8 系统安装 网卡/显卡驱动
在ks.cfg 文件最后%port 部分中添加:
rm –rf /lib/modules/2.6.9-89ELsmp/kernel/drivers/net/e1000e ;
mkdir /lib/modules/2.6.9-89ELsmp/kernel/drivers/net/e1000e ;
cd /lib/modules/2.6.9-89ELsmp/kernel/drivers/net/e1000e ;
wget
#***为kickstart 服务器地址,e1000e.ko为2.6.9-89ELsmp内核模块系统中编译出的驱动
Cd /tmp ; wget ; rpm -ivh nvidia-260.19.12-1.x86_64.rpm