zenglOX v2.0.0 E1000系列网卡驱动, PCI驱动, PS/2控制器驱动, 以太网,ARP,IP,UDP,DHCP,ICMP协议, dhcp,arp,ipconf,ping,lspci命令行程式

该版本在开发E1000系列的网卡驱动时, 最开始是参考的ChickenOS(一个小型的hobby OS)里的驱动, 但是该驱动的Bug
比较多, 而且无法在VMware下正常运行, 因为它没有开启网卡所在的PCI的Bus Master, 也就无法使用
DMA传输, 在VMware下默认是禁用了DMA Bus Master的。

(ChickenOS的github地址：https://github.com/blanham/ChickenOS/)

因此, 该版本最终移植的是Minix3里的E1000驱动, 该驱动的初始化过程非常完整, 完全按照intel的e1000系列网卡开发手册里
的要求来做的。(Minix3官网: http://www.minix3.org/)

由于默认情况下, bochs并没安装e1000相关的插件, 因此, 使用常规的make和make iso命令后,
再运行startBochs脚本时, 会提示 plugin 'e1000' not found 的错误,
要让bochs安装e1000插件, 需要重新编译bochs, 步骤如下：

首先按照之前v0.0.1版本里的说明, 下载并解压bochs-2.6:

root@zengl:/mnt/zenglOX# tar xvf bochs-2.6.tar.gz
root@zengl:/mnt/zenglOX# mkdir bochs-build
root@zengl:/mnt/zenglOX# cd bochs-build
root@zengl:/mnt/zenglOX/bochs-build# ../bochs-2.6/configure --enable-gdb-stub --enable-pci --enable-e1000
root@zengl:/mnt/zenglOX/bochs-build# make
root@zengl:/mnt/zenglOX/bochs-build# make install

以上都是需要root权限的, 可以看到configure在配置时, 比原来多了--enable-pci和
--enable-e1000的参数, 其中--enable-pci参数表示让bochs支持pci总线, 要使用e1000系列的网卡,
就必须开启PCI的支持, 另一个--enable-e1000参数表示让bochs可以使用e1000的插件来模拟e1000系列的网卡,
目前bochs的e1000只模拟了intel 82540EM的网卡。

(有关e1000系列网卡的说明, 可以参考 http://wiki.osdev.org/Intel_8254x 该链接对应的文章。)
(有关PCI的信息可以参考：wiki.osdev.org/PCI 该链接对应的文章)

bochs重新编译安装好后, 要在模拟器里使用e1000网卡, 还必须在bochsrc.txt文件里进行配置。
默认情况下, 作者已经在当前版本的bochsrc.txt里添加了如下一行配置信息：

e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=linux, ethdev=eth0

上面一条配置表示在模拟器里开启e1000网卡, 其中ethmod=linux, ethdev=eth0这条配置表示
直接使用linux上的eth0网络接口来收发模拟器里的网络数据包。
因此, 你必须确保linux主机上有eth0接口, 可以在linux命令行下通过ifconfig -a命令来查看,
如果你的主机上的网络接口名为eth2的话, 就必须将上面的ethdev=eth0修改为
ethdev=eth2 , 否则startBochs脚本运行时就会弹出类似如下的错误提示：
eth_linux: could not get index for interface 'eth0'

最后一点需要注意的是, 必须使用root权限来运行startBochs脚本, 因为上面提到的
ethmod=linux配置表示要使用linux的内置网络模块来收发数据, 也就是要用到linux内核驱动层面的东东,
因此, 需要root权限来运行, 在Ubuntu里可以使用sudo ./startBochs命令来运行。

在http://bochs.sourceforge.net/doc/docbook/user/bochsrc.html链接里
可以看到ethmod可以使用的模块名, 在该链接的4-5表格里可以看到有
fbsd , linux , null , tap , tuntap , vde , vnet , slirp , win32几个可用的模块,
我们使用的是linux模块, 在该链接里还可以看到e1000的官方配置里使用的是
slirp模块, 不过如果你想使用该模块的话, 就必须手动再安装一个slirp程式,
作者的slackware系统不是很方便安装这个程式, 因此就采用了linux模块, 该linux模块
从bochs 1.3的版本开始就已经支持了, 而官方配置里的slirp模块则是从bochs 2.6.5版本才加入进来的。
(qemu模拟器默认集成了slirp程式, 而bochs则没有)

在使用startBochs脚本运行模拟器后, 如果一切正常的话, 在zenglOX启动时就会
检测到PCI信息以及e1000系列的网卡信息, 比如网卡的mac地址等。

在键盘初始化前, 当前版本还添加了一个PS/2控制器的初始化驱动, 可以有效的
防止出现之前版本频繁出现的按键失灵的情况, 之前的zenglOX版本容易出现按键失灵
的情况, 是因为grub引导器在将执行权交给zenglOX内核时, 让PS/2控制器处于一种比较
dirty的状态, 这种状态下，容易出现问题, 因此有必要对PS/2控制器重新进行初始化操作以及
必要的PS/2控制器, 键盘所在的PS/2端口等的自检操作。

目前版本, 只有一种情况会出现按键失灵, 那就是PS/2或键盘所在的PS/2端口自检失败时, 当自检失败时,
PS/2控制器的初始化程式会显示一条"PS/2 Controller self test failed ! , you can't use keyboard and mouse..."的
信息或者是"first PS/2 port test failed, you can't use keyboard"的信息给用户,
这种情况下, 就只有重新启动zenglOX了。

(有关PS/2控制器的信息, 可以参考 http://wiki.osdev.org/%228042%22_PS/2_Controller 该链接
, 该链接的"Initialising the PS/2 Controller"节里介绍了PS/2的完整的初始化步骤)

当前版本, 新增了5个工具: arp dhcp ipconf ping lspci
可以先使用dhcp来动态获取IP地址, 当然前提是你的网络环境下有网关提供DHCP服务。
如果没有DHCP服务, 就只能使用ipconf工具来手动设置IP地址等信息。
下面看下这些工具的基本使用方法：

>>>> arp 工具

zenglOX> arp 10.0.2.2
send arp request and wait for reply
the dest ip [10.0.2.2] mac is [0x52:0x54:0x0:0x12:0x35:0x2]

zenglOX>

使用arp工具可以检测你的模拟器所能访问的局域网内的某台主机的mac地址
如果目标主机不存在, 则会有3000个tick的超时时间, 在bochs下tick过的非常快
尽管一个tick的理论时间是20ms, 那么3000个tick就应该是60000ms即60s, 但是
在bochs里, 一个tick完全不按实际来, 可以快达2ms(估计的), 因此也就5到6秒钟的
超时时间, 如果将生成的zenglOX.iso镜像放到VirtualBox或VMware下的话, 那就
确实是60秒的超时时间, 如果你不想在VirtualBox或VMware下等那么久的话,
可以手动按 ESC 键来退出arp程式, 其他的dhcp , ping工具都可以使用ESC键来退出
程式。(下面会提到如何在VirtualBox或VMware里设置使用e1000网卡)

之所以设置为3000个tick的等待超时, 完全是为了照顾Bochs!

arp工具使用的是ARP协议,和ARP协议相关的内容可以参考：
http://en.wikipedia.org/wiki/Address_Resolution_Protocol 该链接对应的文章
以太网的帧结构可以参考:
http://en.wikipedia.org/wiki/Ethernet_frame#Header
在程序里也就用到了以太网帧结构里的Header部分(目标mac, 源mac, 及EtherType以太网类型字段)

>>>> dhcp 工具

zenglOX> dhcp
send dhcp discover and wait for reply
received dhcp offer reply
send dhcp request and wait for reply
received dhcp ack , config is follow:
ip addr: 10.0.2.16
net mask: 255.255.255.0
route gateway: 10.0.2.2
zenglOX>

dhcp工具会使用DHCP协议来从网关那里获取到IP地址, 子网掩码, 网关地址信息, 这里暂时
没有包含dns信息, 因为作者还没添加dns相关的协议。

同样的, 如果你的网络环境下没有主机提供DHCP服务的话, 就会有3000个tick的超时时间。
可以按ESC键来退出超时等待过程。

(DHCP协议相关的内容可以参考：
http://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
该链接对应的文章)

DHCP协议还涉及到IP协议, UDP协议：
和IP协议相关的内容可以参考：http://en.wikipedia.org/wiki/IPv4 该链接对应的文章。
和UDP协议相关的内容可以参考：http://en.wikipedia.org/wiki/User_Datagram_Protocol 该链接对应的文章
以太网的帧结构可以参考: http://en.wikipedia.org/wiki/Ethernet_frame#Header
在程序里也就用到了以太网帧结构里的Header部分(目标mac, 源mac, 及EtherType以太网类型字段)

>>>> ipconf 工具

zenglOX> ipconf -a
mac address: 0x52:0x54:0x0:0x12:0x34:0x56
ip addr: 10.0.2.16
net mask: 255.255.255.0
route gateway: 10.0.2.2
zenglOX>

使用ipconf -a命令可以查看到当前配置的ip地址等信息
如果想手动配置ip地址的话, 可以使用ipconf -set命令：

zenglOX> ipconf -set 10.0.2.35 255.255.255.0 10.0.2.2
network config success! you can use 'ipconf -a' to see your network configure
zenglOX>

如果你是在路由器的环境下, 那么手动配置时需要注意ip地址的网络段, 子网掩码, 和网关信息,
配置的IP地址必须和路由器的ip在同一个网络段里, 否则就无法正常访问路由器(路由器会拒绝接受非同一网段的数据包)。

>>>> ping 工具

zenglOX> ping
usage: ping num timeout_sec destip
zenglOX> ping 4 60 10.0.2.2
send arp request to gateway and wait for reply
the gateway ip [10.0.2.2] mac is [0x52:0x54:0x0:0x12:0x35:0x2]
sending ... Reply from [10.0.2.2] bytes=8 ticks=0 TTL=63
sending ... Reply from [10.0.2.2] bytes=8 ticks=0 TTL=63
sending ... Reply from [10.0.2.2] bytes=8 ticks=0 TTL=63
sending ... Reply from [10.0.2.2] bytes=8 ticks=0 TTL=63

zenglOX>

ping工具是使用ICMP协议来检测目标主机的。
ping工具的第一个参数表示ping多少次, 第二个参数表示每个ping的超时时间,
由于bochs下tick过得很快, 上面的60秒超时在bochs里也就是5到6秒的样子。
如果在VirtualBox或VMware下, 则建议设置为5到10秒的超时, 因为VirtualBox或VMware下
是按照实际的时间来执行的。
可以按ESC键来退出超时等待过程。

有关ICMP协议的相关说明请参考:
http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol 该链接对应的文章

另外在虚拟机的网络配置为NAT的情况下, 比如VirtualBox与VMware默认就是用的Nat方式
, 这种方式下, ping包很容易超时, 我的某些在VirtualBox里安装的linux系统, 在Nat方式下
ping的丢失率高达80%以上。而且Nat方式也会偷偷摸摸的神不知鬼不觉的过滤掉一些ICMP数据包。
因此, 如果你喜欢ping外网的话, 建议用Bridge桥接模式。

>>>> lspci 工具

zenglOX> lspci
PCI dev list:
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x1237
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x7000
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x7010
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x7113
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x100e
pci device total count: 5

lspci工具可以将PCI总线上的设备信息显示出来, vend id为厂商id
8086表示是intel的设备, dev id为设备ID, 如上面的0x100e就是intel 82540EM网卡
的设备ID, 可以从intel的e1000开发手册上看到网卡的ID信息。

lspci还可以接一个显示数目的参数:

zenglOX> lspci 3
PCI dev list:
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x1237
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x7000
bus [0] dev [0] func [0] vend id: 0x8086 dev id: 0x7010
pci device total count: 5

上面lspci接了一个参数3, 表示只显示前3个PCI设备信息

以上就是各工具的使用方法。

下面看下如何在VirtualBox与VMware下使用e1000网卡:

>>>> VirtualBox设置e1000网卡：

在VirtualBox界面, 对zenglOX虚拟机通过右键选择设置, 进入设置界面,
在设置界面, 选择网络, 在网络界面点击高级来展开一个界面,
在展开的高级界面里将控制芯片设置为 Intel PRO/1000 MT 桌面 (82540EM) 或者
Intel PRO/1000 T 服务器 (82543GC) 或者
Intel PRO/1000 MT 服务器 (82545EM) , 这三个都是e1000系列的网卡。

在网络界面的"网卡 1"的"连接方式"里可以选择"网络地址转换(NAT)"方式或者
"桥接网卡"的方式, 在桥接网卡下的界面名称里, 可以选择需要桥接的实际的物理网卡名字。

桥接方式下, 就会使用实际的局域网中的路由器来获取ip地址, 和进行ICMP的包转发

>>>> VMware设置e1000网卡：

VMware设置E1000网卡没有图形界面, 需要修改配置文件。
进入zenglOX虚拟机所在的目录, 找到vmx后缀的配置文件,
作者的配置文件名为: zenglOX.vmx , 打开该文件, 在该文件里
添加一行如下的配置:
ethernet0.virtualDev ="e1000"
保存配置文件。

这样再在VMware里启动zenglOX时, 就使用的是e1000的网卡了, VMware模拟的是
e1000系列中的Intel 82545EM

默认情况下VMware使用的是NAT方式,
如果你想使用桥接方式, 可以进入虚拟机的设置界面, 将Network Adapter
设置为Bridged桥接方式。

这里需要注意的是VMware的桥接模式对某些无线网卡支持不好, 尽量使用有线网卡。
例如作者的笔记本在使用无线网卡时, 桥接无法联系到局域网, 在切换到有线网卡后,
桥接就可以正常工作了。

另外, 在设置桥接方式前, 还需要在Edit菜单里选择Virtual Network Editor ,
在出现的虚拟网络接口编辑器界面, 将Type为Bridged的接口(一般为VMnet0)
设置到实际的物理网卡。

作者：zenglong
时间：2014年8月24日
官网：www.zengl.com