从PC总线的发展看设备驱动程序的发展

PC总线的发展看设备驱动程序的发展

 

20100603修改版)

 

0 前言

 

让我们把时光倒流13年。

 

1997年,随着所谓“第一种能破坏硬件的Windows 95病毒”——CIH病毒的出现,之前很少有人感兴趣的Windows 95设备驱动程序技术——虚拟设备驱动程序(VxD)——这种被CIH病毒“歪用”来“破坏硬件”的技术,似乎一夜之间成为了“热点”。

 

笔者当时正对加密解密、反病毒和内核编程有着浓厚兴趣,于是也开始研究VxD,当时只要在书店里看到关于Windows设备驱动程序的书籍,不管是VxDNT Driver还是WDM,不管对于当时的笔者有多贵,都掏钱买下来看。

 

看着看着,觉得这些书怎么有些看不太懂啊?这些书里讲的更多的是总线、即插即用、USB之类的内容,没讲多少Windows内核编程呀?失望之余,一些书就这样被打入冷宫了。

 

2004年,笔者开始转向硬件和嵌入式系统开发,一块块电路板被制作出来,使用串口、USB接口……连接到PC上,需要开发USB设备驱动程序了……

 

嵌入式系统硬件和PC硬件原理本来就是相通的,一些当年很模糊的概念,逐渐变得清晰起来,再找出当年被打入冷宫的书籍一看,恍然大悟,原来是这样啊!Windows设备驱动程序是Windows内核应用程序没错,可是设备驱动程序的基本用途是驱动外设硬件,和硬件打交道是它们的基本工作,不弄懂硬件能真正看懂吗?

 

可是,现在设备驱动程序——“驱动”的名声似乎很不好,经常可以听到这样一些声音:

 

“这个病毒使用了驱动,难于查杀……”

“这个病毒使用了驱动,Windows连安全模式都进不了……”

“这个木马使用了驱动——Rootkits,隐藏自身,逃避检测……”

“这个恶意程序使用了Rootkits,清除不干净……”

“我们的XX杀毒软件,独有XX技术,专门对付Rootkits……”

 

本来用于驱动外设硬件的“驱动”,现在和病毒、黑客、Rootkits等让普通电脑用户“闻之色变”的一些名词挂上了钩。假如设备驱动程序有自我意识的话,它一定会觉得很冤枉:我本来不是用来干这些“坏事”的呀!谁让某些人把我“歪用”的呀!我好冤哪……

 

现在,是到了还设备驱动程序本来面目的时候了!

 

笔者准备写一组名为“硬件工程师学驱动”的系列文章,结合笔者这些年内核编程以及硬件开发的经验,向需要学习设备驱动程序开发的硬件工程师全景式地展现设备驱动程序的原理和开发方法。本系列文章不打算使用一般书籍中通过编程开始学习设备驱动程序开发的讲述模式,而使用一种较新颖的讲述模式:通过自制简单的外设硬件学习设备驱动程序开发,尽快让设备驱动程序开发与实际硬件相结合,还设备驱动程序本来面目!

 

当然,本系列文章也会讲述一些Windows内核编程的基本知识,以补充一些必需的背景知识,不过如果读者是想开发病毒或者Rootkits,笔者还是劝您不要看这个系列文章了,会让您很失望的。

 

本系列文章中的设备驱动程序,未经特殊说明,均指Windows设备驱动程序。

 

1 80868088 CPU开始谈总线

 

如果读者有大学计算机专业《微机原理》或者《微机原理与接口技术》的教材,不妨将它拿出来重新阅读一下。

 

您可能觉得这样做没有什么意义,大学教材很多内容都过了时,上述教材大部分内容还是讲基于8088(或者80868088CPU,可以说是最早型号的IBM PC微机。808816CPU,只能工作于实模式,今天的PC早已使用32位甚至64位的x86 CPU,工作于保护模式,保护模式和实模式连编程方式都不一样了,还读这些教材干什么?!

 

实际上,无论x86系列CPU怎样发展,从80868088一直到今天的Core i7,基本的硬件原理没有改变,至少冯·诺依曼体系结构没有变,PC采用总线结构以及外设连接在总线上的方式没有变,而且,由于“向下兼容”的需求,x86 CPU访问外设的一些基本方式,例如独立IO地址空间,一直没有改变。了解基于8088IBM PC怎样连接和访问外设,对于了解今天的PC怎样连接和访问外设,仍然不失其意义。

 

基于8088IBM PC系统板(主板)框图如图1-1所示(图1-1和图1-2来自超星数字图书馆中李克春主编的《IBM PC系列微机接口与通讯原理及实例》一书,19906月第1版)。

 

1-1  IBM PC系统板框图

 

IBM PC主板上,IO通道又称为PC总线或者PC/XT总线,现也称为8ISA总线。总线是一组信号线的集合,是计算机系统各部件之间传输地址、数据和控制信息的公共通路。总线的特点在于其公用性,可同时挂接多个部件或设备,对于只连接两个部件或设备的信息通道,不称为总线。PC总线插槽中可以插入显示卡、XT多功能卡等,用于连接显示器、软驱、硬盘、打印机等外设,也就是说外设最终连接到PC总线上。

 

容易看出,IBM PC主板上的PC总线是直接连接到IBM PC系统总线上的,IBM PC系统总线实际上是工作在最大模式下的8088系统总线。IBM PC系统总线框图如图1-2所示。

 

1-2  IBM PC系统总线框图

 

工作在最大模式下的8088系统总线包括数据总线、地址总线和控制总线,其中控制总线有两组读写信号,分别是内存(存储器)读写信号/MEMR(信号前的正斜线表示低电平有效,下同)和/MEMW,以及外设(IO端口)读写信号/IOR/IOW,这两组不同的读写信号分别受8088MOV指令以及INOUT指令控制。

 

因此,8088访问内存使用MOV指令,当PC总线连接有外设时,如果外设带有存储器并映射到内存地址空间(例如显卡显存),也使用MOV指令访问,但访问外设使用INOUT指令,这样我们称8088有着“独立IO地址空间”。实际上,所有x86系列CPU都有独立IO地址空间,时至今日,x86 CPU访问外设,归根结底是使用MOVINOUT指令。

 

如果外设和CPU之间采用中断方式完成通信,那么外设还要能够引发CPU中断,为了管理多个硬件中断源,通常将外设中断请求信号通过可编程中断控制器(PIC)连接到CPU芯片中断引脚上。PC总线插槽中有连接到PIC——8259的引脚(图1-1中未画出),这样一来连接到PC总线上的外设即可使用中断方式与8088完成通信。时至今日,PC中的PIC仍然具有8259的功能。

 

2 PC总线的发展

 

随着基于80286 CPUIBM PC/AT微机和兼容机(286微机)的推出,PC总线——8ISA总线发展成为16ISA总线。但无论是8位还是16ISA总线,ISA总线仍然可以认为是直接连接到CPU的系统总线,ISA总线中的中断请求信号仍然可以认为是直接连接到8259,这意味着CPU使用MOVINOUT指令还是可以直接访问ISA总线设备,ISA总线设备也还是可以直接使用中断方式与CPU完成通信。

 

基于80386 CPUIBM PS/2微机使用了非开放标准的MCA(微通道)总线,迫使兼容机厂商在基于80386的兼容机(386微机)上继续使用ISA总线,这样一来使得ISA总线有了顽强的生命力,ISA总线设备直到19951996年仍然大量存在,此时Windows 95Windows NT 4.0已相继推出。

 

ISA总线生命力虽顽强,但无法实现32位数据传输,传输速度有限,为了解决这些问题,出现了各种新的PC总线标准,有代表性的是PCI总线(严格地说是PCI局部总线)。关于PCI总线的细节,例如北桥芯片、南桥芯片等,不是本文准备讨论的问题,感兴趣的读者可以参考相关资料。ISA总线设备通常需要手工配置,配置不当容易引起内存地址空间、IO地址空间或者IRQ冲突,即插即用(PnP)技术的出现进一步解决了该问题。

 

PCI总线协议比ISA总线协议复杂得多,但PCI总线设备同样可以通过x86 CPUMOVINOUT指令访问,PCI总线设备的中断请求信号最终也连接到PIC8259),因此相对于ISA总线设备,PCI总线设备的访问以及通过中断方式与CPU完成通信,编程方式上并没有本质上的区别,但由于PCI总线协议较复杂,编程复杂度也较高。

 

PCI总线可以支持PnPISA总线后期也加入了对PnP的支持。PnP的含义是支持PnP的外设添加到支持PnP的总线上时,操作系统能够通过总线获取外设提供的配置信息,使得操作系统能够自动识别外设,分配资源并安装设备驱动程序,进而与外设进行通信。

 

新型外设总线——通用串行总线(USB)出现之后,USB不仅支持PnP,还可以支持热插拔。USB协议采用串行通信方式,一条USB总线上还可以挂接多个USB设备,CPU访问USB设备必须通过PC主板上的USB主机控制器(USB Host Controller)完成,使用x86 CPUMOVINOUT指令可以访问到USB主机控制器,但无法直接访问到USB设备。USB传输类型中的中断传输实际上是轮询中断请求,属于查询方式,不是真正的硬件中断请求,因此USB设备也无法引发真正的CPU中断。这些原因导致了USB协议相当复杂,进而导致了USB设备访问的编程方式与ISAPCI总线设备大不相同。

 

由于并行总线自身的缺点(主要是干扰问题)限制了并行总线速度的进一步提高,PC高速总线串行化已经成为必然趋势。USBIEEE1394取代并口,SATA取代ATAPCI Express逐步取代PCI,甚至Intel Core i7 CPU的前端总线都已串行化——QPI总线等,都是这一趋势的体现,但串行总线协议通常都比并行总线协议复杂。

 

3 设备驱动程序的发展

 

外设硬件是一种临界资源,在对某个外设的一次访问结束之前,不应该允许打断访问再进行一次新的访问,否则会把对外设的访问搅乱。即使操作系统是多任务操作系统,这个原则也是不能违反的,如果宏观上多个任务需要同时访问某个外设,那么在微观上只能让访问一次一次地进行,这种处理方式称为多任务同时访问外设时的同步。

 

DOS时代,由于DOS是单任务操作系统,任何一个进程都可以认为独占PC所有硬件,因此DOS无需考虑同步问题,硬件可以对DOS应用程序透明。在DOS时代,除了存储设备(块设备)之外,很少开发外设的设备驱动程序,而采取DOS应用程序直接访问外设的编程方式。

 

到了Windows时代之后,Windows是多任务操作系统,但早期的Windows版本采用协同多任务方式,只有某个任务主动放弃控制权(通过消息循环隐含实现)时,其它任务才可能获得被调度的机会,因此早期的Windows版本实际上并不存在对某个外设的一次访问被打断的问题,同样无需考虑同步问题,Windows 3.0以前的版本可以工作于实模式,使用DOS的设备驱动程序。

 

随着Windows 3.x的推出,386增强模式的出现,工作于386增强模式下的Windows 3.x可以允许多个DOS应用程序和多个Windows应用程序同时运行,由于DOS应用程序都假定自己独占所有硬件,可能自己直接访问硬件,同步问题终于出现了。Windows 3.x386增强模式下使用80386 CPU的保护模式,内核称为虚拟机管理器(VMM),工作于Ring 0,而Windows应用程序和DOS应用程序工作于Ring 3。所有Windows应用程序运行于同一个虚拟机(VM)中,而每一个DOS应用程序都运行于一个独立的虚拟机中。VMM管理虚拟机,并通过80386保护模式功能,例如内存分页机制、IO保护机制等,以及虚拟设备驱动程序(VxD)将硬件虚拟化提供给每一个虚拟机,使得每一个虚拟机,特别是运行DOS应用程序的虚拟机都认为自己有独立的硬件,进而实现DOS应用程序的兼容性,同时VMM通过不重入(实际上VMM也不可重入)保证了多个虚拟机同时访问外设时的同步。

 

Windows 9x的内核和工作于386增强模式下的Windows 3.x内核基本相同,也是VMM,因此Windows 9x的设备驱动程序也是VxDWindows 9x支持Win32,所有的Win32Win16应用程序运行于同一个虚拟机中,每一个DOS应用程序仍然运行于一个独立的虚拟机中。Windows 9x支持抢占式多任务,但VMM仍然不可重入,还是能保证多任务同时访问外设时的同步。

 

可见,对于x86平台上的多任务操作系统,通常应用程序工作于Ring 3,而设备驱动程序和操作系统内核一起工作于Ring 0,这样既能保证设备驱动程序能够访问外设,又能通过保护模式拦截应用程序对外设的访问,或者限制应用程序只能通过内核和设备驱动程序访问外设,进而解决同步问题。这个基本体系结构从Windows 3.x一直到今天的Windows Vista,甚至Linux都是一致的。

 

这样一来,如果要让附加的外设能够用于Windows 3.x9x,应该为其开发相应的VxD,配合VMM完成外设硬件的虚拟化。如果某附加外设不考虑被DOS应用程序访问,也就是只被一个虚拟机访问,则相应的VxD就只相当于直接访问该外设的驱动程序,在Windows 9xVxD可以为Win32应用程序调用VxD提供通过DeviceIoControl API访问的接口。

 

Windows 95推出时,ISA总线设备仍然大量存在,PCI总线设备较少,Windows 95还不能支持USB,因此Windows 95时代的VxD还使用汇编语言编程,使用x86 CPUMOVINOUT指令访问总线上的外设,通过调用Windows 95自带的VPICD(虚拟可编程中断控制器设备)和VDMAD(虚拟DMA设备)VxD提供的VxD服务实现外设的中断和DMA方式通信,外设访问比较透明。Windows 9x支持PnPWindows 9x VxD可以读取支持PnP外设的配置信息,但功能较有限。

 

Windows NT是源自OS/232位操作系统,内核为NT Kernel,与Windows 9x的内核VMM不同,因此Windows NT使用与Windows 9x不同的驱动程序模型,称为NT Driver,工作于内核模式的NT Driver称为Kernel-Mode DriverKMD,内核模式驱动程序),通常NT Driver就是指KMDWindows NT有可移植性,并不一定工作于x86平台,因此NT Driver推荐使用Windows NT硬件抽象层(HAL)提供的内核功能调用代替与x86 CPU相关的MOVINOUT指令,例如使用READ_REGISTER_UCHARWRITE_REGISTER_UCHAR内核功能调用访问外设映射到内存地址空间的存储器、使用READ_PORT_UCHARWRITE_PORT_UCHAR内核功能调用访问外设IO地址空间的IO端口等。如果Windows NT工作于x86平台,那么NT KernelNT Driver同样工作于Ring 0,上述HAL提供的内核功能调用内部最终还是使用了MOVINOUT指令。

 

Windows NT 4.0仍然不支持PnP,同样也不支持USB。在x86平台上,NT Driver访问ISAPCI总线设备实际上还是使用CPUMOVINOUT指令,HAL提供的HalTranslateBusAddress内核功能调用虽然与总线相关,但实际上只是完成地址转换,对于PCI总线之外的总线,基本没有实际作用。但随着PC总线的发展,总线协议越来越复杂,这种直接使用x86 CPUMOVINOUT指令访问外设的方法,越来越行不通了。

 

随着Windows 95 OSR 2.1(即俗称的“Win97”最终版本)加入了对USB的支持,USB本身的特点以及复杂的USB协议,使得直接使用x86 CPUMOVINOUT指令访问USB设备很难行得通,传统的VxDNT Driver都不能满足USB设备驱动程序的要求,有必要使用新的设备驱动程序模型。在Windows 95 OSR 2.1中,Microsoft加入了新的设备驱动程序模型——WDMWindows Driver Model)以支持USBUSB设备驱动程序,早期的WDM文档可在Windows NT 4.0 DDK中找到,版本为0.80,发布日期为19961023

 

WDM使用NT Driver的基本编程模型,某些资料认为WDM是支持PnPNT Driver,这种看法不全面。WDM的重要特点之一当然是能很好地支持PnP,但WDM的另一重要特点,或者说核心思想,是层次化的驱动程序模型,以USB为例:WDMUSB驱动程序分层,下层的USB总线驱动程序处理USB协议,上层的USB功能驱动程序处理USB设备功能,Windows提供USB总线驱动程序,这样用户为USB设备开发驱动程序时,只需要开发USB功能驱动程序,与USB总线驱动程序通信,即可驱动USB设备,无需自己直接和USB协议打交道。因此,在开发USB设备的WDM驱动程序时,不需要也不可能通过MOVINOUT指令方式自行访问存储器和IO端口。

 

Windows 98开始扩大WDM的应用面,在Windows 98中,USBIEEE1394等具有复杂协议的总线,其设备的驱动程序均必须使用WDM驱动程序;传统ISAPCI总线设备的驱动程序开发既可以使用VxD,又可以使用WDM,在使用WDM的情况下,可以提供更好的PnP支持。Windows 2000同时支持NT DriverWDM

 

随着Windows Vista推出的Microsoft下一代驱动程序开发模型——WDFWindows驱动程序基础,Windows Driver Foundation)中的KMDF(内核模式驱动程序框架,Kernel-mode Driver Framework)仍然基于WDM实现,是对WDM的进一步包装简化;WDF中的UMDF(用户模式驱动程序框架,User-mode Driver Framework)可以将USBIEEE1394等总线设备的驱动程序进一步分层,使得设备驱动程序位于用户模式(Ring 3),进一步简化设备驱动程序的开发并提高安全性。Windows Vista同时支持WDFWDM

 

可以这样说,PC总线的发展在很大程度上推动了设备驱动程序的发展。

 

4 设备驱动程序与Rootkits的关系

 

上面已经提到过,x8680386以上CPU)平台上的多任务操作系统,通常设备驱动程序和操作系统内核一起工作于Ring 0Ring 0x86 CPU保护模式的最高特权级,可以不受限制地访问任何内存代码/数据以及外设硬件,这样一来,一方面解决了设备驱动程序访问外设的问题,另一方面也使得设备驱动程序实际成为了Ring 0应用程序,也就是操作系统中的最高特权级应用程序,与操作系统内核“平起平坐”。

 

设备驱动程序本来是设计来驱动外设硬件的,但设备驱动程序本质上是Ring 0应用程序这一特点使得设备驱动程序成为了一把双刃剑,操作系统内核无法对同样工作在Ring 0的设备驱动程序进行行为限制,如果设计设备驱动程序不用来驱动实际外设,而用来对操作系统内核进行监控、钩挂甚至篡改,操作系统内核是允许这种行为的,无法进行保护。很显然,设备驱动程序的这一特点为病毒和Rootkits大开了方便之门。设备驱动程序容易设计,操作系统加载设备驱动程序简单,是最容易被“合法”加载的Ring 0应用程序,因此大部分Rootkits都设计成设备驱动程序的形式。当然,如果有绕过操作系统保护侵入操作系统内核的手段,Rootkits也不一定需要设计成设备驱动程序。

 

可以说,设备驱动程序本身并无过,是开发Rootkits的人将其“用歪”了。 不过,这种“歪用”设备驱动程序的方式也是设计内核调试器、反病毒软件实时监控程序等内核应用程序的核心技术。

 

显然,作为Rootkits的设备驱动程序并不驱动实际外设,通常不与USB之类的总线打交道,PnP支持更谈不上,因此Windows NT2000XP及其以后版本上的Rootkits通常都设计成NT Driver,无需使用WDM

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值