ACPI规范:
ACPI Specification 概述(基于ACPI_Spec_6_4_Jan22)_anqi8955的专栏-CSDN博客
ACPI Specification 第一章 介绍_anqi8955的专栏-CSDN博客
ACPI Specification 第二章 条款的定义_anqi8955的专栏-CSDN博客
ACPI Specification 第三章 ACPI概念_anqi8955的专栏-CSDN博客
ACPI Specification 第四章 ACPI硬件规范_anqi8955的专栏-CSDN博客
UEFI规范:
UEFI Specification 第一章 引言(基于UEFI_Spec_2_9_2021_03_18)_anqi8955的专栏-CSDN博客
UEFI Specification 第二章 概述_anqi8955的专栏-CSDN博客
EDK2编译:
edk2-vUDK2018编译_anqi8955的专栏-CSDN博客
一、素 质 要 求
1 强烈的责任心和敬业精神
项目的BIOS工作,与独立的软件工作有一个重要的区别就是,BIOS工作配合硬件、电源、Layout、测试、EMI、测试等部分紧密工作,如果由于BIOS的Delay,那么可能造成这个项目的严重Delay,这样不但拖住产品上市,而且拖住整个项目的资源,造成资源的严重浪费。所以,BIOS工程师必须有十分强烈的时间观念,这种的较强的时间观念不仅仅表现在整个项目中间,更表现在一些小问题上,例如,如果某个问题能够一天解决的话,我们就不要拖到两天。
另外,BIOS工作和底层硬件、上层操作系统的结合非常紧密,很多时间,BIOS工程师需要解决各种BUG,而且这些BUG的直接原因可能很难寻找和界定,这要求BIOS工程师对于各种BUG,必须时时刻刻从BIOS出发,从系统和全局出发去分析每一个BUG的原因以及解决方法。同样,有的问题,可能从硬件和软件等角度都可能解决,BIOS工程师必须根据成本和品质综合衡量,寻找较优的解决方法。
而且,在实际项目开发中间,BIOS工程师必须具有主动精神。例如,为了解决某个问题,我们需要验证这个现象,那么我们需要一个机台去验证,这时,我们不能等待别人给我们送来机台,而应该主动去库房借用,或者就在测试室借用。在BIOS工程师工作过程中间,遇到问题等待,或者无能为力而等待是致命的。所以BIOS工程师必须具备寻求多种资源解决问题的能力,但是BIOS必须对这些问题负责,具有主动精神。
2 开放的思想
BIOS工作,由于牵涉的内容广而深。所以,对于一个BIOS工程师不可能对于每一个部分都了解的十分清楚,所以,BIOS工程师对于所有问题认识都必须有一个开发的心态,虚心听取别人的意见,他山之石,可以攻玉。而且,对于不同的内容从不同角度出发可能理解和解决方法都不一样。
有很多工程师,在考虑问题的时候,不能够从大局出发考虑问题,总是局限自己的一个小范围,这样的工程师的发展前途是不大的。例如,测试部或者品质部就某个BUG向BIOS工程师施加压力的时候,有些BIOS工程师就急了,和其它部门争论,说明这些问题不是问题,或者不是自己的问题。这样的态度本身就是不对的,我们的正确态度应该建立在大家都是为了产品利益的基础上考虑的,即使真的是其它部门的问题,我们也应该考虑从BIOS角度是否可以更经济的解决问题。
保证开发的思想很重要,很多刚毕业的学生,或者刚到一个新的环境中的新员工,对于新的环境很多都是不熟悉的。很多员工不愿意问问题,不懂装懂,怕问了外行的问题别人笑话。其实术业有专攻,如果我们只是不懂装懂,那么将来结果就是我们真的不懂了,即使以后搞懂了,我们进步的速度也没有喜欢问问题的工程师快。
3 强烈的求知欲和优秀的学习能力
BIOS工作,向下需要理解硬件的工作的物理和逻辑原理,向上需要了解OS的工作机理。所以,无论从深度上而言,还是从广度上而言,BIOS都是很深的。所以,作为一名优秀的BIOS工程师,必须具有强烈的求知欲和优秀的学习能力。
BIOS是本身是非常枯燥和琐碎的工作,很多的时间,BIOS工程师都在和各种各样的BUG打交道。如果BIOS工程师没有一种对于各种设备工作原理强烈的求知欲,是很难有耐心处理这么多琐碎的BUG,也是很难深入理解这些总线、协议的深层原理的。
强烈的求知欲首先表现在学习态度上。在学习态度上,一定要清楚的能明白每一种总线、协议的工作原理,不要不懂装懂,随便糊弄自己,敷衍自己,不去深究自己不懂的地方。在学习和工作中间,遇到不懂的地方,我们要仔细思考,探询合理的答案,如果经过自己仔细思考,还是能不明白,可以暂时放过这个问题,以后一定再回头思考,或者是向别人请教寻求答案。但是,无论如何都不能随便放掉自己不能的问题。
如果向别人请教,但是所得到的答案你并不满意,那么BIOS工程师必须具有打破沙锅问到底的精神,最后,如果还是得不到满意的答案,只有靠自己去探索。
BIOS所牵涉的知识特别多,所以很多的知识是没有办法靠别人来一点一点的教你的,主要还得靠自己自学。当然,我们也会提供一系列的讲座,但是,这些讲座只是给你提供一些重点的思路和一些难点问题,而且,讲课特别密集,或者是时间长了都是容易忘的,所以,还得靠自己自学。
自学包括学习理论知识和阅读代码,如何学习是一个方法的问题,方法因人而异,每一个肯定都有自己的方法。这里提出一些方法仅供参考。
在实际工作中,我们发现很多工程师学习理论知识并不深入。究其原因,很多工程师的解释是BIOS牵涉的内容太多,特别是一接触项目的时候,一会儿测试给你报一个ACPI方面问题,一会儿给你报一个PCMCIA方面问题,一会儿又给你报一个网络方面的问题,所以很多工程师阅读各个方面的理论知识只是泛泛了解,没有深入进去。其实这是非常错误的思想。实际解决问题,需要特别精准的理解,这样才能保证项目的质量。泛泛的理解,对于解决问题而言,等于没有理解。这样,我们如何处理工作中间如此之多方面的问题,后面我们介绍的一些BUG解决方法,可以简单的解决,另外,我们还可以通过向这方面熟悉的工程师请教。还有一点,技术资料很多,但是其结构和术语都是差不多,如果我们先花力气,读完一个之后,我们再去读其它的可能会容易的多。这就是中国一句古话“伤其十指,不如断其一指”。
首先自学要有记笔记的习惯,有人喜欢有纸和笔来记,有人喜欢有电脑记,反正效果都是一样的。记录的内容一般包括一些重点、纲要和难点等。当然,也要记录自己不懂的地方,也可能是自己有理解歧异的地方。例如,如果我们阅读PCI规范的开头一句“The PCI Local Bus is a high performance 32-bit or 64-bit bus with multiplexed address and data lines.”,这句话对于一个初次接触PCI总线的人可能就存在歧异,这里说“PCI是一个32位或者64位总线”,这里是说“PCI总线是64根数据线吗?还是指PCI总线支持64位地址?”“32位和64位的或者是什么关系,是在一个一定的总线上要么设计成32位,要么设计成64位?还是指一个总线在一种条件下可以作为32位使用,而在另一种条件下是作为64位使用?还是指在时间上,有时根据需要可以作为64位来用,而在另一个时间根据需要又可以作为32位来使用?”等等,我们可以提出很多的问题,当然,这些问题随着我们学习的深入,可以自动解决。这种提问题的能力非常关键,我相信有很多工程师拿过一本规范,翻着翻着就读完,然而,度过后你问他学到什么,他肯定是一问三不知。所以,能不能提问题,表明你的学习是否投入,以及你的思考能力如何。另外,我们在听别人讲座的时候,也一定要多提出问题,只要是自己没有听懂,或者是理解有歧异的地方都可以提问。
上面讲了阅读规范的时候记笔记的方法,在阅读BIOS源代码的时候,更应该养成记笔记的习惯。BIOS的代码,很多时候都是汇编代码,结构性差,而且注释晦涩,或者是没有注释,我们如何你一步一步的阅读这些代码呢?有一种的方法,就是我们从整体读到局部的方法。在整体上,我们首先画出系统的流程图,然后,一部分一部分,逐步喜欢,最后,我们不但可以看懂这个框架,还可以深入到每一个细节中。当然,看程序前,我们必须对基本的理论知识熟悉,当然,阅读源代码也可以加深我们对于理论知识的理解。阅读程序的时候,画出这些流程图是很好的学习方法。
很多大师都提到过,如果你真正懂一个原理,那么就把这个原理讲给一个外行听。如果我们把一堆术语、公式和程序讲给一个外行听,外行肯定是听不懂的,那么如何把一些“深奥”的理论讲给一个外行听呢?最好的方法就是使用比喻的方法。万物的运行法则是一样的,其中肯定是可以类比的。使用这种方法我们可以深刻理解一些理论的原理。例如,如果有一个学文科的人问你,“为什么接上一个USB摄像头需要安装一个驱动程序?这个驱动程序是不是就是在桌面形成的那个”如果打个比喻来回答这个问题,我们可以这样比喻,如果把一个笔记本软硬件系统(包括OS)比喻成一个公司,那么我们接入一个外接设备就是类似于新买了一辆轮船、一架飞机等。虽然买了轮船,但是轮船自己不会动,例如老板不能直接向轮船发送“请把货物给我送到法国的加来港去”,老板必须给这个轮船配备相应的船员(报告船长和驾驶员),有了船员之后,老板就可以给船员下“请把货物给我送到法国的加来港去”的指令了,接到这个指令,船员就会把货物装上船,然后,驾驶员就可以把船开到法国的加来港去。如果这样比喻,设备的驱动程序就是轮船的船员了。那么为什么有的设备不需要安装驱动程序呢?例如把USB硬盘接入系统。这就相当于公司买了一辆汽车,因为汽车的驾驶等都是成熟的和标准化的,所以,无论是雪铁龙的,还是标志的,还是奔驰的,宝马的,他们的驾驶方式都是一样的,所以,即使不要安装驱动程序,我们也可以使用这样成熟的设备。不要安装驱动程序,不等于不需要驱动程序,这就好比公司买了一辆汽车,公司不需要配新的司机,不等于汽车不需要司机可以自动行驶。
那么为什么需要在桌面上放置一个程序呢?这个是不是驱动程序呢?我们说不是,这个程序我们一般叫做Applet,这个程序是怎么工作的呢?我们可以这样比喻,公司买了一条轮船,那么我们需要接单子才能有货物运输,所以,我们必须有一个人接单子,也就是Sales。也就是,如果我们想使用一个航运公司的船把货物运送到加来港,我们不能直接让航运公司的船员直接给我们运送。我们只有通过船运公司的Sales运送这些货物。而我们根本就不知道是哪些船员把货物送到的。这个Applet就相当于Sales。
这样,我们可以通过一些简单比喻把一些复杂的理论简单化。通过这样的简单化,我们可以加深对于这些理论的认识。其实这些工作并不是一些琐碎无聊的工作,许多科学家都做过这些工作,就是写一些科普读物。同样,在我们工作中间,我们不同的岗位许多互相合作,互相学习,所以BIOS要给测试培训,硬件要给信号培训等等。例如,虽然测试不要求测试完全细致学习ACPI规范,但是测试必须了解ACPI规范,所以,BIOS工程师如果能把ACPI规范讲懂给一个测试工程师听懂,而且觉得有所真正收获,那么,这说明BIOS工程师真的搞懂ACPI。
总结,学习了之后必须写总结,这样可以整理自己的思路,加深这些对这些内容的理解。不论是听别人的讲课,看别人写的PPT,还是阅读资料,资料写的东西都是别人的,只有经过自己的总结,这些东西才能成为自己的东西。所以不论学习什么规范,都要使用自己的思路和语言,把这些东西变成自己的东西。另外,BIOS工作所牵涉到内容太多,所以,BIOS工程师更要互相学习,所以,BIOS工程师学习完一项内容之后,要互相分享,互相学习,给其它BIOS工程师讲课。
许多BIOS相关资料,例如ACPI规范,内容都是非常琐碎和具体,所以,学起来相当枯燥。有些BIOS工程师要不就学习的相当粗,要不就学不下去,学不下去非常容易发累发困,尤其许多BIOS相关资料都是英文资料,对于细节理解起来更是有些困难,所以,这时,BIOS工程师可以采用抄写的方式学习。这样,抄写可以稳定急躁的情绪,另外,抄写可以留给自己足够的时间思考细节,深入理解。
学习方法因人而异,不论采用什么方法,必须达到深入理解的、熟练应用的目的。
4 很强的动手能力
理论是枯燥的,而且读起来非常晦涩,如果想深入理解,那么必须和实际结合在一起。动手能力对于解决问题相当关键。前面讲到,BIOS工程师很多时间都在解BUG,BIOS工程师可以从BugList系统上得到Bug的描述,但是,BIOS工程师如果解决BUG,必须自己能够复现这个现象。BIOS在复现这些现象的时候,加深对于BUG的认识,可能同时就解决了问题。例如,测试工程师汇报机台不能上网。BIOS工程师接到这个BUG以后,应该重现这个问题,BIOS工程师使用自己的机台验证这个问题,可能在BIOS工程师的机台上就无法重现这个BUG,所以,BIOS工程师就可以把这个BUG局限到有的机台出现,有的机台无法重现。
很多理论,我们必须联系到实际应用中间才能深刻理论。例如在USB协议中间,提到很多抽象的包、交易、传输、描述符等,如果单看理论,非常复杂和抽象。但是,如果我们阅读别人写的USB驱动程序,那么理解起来就比较容易。如果设备允许,BIOS工程师有USB分析仪,那么BIOS使用USB分析仪去理解这些协议,那么理解这些协议就更加容易。
所以,对于BIOS工程师而言,无论怎样的理论学习,都无法代替试验。动手能力对于BIOS工程师尤其重要。
5 优秀的分析能力和问题处理能力
BIOS工程师,遇到很多的问题,都是系统级问题,如果BIOS不具备优秀的分析能力和问题处理能力,许多问题根本无法解决。在后面我们会详细讨论如何解决BUG,在这里,我们主要分析一些处理问题的基本能力。
在学校里,学生主要培养的是学习能力。那么,作为一个合格的毕业生,进入实际工作中后,除了,除了具有学习能力外,更应该培养解决问题的能力,也就是工作的能力。作为一名优秀的BIOS工程师,BIOS工程师做事必须注意工作三点原则。“凡是必须文档化、凡是必须计划、凡是必须总结”
作为一名BIOS工程师,必须注意形成写文档的习惯。很多软件工程师都有经验,在写程序的时候,对于每一行、每一句和每一段都了如指掌,当时认为写文档是非常无聊很没有意义的事情。但是,等到几个月或者半年以后,回头再去读的时候,可能一点都看不懂,所以,后来必须花掉很多的时间去重新温习以前写过的代码。如果在写程序的时候,BIOS工程师就很好的做好注释,这些代码,在以后的工作中间,不仅修改容易,而且,这些代码很容易应用到其它项目中间。
前文也提到过,我们在学习的时候,最好也能够记笔记,这些笔记一边可以总结自己的思路,另外,如果以后想忘记,可以回头查找。这个习惯也是使用文档的方法。另外,在我们工作中间,很多时候,我们都需要各种环节来记录我们的工作交付,这些工作需要Check工作,Check工作可以保证我们工作做到位,保证产品质量。所以,我们一定要养成写文档的习惯。
下面我们解释计划的重要性。在学习的时候,不论是做课后习题,还是做课程设计,这些题目都是比较小。在我们的实际工作中间,尤其是我们笔记本的开发,是非常巨大的工程,不但需要各个部分的紧密配合,而且开发工作长达数月之久,如果没有缜密的计划,我们开发不但无法保证我们的质量,而且没有办法优化我们的资源配置和缩短开发周期,这些都是我们的优势所在。
计划对于我们开发独立的软件项目也非常重要,例如,如果我们在BIOS中间开发一套硬盘备份和恢复的工作,我们应该做如下的计划。第一步,我们要讨论整个项目的目标。第二步,我们讨论我们的工作设计多少内容。我们的项目涉及到如下几个部分:硬盘的操作与INT13知识、文件系统知识、OS下备份恢复的Applet和硬盘操作驱动程序。第三步,我们根据各个部分的内容分别研究各个部分内容,学习相关知识,制定相关的部分接口。第四步,上一步,我们将工作划分成几个部分,各个部分的工作可以独立开发。我们根据各个部分的文档,可以开发我们的程序。
通过以上四步,我们很容易开发我们的功能,而且各个部分和各个步骤可以有条不紊的进行,而且,如果人员许可,我们可以安排很多部分并行进行。当然,在学习开发过程中,每一部分和每一步骤都可能存在困难,但是,这个困难可以逐个突破和逐个解决,不会影响其它部分的开发。否则,我们可能陷入每个琐碎的困难中间没有办法自拔。所以说,计划能力对于我们的开发工作非常重要。
计划的执行对于工作的每日工作也很重要。一些工程师的工作的每一步都需要别人督促,一个很重要的原因就是做事缺少计划性。这些工程师如果没有别人督促就是坐在那里发楞,其实事情很多,那是没有计划在那里,所以没有人督促就想不起应该干什么事情,所谓“好记性比不上好笔头”。一个很好的工作习惯就是每天早上上班之前给自己一天的工作做一个很好的计划,花上几分钟思考一下今天有那些工作需要做,主要回忆今天项目中间有那些事情要做?有哪些文档要写,有哪些资料需要学习,有哪些BUG需要解,有那些会议需要参加,有那些Mail需要处理,等等,做一个简单的回顾,我们就可以列一个简单的List,这样,工程师在没有什么事情做的时候,可以看看什么事情没有做。所以,计划是一个很好的工作方式。
其实计划的能力对于我们调试的时候也是非常有用的。特别是一些系统级的问题,其原因可能多方面的,到底是那个问题造成的?需要我们逐个分析和逐个排除。为此,我们需要分析可能的原因,逐个排除。例如,系统出现S3 Resume之后,播放媒体文件异常,我们怎么分析?有那些可能的原因呢?我们可以如下分析,第一,可能是Aliza的声卡有关,这是ICH6M与ICH4M的不同。第二,这可能是BIOS对于Codec初始化错误造成。第三,这可能是S3中,Codec的配置值丢失造成的。第四,可能是Codec驱动程序的问题。然后,我们按照可能性和分析的难易程度排序,然后逐个实现,最终,我们可能找到解决方法。如果我们没有计划,东一头、西一头,遇到困难就退,然后找新的方法,结果是浪费时间,往往解决不了问题,还走入了死胡同。所以,计划性就是对于我们解决一个BUG也是非常关键。
计划执行以后,后面的关键就是我们如何实施机会了。这关键就看我们的执行力了。好的执行力加上计划,可能比过一些个人能力上的缺陷,这就是我们常说的,有时候“按部就班解决问题可能是最快的”,我想打过RPG游戏的工程师可能知道,在RPG游戏中间,很多时候我们都在走迷宫,从迷宫中到出口,肯定有一条最近的路线,然后,在迷宫中间,我们肯定不能一下看出到出口的最近路径,随意,这时,我们如果按照“左手法则”或者“右手法则”才是最好的走迷宫方法。“按部就班”就是我们解一些复杂BUG的“左手法则”。
计划可以逐层分解,结果,分成了一步一步的小步骤。所以,我们就去逐步实施这些小步骤就可以。例如,在上面的“系统出现S3 Resume之后,播放媒体文件异常”的BUG的第三个计划中,我们分析可能是S3中间,Codec的电源丢了,所以,一些寄存器丢了。于是,我么就可以,比较S3前后的PCI配置空间寄存器的值、IO空间寄存器的值。比较PCI配置空间的值,我们需要在S3之前,记下PCI配置空间的值,然后做S3,然后Resume,之后读取PCI配置空间的值,之后比较,看有那些寄存器的值变化了,然后分析那些值可能造成这个BUG,在S3 Resume的BIOS程序中间恢复这些值。这样一步一步都是非常简单的动作,这就看我们的耐心和细心了。
如何做好小事是非常关键的能力,然后这些能力也是一些刚毕业不久的学生最缺少的能力。大学生以“骄子”自居。毕业以后,都想做一番大事,结果不愿意做一些小事。结果不愿意做一些小事,最好,自己的能力一天一天退步。其实,大学中间学习的东西离实际工作中间的要求是不同的,特别是BIOS工作,毕业的学生几乎的从头学起。工作中间的事情必须从小事一步一步坐起,大事都是由一步一步的小事组成的,只有每个小事的环节都成功,这样,大事才能够成功。另外,工作中间,每一个工作都关乎产品的质量,研发的成本,所以每一步都和经济直接挂钩,只有证明自己能力的工程师才能够从小事做起,一步一步的接一些大的事情做。这些原则,对于初步接BIOS工作的工程师尤其重要。
另外,我们在执行计划的过程中间也要注意计划的执行的时间性。公司内的工作与学校中间的试验有许多的不同,其中一个重要的不同就是公司内的项目时间性比实验室内的项目强的多。一些比较大的项目往往计划的步骤很多,所以,很多工程师在做项目的时间性上比较差,例如,一个项目分十步,计划3个月完成,计划前三天完成第一步,到第四天完成第二步,到第七天完成第三步,。。。。。。,很多工程师在前几步的时候时间Delay了,这时工程师会给自己一些借口和安慰,前面Delay无所谓,不过是前面比较难,后面的比较简单的,我能够步回来。其实这种想法本身就是错误,工作中间的每一步都是困难的,我们不能轻视每一步,后面的可能比开头更难。另外,如果Delay,我们必须重视,赶紧补上,否则,一定会造成整个项目的Delay。这种思想时间紧迫性的思想在BIOS工程师中间一定要树立。
优秀的工程师必须善于总结,总结的目的就是为了以后做的更好,总结经验吸取教训。总结分成几种,有项目总结、周报、关键事件总结等等。每种总结我们都要认真做。这里强调几天,第一就是我们周报必须做,很多工程师在工作一段时间之后,如果让写工作总结等,很困难,好像每天都很忙,但是回过头去仔细分析,好像并没有做什么事,这就是因为我们没有做周报的习惯,如果我们一直做周报,等到后期,我们回头总结,把以前的周志拿过来就可以看到我们做什么,我们的效率到底怎么样。这时,我们可以有一个非常全局的观点审视我们的过去,可以很好的总结。对于一个项目也是,因为一个笔记本开发项目的周期很长,所以,在项目结束的时候,让PM或者PL总结的时候,确发现没有什么可以总结,然后好像在项目中间遇到很多很多问题,也有过很多很多的想法,为此,我们项目总结也要做到经常话,例如可以一个月总结一个,这样,在项目结束的时候,我们可以把前面56个月的总结放到一起,可以作为整个项目的总结。
那么总结一般总结那些内容呢?我们总结一般而言,应该主要总结一些成功的经验和教训。对于成功的方法,我们可以加以扩大发扬。例如,我们第一次做SONOMA项目是S27I项目,由于Chipset和BIOS Code全部是新的,所以到稳定需要一个相当长的时间。特别是一些新的功能,我们工程师心理没有底,例如PCI Express等等,于是我们给的解决方法是先在915系列主机板上调试,虽然Chipset不一样,一个是台式的,一个是笔记本的,但是我们可以对于一族芯片组的主要特征熟悉和掌握。这个经验对于我们S27I平台前期工作进展非常顺利,所以,以后新的Chipset的导入,我们都应该提前做类似的工作。另外,我们也可以看到我们的不足。例如,本来我们想6月份就启动主板的调试,结果6月份由于CPU的问题,我们一直没有启动,其实,如果我们向Intel要Sample的,会非常容易,结果我们没有去要,这就是我们的失误,否则,我们S27I的BIOS进展应该更顺利。等等,类似的经验我们都可以去总结,包括技术上新的认识。
提到做事能力,我们这里不得不提一下对待困难的态度。我们做产品研发,也纯粹的科学开发不同,对于我们的研发可以说没有解决不了的困难,区别的只是你的态度。有很多工程师,在遇到的困难的时候,就想放弃,好像真的解决不了,其实,并不是这样的,只要你用心肯定是能够解决的了的。所以,不怕困难,敢于迎着困难上,一个作为优秀BIOS工程师的基本做事态度。
上面我们简单的讨论了BIOS工程师应该具有的一些基本素质。当然,一个基本的职业道德和能力也是需要,例如,能够吃苦耐劳等等,由于这部分是企业文化的内容,我们不在这里详细讨论
二、技能要求
1 深刻汇编语言和C语言。
X86汇编语言是BIOS工程师的基本语言,要求BIOS工程师必须深刻掌握。对于汇编语言的理解包括对于各种常用指令的理解,汇编语言编译方法、文件链接、文件组织和结构等理解,对程序分段控制等位语言的理解,另外,我们还必须理解MAK语法等内容。C语言也是BIOS工程师必须掌握的语言,因为BIOS中间的有一部分程序是用C语言写的,而且很多时候BIOS工程师需要写一些小的Utility工具,所以BIOS工程师必须熟练掌握C语言的高级用法。
2 对于计算机架构有深刻的认识。
BIOS工程师必须对现代计算机架构有深刻的认识,包括对于X86处理器内部架构认识,IO空间和Memory空间的使用,中断和SMI的认识,DMA的认识,Pipeline工作方式的理解等等
3 熟练掌握ACPI规范和编程。
ACPI规范是由Microsoft和Intel等公司制定的一个规范,其使用抽象的ASL语言向OS汇报系统硬件配置和电源管理方法。在ACPI之前,这些功能分别由APM、PnP BIOS和MP等规范定义。随着Microsoft的强大,BIOS工程师的工作很大部分局限到ACPI部分,所以对于BIOS工程师必须熟练掌握ACPI规范。这里说明一下,ACPI涵盖的配置和电源管理功能是没有包含在其它规范定义的,像PCI、USB等,其相应的电源管理功能由相应的总线驱动程序实现。笔记本BIOS与台式电脑的主要不同就表现在电源管理上面,所以,BIOS工程师必须熟练掌握ACPI规范理论知识,并可以熟练编程。
这个地方还需要详细描述ACPI的细节。
4 熟练掌握PCI、ISA等相关规范。
第一代BIOS主要是IBM的8086 BIOS,叫做AT BIOS,主要集中在DMA、PIC等功能初始化和配置,和中断服务程序的提供,其总线是ISA总线。第二代BIOS叫做PnP BIOS,主要是符合PnP BIOS规范的BIOS。现在的BIOS,叫做PCI BIOS,主要专著于PCI架构和ACPI。可见,PCI规范对于BIOS工程师而言,一个必须熟练掌握的规范。
PCI总线产生的主要目的就是解决ISA总线的几个主要缺点:1.随着CPU频率的迅速提高,IO接口的总线很快表现为系统的瓶颈。2.由于传统的总线缺乏灵活的资源分配机制,所以随着外设的品种与数量的不断增加,设备冲突的问题也表现的越来越严重。
对于第一个问题,PCI的解决方案通过几个方面提高了系统的吞吐率。首先,将总线的始终频率提高到33MHZ,这对于以前的ISA总线而言,已经是很大的改进。同时,还在传输的机制上支持突发传输(Burst Transport),突发传输的意义就是对于地址连续的传输,只要给出其开始地址,而以后的传输靠地址的有序递增而计算出相应的数据的地址,这样提高了有效数据传输的吞吐量。
我们知道对于IntelX86家族的处理而言,外设的资源主要是指Memory、IO空间以及中断,而PCI的解决方案就是给定一种灵活的机制,使用软件在启动过程中给每一个设备分配互不冲突的资源,从而是不同的设备可以协调的使用资源。其实更具体的讲,PCI规范的体统实现了可编程的地址解码器,和可编程的中断路由设备,从而实现资源的灵活分配。
作为BIOS工程师,我们必须熟悉PCI配置交易的操作,熟悉PCI Memory和IO资源分配原则,熟悉PCI的中断Routing和分配方式,了解PCI ROM的结构和调用方式等等
5 熟悉BIOS基本架构和现代主机板架构。
现代主板是基于PCI总线的逻辑架构,由上往下,由CPU、北桥、南桥和SuperIO等主要部件组成,逻辑上表现为PCI的树形的总线、设备和功能的结构。现代BIOS是基于PCI架构的BIOS。从BIOS初始化流程上讲,CPU上电开始,去内存F000:FFF0处运行,由于设备处于初试状态,所以这个内存地址指向Flash ROM,这段ROM中的程序主要的功能是完成Chipset的一些简单初始化、RAM的初始化等等,然后将程序从ROM中解压到Memory中间,然后把E000-F000处内容Shadow到RAM中运行。
在RAM中间,主要进行PCI系统的初始化, VGA初始化, PnP初始化和Compatible PnP系统初始化,也就是SuperIO。然后是Keyboard控制器初始化、ISA系统初始化(包括LPT和COMM口等)、IDE初始化、USB初始化、ACPI初始化和CPU电源管理部分初始化、ISA ROM初始化和PCI ROM初始化。在这些主要程序初始化之后,调用INT19启动OS,OS启动过程中间,将调用ACPI的程序,继续配置系统,最后OS Boot完成。
这些功能初始化都是主要部分,其中还有一些细节没有描述。这些部分都要求BIOS工程师能够熟悉。
6 掌握ATA、USB、DDR、PnP、VESA、APM、PC Card、1394、AC97、SMBus等相关常用规范。
前面分析BIOS流程的时候,提到各个主要功能部分初始化的时候,要求BIOS工程师最好掌握各个部分内容。 其实各个部分内容是非常复杂的,所以能够全部掌握是需要艰苦努力的。
ATA指的是系统硬盘系统的相关部分内容,主要包括以下几个部分内容:1,ATA/ATAPI部分,描述ATA控制器、硬盘和CDROM等结构操作等2,INT13例程3,硬盘的文件系统4,OS下的硬盘驱动系统。
USB部分主要包括以下几个部分内容,UHCI和OHCI协议、USB总线传输协议、USB设备架构和USB类设备架构等。只有掌握这个几个部分的内容,BIOS工程师才可以利用USB协议对USB系统和USB设备进行操作。
DDR系统,是指系统RAM系统,因为DDR是现在流行的SDRAM。这个部分内容要求BIOS工程师对DDR的组成结构和交易规范,Memory Sizing机制等等。这个部分内容比较繁琐,所以BIOS工程师必须深入理解这部分。
PnP规范是基于ISA和EISA规范,是PCI之前BIOS的结构。PnP部分主要包括PnP ISA规范、PnP BIOS规范和Compatible PnP相关内容。由于ISA规范已经基本退出历史舞台,所以这部分内容在手提电脑的开发中可以不考虑,PnP BIOS规范定义了给操作系统使用的一些BIOS功能调用和数据结构,Compatible PnP就是现在长使用的SuperIO相关内容。PnP BIOS虽然不长使用,但是这个部分内容如果配置不正确可能会造成一些小的BUG。由于红外、COMM、LPT等内容还都是在SuperIO上实现,所以这部分内容BIOS必须深刻掌握。
VESA规范这里我们对于显示系统的通称,主要包括VESA规范和VGA BIOS的相关内容,VESA规范定义了在DOS下进行真彩色显示相关中断调用,VGA BIOS包括内容更多,BIOS工程师这里更关注的内容是LCD、CRT的切换等等相关内容。
SMBus是一个简单的协议,对于BIOS工程师一个入门级的协议,对于BIOS工程师必须掌握。SMBus操作方式是典型的PIO方式交易,而且简单,所以这是一个理解设备操作的最好的例子。SMBus在笔记本电脑中应用非常广泛,Memory的SPD操作使用SMBus、Clock Chip的操作也是使用SMBus,另外,EC的Battery等设备的操作都是使用SMBus,所以,BIOS工程师必须熟悉SMBus协议。
PC Card规范对于笔记本电脑来讲,非常重要,现在笔记本系统几乎都用PC Card接口。PC Card分成两个部分,一部分是16位卡,一部分是32位CardBus。虽然,PC Card即将被PCI Express Card取代,但是在未来的一段时间内,BIOS工程师还必须掌握PC Card规范。
等等,除了这些规范意外,SD Card、1394、AC97等等协议都是BIOS工程师经常接触到,所以BIOS工程师也需要熟悉。
7 熟悉Intel/VIA/SIS等芯片组功能结构
以上各个部分功能的实现都是基于不同的芯片组实现的,对于我们系统厂商,我们经常使用的芯片组包括Intel、VIA和SIS等芯片组。
现代常用的Intel笔记本类型芯片组包括855系列和915系列(915GM、915PM、910GL)等等。855系列配合ICH4M的南桥使用,915系列配合ICH6M的南桥使用。
855系列北桥包括三种855PM、855GME、852G,北桥内容主要包括以下几个部分功能,HOST桥,和CPU接口;Memory Controller主要控制Memory;Hub Link接口主要是和南桥接口。另外855GME和855GM还包括一个内置的VGA控制器。855PM主要是使用外接AGP显卡使用的,所以有一个AGP的控制器。
VIA、SIS芯片组的结构和Intel差不多,这些芯片组都要求BIOS工程师掌握。
8 熟悉Windows的基本原理与操作
三、BIOS基础知识
BIOS,是技术机系统的基本固件,是系统基本组件。对于一般的用户,BIOS紧紧局限于CMOS设置程序,对于Award BIOS是在POST过程按Del键进入的Setup程序,对于Insyde笔记本BIOS是按F2进入的SCU工具。但是对于BIOS工程师而言,BIOS具有的含义绝对不止这个配置工具。BIOS最初是系统基本输入输出系统(Basic Input and Out System)的意思,最初是IBM 8086兼容机提出的概念,当初的BIOS功能相对简单,除了引导DOS以外,提供了一些系统基本硬件的中断服务程序,例如并口串口的中断调用。
随着现代计算机的发展,BIOS已经从单纯的系统基本输入输出的概念,演化成更复杂的系统。BIOS变成一个平台硬件和操作系统中间一个必要纽带。BIOS使得硬件平台的发展和OS独立,使得两个部分发展的自由度更大,有利于整个产品链的发展。BIOS,作为一个平台硬件和操作系统纽带,其作用主要是硬件初始化,不同媒质上OS的引导,以及把不同的平台信息抽象成一定格式信息给OS,如ACPI表格等,另外,一个更重要的特征,就是现在X86 CPU都提供了SMI的模式,通过这个模式,BIOS可以提供一个Runtime的程序,这个程序可以针对平台,做一些有价值的操作。
计算机一上电,CPU读取的第一条指令是FFFFFFF0h,这个地址被系统映射到BIOS ROM中间,CPU在从BIOS ROM中读取第一条BIOS程序指令之后,开始运行BIOS程序。 一般而言,BIOS前面程序的主要作用就是初始化系统RAM,如果系统程序一直在BIOS ROM程序中运行的话,有很多局限,例如,ROM运行速度远比RAM慢,ROM由于其只读特性,导致很多编程语言没有办法使用,如Call的指令没有办法正常执行,等等。所以BIOS一上电的首要任务就是初始化RAM。把RAM初始化方法划为BIOS的功能,对于RAM技术发展非常有利,RAM技术从SDR、发展到DDR、DDR2。
RAM初始化部分主要根据接入RAM的技术参数来配置Memory Controller,Memory Controller一般逻辑设计为系统北桥的一个PCI Function。主要配置参数包括Frequency、RAM颗粒宽度、TRAS、TRP、TRCD、CAS Latency和Refresh Period等参数。在初始化之后,然后做简单的校验,如果校验通过,那么RAM就可以正常工作了。
在RAM初始化之后,还有一个工作就是Clock Chip的编程。现代Clock Chip一般都是在SMBus上的(上面RAM的SPD也是通过SMBus编程)。Clock编程一般是为了省电的目前,关掉一些不需要的Pin,例如,PCI Clock Pin、CPU Clock Pin 2等,另外,PCI电源Stop PCI特性配置以及CPU Clock C3 Stop的配置等等。
RAM可以工作以后,下面的工作就是解压缩功能模块,例如,PCI初始化模块、PnP功能模块、ACPI功能模块、SCU模块、电源管理模块以及VGA等等ROM模块。由于BIOS ROM空间一般比较下,现在常用的BIOS ROM一般是2M/4M/8M比特。所以对于这么多的功能模块,BIOS ROM空间比较紧张,所以,一般都是把各个模块压缩放入BIOS ROM。所以,必须把压缩部分的代码解压缩到RAM中间才可以正常运行。
在ROM解压缩中,有两个特殊的模块,就是E000段和F000段的模块。我们知道,在BIOS刚刚上电的时候,BIOS ROM占有的地址空间E000段和F000段(4G高端的部分是双映射的)。所以,如果把BIOS E000段和F000段目标代码解压缩到这两段,必须Chipset做一个Shadow动作,这个动作就是把E000段和F000段地址从BIOS ROM重新映射到RAM。一般,BIOS先把这部分模块解压缩其他段,然后Shadow,之后,再从其他段Copy到E000段和F000段。
在ROM运行的模块还有一个特殊的模块,就是BootBlock。BootBlock是现在Flash ROM的一个技术,这个模块可以被保护,这样就不会被更新。在解压缩过程中,如果发现某个关键模块坏了,可以进入一个Crisis,在这个模块中将对系统做一个简单的初始化,然后从USB Floppy中寻找新的BIOS ROM文件,更新系统。这样,可以防止BIOS刷死的时候可以方便的更新系统。
然后,BIOS需要对Chipset做一些特别的配置,例如,SMI、PMBase等。接着BIOS初始化8259(或者APIC),完成之后,可以初始化Keyboard,因为Keyboard需要使用中断,Keyboard初始化之后,内置键盘就可以使用(注意,在Legacy模式下,由于USB键盘是使用Trip的方式,所以Keyboard Controller初始化必须正确,否则USB keyboard也将不能使用)。当然之后还有DMA等系统设备的初始化。
然后系统BIOS进行PCI初始化,在PCI初始化部分主要任务就是遍历PCI总线,填写总线号,然后统计每个设备的资源需求,从系统资源池中间给每一个设备分配资源,设备资源需要包括Memory资源、IO资源以及中断资源。在资源分配成功之后,BIOS将Enable这些设备的解码。BIOS初始化PCI总线有两个目的,其一是POST过程的必须的设备,例如PCI显卡、USB键盘和PATA和SATA硬盘等,如果使用的话,必须分配资源才可以使用,而且这些资源分配不能够重复。其二,对于Windows 98和老的Unix的,其本身没有PCI的初始化驱动,所以这些OS必须依赖BIOS的初始化。
PCI初始化是分成几个模块的,这些模块是分开运行的。在前面主要动作是遍历PCI总线,之后在IDE初始化之后,才进行PCI设备资源的配置,接着运行PCI设备ROM。
在PCI初始化之后,BIOS需要对VGA系统进行初始化,对VGA初始化主要就是查找显卡的VGA ROM,如果是外接的VGA卡,例如AGP或者是PCI Express的,那么BIOS本身没有这个卡的ROM,此时,BIOS必须从这个卡的ROM地址中读出ROM。如果这个卡本身是Onboard的,那么BIOS必须把ROM解压缩到RAM(可能已经解压缩过了)。之后,BIOS把VGA ROM Copy到RAM的C000段,然后,按照PCI ROM数据结构,找到ROM的初始化入口,跳到ROM的初始化部分执行,在这个初始化程序中间,将对显卡做简单的初始化,然后,把INT10安装到系统中。之后,BIOS就可以调用INT 10在系统显示屏上显示信息。
接着系统需要对键盘控制器进行初始化。首先系统检测是否有键盘接入,这是靠发送键盘Enable命令给键盘控制器,然后读键盘的回应状态,如果回应是0FAh,那么表明有键盘接入,否则没有键盘接入。然后Check键盘的Out Buffer Full中断功能是否正常。然后编程Keyboard的Command寄存器,进一步Check这些功能是否正常。在这些检测之后,则表明键盘可以使用。
在PCI初始化之后,系统的主要初始化任务就是初始化PnP系统,PnP系统主要任务包括四个部分,建立系统Device Node和PnP Runtime信息,初始化PnP ISA系统,建立DMI信息和初始化SuperIO部分。Device Node包括两个部分信息,一部分是静态信息,主要包括RTC、DMA、PIC等Legacy部分,另一个部分是系统的动态信息,包括COM、IrDA、LPT等部分。PnP Runtime信息主要是提高OEM INT15需要的系统信息部分内容,包括系统资源使用和内存大小等信息。PnP ISA一个古老部分内容,一般笔记本系统都不会Enable这个Function,这个部分内容主要通过ISA的配置和数据口,枚举PnP ISA设备,并分析设备的资源需求和分配资源给该设备,然后Enable这个设备。然后,在PnP收集之后,需要建立$PnP的Installation Check表格。SuperIO初始化,包括SuperIO设备的资源分析和资源分配,以及COM口等初始化动作。最后一个部分内容就是根据系统配置情况,更新系统DMI信息。
在这些初始化之后,BIOS可以点亮系统Panal,或者CRT显示。显示系统LOGO,或者POST信息,这个部分主要是根据VESA规范,调用VBIOS提供的各个功能调用。BIOS还会检测CPU的类型,然后显示CPU类型信息。如果用户选择的话,BIOS将检查一边系统RAM,是否有错误。
BIOS接下来需要做的事情就是填写系统的硬件中断向量和系统BIOS数据区相关信息。因为很多的系统中断需要参考BIOS数据区,所以必须先去填写BIOS数据区,然后初始化中断向量,这样很多硬件,如键盘,才可以使用。BIOS数据区初始化部分主要是填写并口串口的Timeout时间和键盘的Buffer地址等部分内容。中断向量初始化包括两个部分,硬件部分和软件部分,硬件部分包括两个主从8259,主要是键盘、Floppy、系统时钟等,软件部分包括INT10、INT15和INT13等。在这些初始化之后,BIOS还可选的对于PS/2鼠标进行初始化
下面进入了一个BIOS的大的功能块,USB的初始化一共需要7步,第一步,给每个USB控制器分配资源,主要IO资源,第二步,在RAM中分配一段内存空间给USB建立QH和TD的数据结构,第三步,初始化QH和TD,第四步,Reset USB端口,第五步,把QH和TD的开始地址连接各个控制的Frame List上,第六步,把Frame List的基址写入各个控制器的相应寄存器,第七步,RUN USB控制,开始枚举USB系统设备。在枚举设备的时候,记录设备信息,初始化设备,如果是USB HDD、USB CDROM等设备,还需要填写系统的BBS Table。这样枚举过后,系统的USB键盘鼠标硬盘等设备就可以使用了。
在系统的USB初始化之后,BIOS需要初始化ACPI部分。这个部分的主要内容是在系统建立ACPI相关的Table和DSDT表,并根据OEM配置修改DSDT部分内容。这些表格包括RSDP、DSDT、RSDT、BOOT、FACP和FACS。更新DSDT表主要时修改Mail Box的地址。
接下来,BIOS需要进行处理器电源管理的初始化,这部分代码主要是初始化处理器的P State电源管理,C State电源管理,以及Throttle和Thermal等的电源管理。P State初始化主要就是Intel SpeedStep技术,其主要动作就是根据CPU的类型,在SSDT中间插入_PSS的内部参数表。在C State初始化中间主要就是Enable C1E feature和建立C State的ACPI部分需要的表格。
接着,BIOS需要初始化Floppy和Hard Disk系统。传统Floppy的使用已经很少了,所以这个部分我们不必深究。而硬盘系统是我们整个BIOS重中之重。硬盘部分主要是检测每个硬盘,然后初始化硬盘,之后建立EDPT,初始化之后,我们可以使用INT 13访问硬盘。ATA设备和ATAPI设备由于其许多指令是不同的,所以,这两个部分初始化的路径是分开的。在硬盘初始化之前,我们必须对硬盘控制器进行初始化,这个部分的初始化主要涉及到各个时序的支持以及PCI资源分配等等。
接着,BIOS需要对系统电源管理进行初始化,特别是APM支持的系统。这个部分主要部分是初始化PM需要的各种数据,配置PM寄存器。然后Enable SW SMI功能。
在PM初始化之后,BIOS调用Int 19h,引导OS。Int 19h根据CMOS的配置和POST过程中BBS设备的Detect结果,尝试从各个Bootable设备启动。最终,Boot到OS。在引导OS的时候,可引导的媒质上的引导程序将继续使用BIOS提供的INT 13以及其他中断程序,知道OS使用自己的程序将这些中断程序替代掉。
在OS下面,ACPI系统将起到非常重要的作用,OS将分析ACPI数据结构,在特定时候执行ACPI的方法,完成平台要求的任务。除了ACPI之外,BIOS还有一些其他数据结构,如果PnP、PCI BIOS和MP数据结构,不同的OS可能使用这些数据结构。除了OS可见的数据结构之外,BIOS SMI程序可能被软件或者是硬件触发,独立于OS程序,执行我们定义的特殊功能。
以上是针对BIOS功能的一个简单描述,具体的BIOS实现会有所不同。如果仔细了解每一个部分内容,需要对每一个部分相关的规范和代码仔细阅读。
四、主板架构基础知识
上图是现在典型的PC主板架构,下面我们以Insyde的Chipset,从功能上看,主板共三个部分模块,最上面是CPU,有些主板上直接有CPU,有些主板是提供CPU插槽,CPU是可以外接的。往下面的两个主要部件是MCH和ICH,俗称北桥和南桥。一般的北桥有四个功能模块,第一提供HOST接口,外接CPU;第二是Memory Controller,外接RAM;第三内置显卡和/或外置AGP或PCI Express外置显卡接口;第四个是和南桥之间的接口。从逻辑上来看,一般北桥内有三个PCI设备,Device 0是Host和Memory Controller的相关配置,Device1是AGP或者是PCI Express桥,Device 2是内置的显卡,具体设备号不同的Chipset会有所不同。
现代南桥主要是一个常用外设的集成体,一般,其内部设备都是在BUS 0上的。北桥和南桥之间的接口一般是比较高速的总线,各家有不同的定义,Intel的865/855系列之前是用的Hublink,915系列是DMI,VIA用的是VLINK等等。逻辑上,我们可以把这些类型总线最为芯片内部总线的延伸。一个典型的南桥包括如下功能:LPC桥、硬盘控制器(SATA和PATA)、USB控制器、PCI总线桥和PCI Express总线桥等。LPC桥集中了ISA Legacy部件和电源管理部分。
上图是一个具体主板框图的例子,图中的CPU是支持Banias或者Dothan CPU。北桥是855GME,南桥是ICH4M,北桥集成VGA控制器,输出两路,一路是数字信号的LVDS,一路是模拟信号的CRT接口。内存控制器支持DDR,最高Support DDR 333。Host FSB支持400MHz。
CK408??
南桥PATA部分接出两路,一路是Primary,用来接HDD,一路是Secondary,用来接ODD。USB引出两个独立Controller的Port。PCI总线外接三路,一路接Onboard LAN;一路接Ricoh的CardBus Controller,这个设备是一个多功能设备,本身集成了四合一读卡器;一路接一个Mini PCI Socket,这个Socket可以接入Wireless Lan,或者是TV Card等Mini PCI设备。LPC上接两个设备,一个是EC,一个是SIO。Flash ROM外挂在EC上,所以EC除了解码键盘端口60h/64h和EC端口62h/66h外,还必须解码Flash ROM的内存地址。SuperIO采用的是National Semicondact的PC87381,内含串口控制器和FIR控制器。AC97分成两个部分,一部分是数字部分,内置在南桥内,一部分是模拟部分的AC 97 Codec,AC97在逻辑上是一个PCI设备。
- CPU
- MCH
- ICH
Insyde BIOS架构
现代BIOS,一般采用Flash ROM,常用的是2M/4M/8M bits.我们以4M Flash ROM为例描述BIOS结构。现代BIOS ROM一般直接或者间接接在LPC上,LPC可以配置为把Memory Cycle在E000h和F000段,映射到BIOS ROM的相应高端,同时也可以把相应的(4G-ROM Size)的Memory Cycle也映射到相应的BIOS ROM上。由于CPU一上电的指令指向F000:FFF0,所以这条指令就是BIOS上电的初始指令,这条指令将给Chipset映射到BIOS ROM的相应位置,所以BIOS ROM相应F000:FFF0位置必须存放相应的BIOS初始化代码,这些代码是不可以压缩的。在Insyde BIOS上,这段代码就是放BLOAD.ROM代码,如果Support BootBlock,那么这个地址放置的是BBLOCK.ROM的代码。前面已经讲过,这些没有压缩的代码主要作用就是简单初始化Chipset,然后初始化RAM系统,在RAM系统初始化成功之后,这个模块将把BIOS其他部分的压缩代码解压缩到RAM中间,然后跳到RAM中运行。所以,纵观整个BIOS ROM的代码结构,ROM的地段对应F000:FFF0地方放置非压缩的代码,往下的空间则防止压缩的BIOS代码模块,例如DSDT表和PCI初始化代码等。对于一部分笔记本计算机,为了Cost Down的原因,一般把EC ROM和BIOS ROM合起来使用同一个ROM,例如NS87591L的EC。这样,根据EC解码特性,必须把BIOS ROM最低端的两个段分配给EC使用,这一部分代码是EC的代码,可能不是X86处理器的指令。这样,这个BIOS ROM分成三个部分,最高端存放非压缩BBLOCK和BLOAD代码,最低端存放EC代码,中间是压缩的BIOS代码。
Insyde BIOS ROM生成在BIOS SourceCode的OEM\BIND目录下的MakeBIOS.mak文件中生成,如下方式
romloc: $(ROMLOC_FILES) .\makedir\bios.loc
romloc .\makedir\bios.loc
这段代码使用一个romloc.exe程序,这个根据根据bios.loc文件中定义的各个模块在BIOS ROM中摆放的位置,把各个模块的压缩或者非压缩代码放到BIOS ROM中间,生成BIOS.ROM文件。
BIOS.loc文件由BIOS.lpp生成,其在MakeBIOS.mak中的代码如下:
.\makedir\bios.loc: bios.lpp ..\shared\config.h ..\shared\project.h
set INCLUDE=..\shared;$(INCLUDE)
CL /EP /nologo %s > .\makedir\bios.lo1
delempty .\makedir\bios.lo1 > .\makedir\bios.loc
set INCLUDE=$(INCLUDE)
BIOS.lpp是一个简单的文件,使用C语言的方式定义BIOS.ROM各个模块位置,典型的BIOS.lpp内容如下:
;—————————————————————————-
#include <CONFIG.H>
; BIOS Build Utility
; The ROMLOC program builds a 512KB x 8 BIOS (4MBit) using the following files.
OUTPUT = RELEASE\BIOS.ROM
MAPFILE = RELEASE\ROMLOC.MAP
SIZE = 512K
FILL = FFFF
;— File Name ——– @ ROM ——————- @ RAM
ROMSRC\ECBIOS\FBR.ROM @ 0000 ;;V02+
MAKEDIR\VGA.CHB @ 10000 ; Video BIOS C000:0 //yl012704
;Compressed Video bios size should not exceed 48K Bytes!!!!
MAKEDIR\DISPSEG.CHB @ APPEND,align16 ; DISPSEG
MAKEDIR\IMAGE.CHB @ APPEND,align16 ; IMAGE.PCX
;Intel OSB Code
MAKEDIR\OSB.CHB @ APPEND,align16 ; LOGO.OSB ;Intel OSB module
MAKEDIR\USB.CHB @ APPEND,align16 ; USB BIOS E000:0
MAKEDIR\ACPI.CHB @ APPEND,align16 ; E400:0
MAKEDIR\DSDT.CHB @ APPEND,align16 ; E500:0
MAKEDIR\SCU.CHB @ APPEND,align16
MAKEDIR\MENUENGL.CHB @ APPEND,align16
MAKEDIR\SCUSRVR.CHB @ APPEND,align16
MAKEDIR\MAXSMI.CHB @ APPEND,align16
MAKEDIR\PCI.CHB @ APPEND,align16 ; E800:0
MAKEDIR\PNP.CHB @ APPEND,align16 ; EC00:0
MAKEDIR\BIOS.CHB @ APPEND,align16 ; F000:0
MAKEDIR\PXE.CHB @ APPEND,align16 ; PXE BIOS C800:0
ROMSRC\DMIROM.ROM @ 60000 ;must in 2k boundary
;———–The above code should not exceed @70000————
; Micro code put the following location.
ROMSRC\mcu\m206D210.pdb @ 67000 ;(Dothan) Mobile Pentium-M A1 with CPU Signature 06D1h
ROMSRC\mcu\m206D617.pdb @ APPEND, align16 ;(Dothan) Mobile Pentium-M B1 with CPU Signature 06D6h
;———–The above code should not exceed @7A000————
MAKEDIR\CHBTBLS.ROM @ APPEND, align16
MAKEDIR\BLOAD.ROM @ 70000 ; 16k F200:0
..\BB\MAKEDIR\BBLOCK.ROM @ BOTTOM ; 16k FC00:0
Insyde BIOS是一个结构化的BIOS,Insyde BIOS分成两层,Core层、Chipset层和OEM层。Core主要提供一些成熟的库程序,这些库和Chipset是不相关的,Chipset层和OEM层提供需要Chipset和OEM定制的一些代码。一般Core编译以后是生成库文件,一般都是.lib文件,例如pnp.lib、usb.lib、pcicore.lib。OEM层生成实体代码,一般都是.ROM模块,例如pnp.rom、usb.rom、pci.rom。BIOS的代码结构如下图所示:
Chipset层主要提供一些特殊的特性设置,例如,我们在PM初始化过程中经常需要Enable和Disable Software SMI,但是SMI设置的方法对于不同的Chipset是不同,所以,这部分代码必须由Chipset层提供,hcCS_Enable_SW_SMI。还有一个例子就是BIOS刚上电时需要对Chipset初始化和Memory Size,这两个方法对于不同的Chipset而言也是不一样的,Chipset层提供这两个程序,hjCS_BIO_CHIP_INIT和hjCS_BIO_MEMORY_SIZE,还有的例子就是Memory Shadow的方法不同的Chipset也是不同的。OEM层一些主要程序是Core专门Hook到OEM层,这些Hook测存在使得OEM可以根据自己系统的不同配置而配置自己不同特性,这些例子就比较多了。
这个BIOS,可以看作由两个文件展开的,一个是Bload.asm,一个是Biospine.asm。这两个文件都是Core层的文件,Bload.asm最终被编译到Bload.ROM的模块中间,这个模块是BIOS在ROM运行的模块,文件的主要程序将被编译到压缩的BIOS.ROM。Bload.asm是BIOS.ROM的主文件,这个文件主要通过几个函数Hook到BIOSOEM这个OEM层,这个程序是hjOEM_EARLY_RESET、hjOEM_INITIALIZE_CHIPSET和hcOEM_SHADOW_BIOS。第一个跳入的程序,hjOEM_EARLY_RESET这个程序给OEM客户提供一个在处理器一上电就需要执行的动作,例如保存CPU ID信息、配置Debug口等等配置,hjOEM_INITIALIZE_CHIPSET这个部分内容比较多,一般OEM客户需要在这个程序中间对Chipset做一些前期的初始化,然后做Memory Sizing和Clock Generator的编程等等。hcOEM_SHADOW_BIOS这个程序把各个BIOS功能模块解压到RAM中间,而后把E000h和F000h段Shadow到RAM中。Shadow之后,BIOS跳到RAM中运行,最终运行到BIOSPINE.asm中间。
Bload.asm中间跳转语句是
jmpf BIOS_Segment, BIOS_Reset ; Jump to the reset pointer
在Bload.equ中间这两个变量是这样定义的:
BIOS_Segment equ 0F000h ; BIOS segment
BIOS_Reset equ 0FFF0h ; BIOS Reset offset
所以这条jmpf语句将跳到F000:FFF0h
Shadown之后,压缩的BIOS.ROM将被解压缩到F000h段的RAM中间,F000段的排列是这样的。BIOS.ROM文件的ORG文件是BIOSORG.ASM,其中这样定义F000:FFF0h
ORG_POWER_ON_JUMP_MACRO
这个宏定义在atorg.equ中定义
ORG_POWER_ON_JUMP_MACRO MACRO NOARGS
ORG 01FF0H ;
JMPF 0F000H, 0E05BH ; This makes CS for all ROM code F000h
ENDM ;
E05Bh处代码也在BIOSORG.asm中定义
ORG 0005BH
PUBLIC ORG_RESET_JMP ; Used By CTRL-ALT-DEL In INT 09 Handler;
ORG_RESET_JMP: ;
JMPF 0F000H, CPU_RESET ; Reset CS and Jump to CPU reset
这里的CPU_RESET就是BIOSSPINE.asm开头,这样
压缩的BIOS.ROM由几个部分链接而成,这几个部分是
恍恍忽忽
BIOSPINE.ASM前面的程序都跳到OEM层的BIOSOEM中程序,例如,
hjCS_BIO_CHIP_INIT,跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
hscOEM_PCI_EARLY_RESET_INIT跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM
hcOEM_START_SIMPLE_BOOT跳到CMOS.ASM,之后返回到BIOSPINE.ASM
HWIO_RESET_CTC1跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
HWIO_TICKLE_RAM跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
RAM_INIT_FIRST_64K跳到RAMPRIM1.ASM,之后返回到BIOSPINE.ASM
HWIO_RESET_INTS跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
HWIO_ENABLE_APIC跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
hcOEM_BIO_TEST_DMA_PAGE跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM
HWIO_TEST_DMA_PAGE跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
KEYB_SELFTEST_CTLR跳到KEYBPRIM.ASM,之后返回到BIOSPINE.ASM
KEYB_RESET_KEYBOARD跳到KEYBPRIM.ASM,之后返回到BIOSPINE.ASM
POST_Check_CMOS_RAM跳到POSTPRIM.ASM,之后返回到BIOSPINE.ASM
POST_TEST_BATT_CMOS_SUM跳到POSTPRIM.ASM,之后返回到BIOSPINE.ASM
hcOEM_BIO_CMOS_SUM跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM
HWIO_TEST_DMA跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
HWIO_INIT_8237跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
hcOEM_BIO_FAST_BOOT_REQUESTED跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM
GEN_INIT_VECS跳到GENPRIM.ASM,之后返回到BIOSPINE.ASM
hcOEM_BIO_FAST_BOOT_REQUESTED跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM
RAM_QUICK_SIZE跳到RAMPRIM1.ASM,之后返回到BIOSPINE.ASM
hcCS_BIO_SHADOW_SETUP跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
hcCS_Enable_Cache跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
hcOEM_PCI_MAIN_INSTALL跳到OEMPRIM.ASM中间,中间引用PCI_INIT,跳到PCIPRIM.ASM中间,接着以call dword ptr es:[bx]这样的方式跳进PCI.ROM中间运行,而后返回。
hcOEM_PCI_VIDEO_DETECT跳到OEMPRIM.ASM中间,使用call dword ptr es:[BX] 跳进PCI.ROM中间运行,而后返回。
hcOEM_PNP_INIT跳到OEMPRIM.ASM中间,使用CALLF PNP_ROM_SEGMENT, 0003h语句跳入PnP.ROM中间运行,而后返回。
hcOEM_DECOMP_OSB跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
hcOEM_SPLASH_BIGLOGO跳到OEMPRIM.ASM中间,使用CALLF DISP_FarCall_SEG, DISP_FarCall_OFF跳进DISPSEG.ROM中间运行,而后返回。
hcOEM_GRAPHIC_POST跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
hcOEM_TrapInt10跳到OEMINT.ASM,之后返回到BIOSPINE.ASM。
TXT_SSPRINTF跳到TEXTPRIM.ASM中,引用_SSPRINTF,再调用_PRINTF跳到DISPPRIN.ASM,然后以CALLF DISP_FarCall_SEG, DISP_FarCall_OFF方式跳入DISPSEG.ROM中间运行,而后返回。
hcOEM_BIO_CONFIG_KBD_CTL跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
KEYB_PRESENT_TEST跳到KEYBPRIM.ASM,之后返回到BIOSPINE.ASM
KEYB_TEST_OBF_INT跳到KEYBPRIM.ASM,之后返回到BIOSPINE.ASM
KEYB_TEST_CMD_BYTE跳到KEYBPRIM.ASM,之后返回到BIOSPINE.ASM
hcCS_INT15_DISABLE_A20跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
HWIO_TEST_PERIODIC跳到HWIOPRIM.ASM,之后返回到BIOSPINE.ASM
GEN_INIT_RAM_DATA跳到GENPRIM.ASM中间,之后返回BIOSPINE.ASM
GEN_INIT_HARD_VECS跳到GENPRIM.ASM中间,之后返回BIOSPINE.ASM
PM_USB_HC_INIT跳到BIOSUSB.asm中间,通过call trigger_SYS_SMI_FN陷入SMI中间,之后返回。
PM_ACPI_INIT跳到PMPRIM.ASM中间,之后callf ACPI_ROM_SEGMENT, ACPI_ROM_INIT_VECT的方式,跳入ACPI.ROM。
PM_CPU_INIT跳到PMPRIM.ASM中间,通过call trigger_SYS_SMI_FN陷入SMI中间,之后返回。
INITTOD跳到TOD.ASM中间,之后返回BIOSPINE.ASM
hcOEM_InitTOD跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
hcOEM_BIO_DEVICE_CONFIG跳到OEMPRIM.ASM,中间调用hcOEM_SUPERIO_CONFIG,跳到OEMCORE.ASM中,然后使用CALLF PNP_ROM_SEGMENT, PNP_FARCALL_VECTOR跳进PnP.ROM。
HWIO_FIND_COMM_PORTSHWIOPRIM.ASM,之后返回到BIOSPINE.ASM,
hcOEM_PCI_IDE_DETECT跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
WINI_INITIALIZE跳到harddisk.ASM,之后返回到BIOSPINE.ASM。
hcOEM_POST_WINI_INIT跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
hcOEM_PCI_MAIN_CONFIG跳到OEMPRIM.ASM,使用call dword ptr es:[BX] 跳进PCI.ROM中间运行,而后返回。
hcOEM_Prepare_Addon_Card跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
hcOEM_PCI_ROMINIT跳到OEMPRIM.ASM,使用call dword ptr es:[BX] 跳进PCI.ROM中间运行,而后返回。
hcOEM_Check_Addon_Card跳到OEMPRIM.ASM,之后返回到BIOSPINE.ASM。
hcOEM_SetBootFirst跳到OEMPRIM.ASM中间,使用CALLF DISP_FarCall_SEG, DISP_FarCall_OFF跳进DISPSEG.ROM中间运行,而后返回。
PM_PM_Init跳到PMPRIM.ASM中间,通过call trigger_SYS_SMI_FN陷入SMI中间,之后返回。
在POST的最后,有一个表格驱动的方式调用,
MOV BX, OFFSET HOOK_LAST_MINUTE_INIT_TABLE
NEXT_HOOK: ;
CMP WORD PTR CS:[BX], -1 ; -1 Means End Of Hook Functions
JE LAST_HOOK ;
PUSH BX ; Dispatch To OEM Hook From Table
CALL WORD PTR CS:[BX] ; BX Holds Index Into Table
POP BX ;
INC BX ; Increment To Next Hook In Table
INC BX ;
JMP NEXT_HOOK ; Go Around To See If Another
LAST_HOOK: ;
其中表格是如下定义:
HOOK_LAST_MINUTE_INIT_TABLE LABEL WORD
DW offset CS:hcCS_WP_ShadowRAM
DW offset hcCS_Final_Chip_Init
DW offset hcOEM_Equipment_Banner
DW offset hcOEM_Gen_Setup
DW offset hcOEM_WinNTCMOSPatch
DW offset hcOEM_Y2K_Update
DW -1 ; -1 ENDS THE LIST
hcCS_WP_ShadowRAM跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
hcCS_Final_Chip_Init跳到CSPRIM.ASM中间,之后返回BIOSPINE.ASM
hcOEM_Equipment_Banner跳到OEMCORE.ASM,之后返回BIOSPINE.ASM。
hcOEM_Gen_Setup跳到OEMPRIM.ASM,之后调用biosInitUsbMassDevice,进入BIOSUSB.ASM,其中使用CALLF USB_ROM_SEGMENT, USB_ROM_OFFSET方式,陷入USB.ROM。
hcOEM_WinNTCMOSPatch跳到OEMCORE.ASM,之后返回BIOSPINE.ASM。
hcOEM_Y2K_Update跳到OEMPRIM.ASM,之后返回。
在POST最后,跳用INT19,Boot OS。
从上面BIOSPINE.ASM中对于子程序的引用看来,可以分为四种情况:
- Procedure调用继续在Core层。
- HWIO_RESET_CTC1
- HWIO_TICKLE_RAM
- Procedure调用进入OEM层,但是始终在BIOS.ROM中运行。
- Procedure调用进入OEM层,但是最终进入其他ROM块。
- Procedure跳用进入OEM层,最后进入SMI程序。
- Procedure使用软中断的方式,但是在Core层运行。
下面我们从功能角度分析一个一个模块。
PM.ROM:
首先分析SMI模块。SMI的Core层是PMCORE,OEM层是PMOEM。类似于BIOSSPINE是BIOS.ROM的主程序,STRAT.ASM是PM.ROM的主程序。STRAT.ASM几个主要Procedure的调用是
jCS_SMI_entry
jCS_cpu_spd_fast
CS_map_ACPI_SMI
_SMI_map_SWSMIfn
jCS_SWSMI_override_fn
_SMI_fnSWSMI
cCS_Allow_HWSMI_during_CE
cCS_SMI_map_fn
_SMI_fnSMI
hw_smi_service_top
jCS_cpu_spd_exit
jCS_SMI_rest_chip
jCS_SMI_exit
CS_map_ACPI_SMI跳进ACPICHIP.ASM,这个程序主要处理关于ACPI的几个SMI,这几个SMI都是由B2/B3口写命令和命令码所触发的SMI。其中,B2是命令口,这里处理的命令包括:
E0——ACPI Enable
E1——ACPI Disable
E2――ACPI S4 Request
E3――ACPI OEM Function:这里主要是根据B3口的命令码,确认不同的SMI Function。
这个部分还加入另外一个部分的处理,就是处理80h的命令,80h的命令主要是根据Intel SpeedStep提供SMI Function。我们知道,SpeedStep的操作又两种方式,一种是FixedHWIO方式,这种方式是让OS的Drive来操作,另外一种就是OS本身没有Driver,只有使用SMI的方式,这里就是提供的这些方法。
ACPI SMI部分的处理方式主要是使用表格驱动方法,ACPI表如下定义
ACPI_SERVICE_TBL label word
SMI_ENTRY <ACPI_OEM_DSPTCH ,offset CS_ACPI_OEM_DSPTCH,SET_CARRY_FLAG>
SMI_ENTRY <FACP_ACPIEN ,offset CS_ACPI_MODE_EN ,SET_CARRY_FLAG>
SMI_ENTRY <FACP_ACPIDIS ,offset CS_ACPI_MODE_DIS ,NO_CARRY_FLAG >
SMI_ENTRY <FACP_S4BIOSRQ ,offset CS_ACPI_S4BIOS_REQ,NO_CARRY_FLAG >
ACPI_TBL_LEN dw $ – ACPI_SERVICE_TBL
SMI_ENTRY的宏定义是这样的,
SMI_ENTRY STRUCT
SMI_TYPE db ? ; will store the SMI value in reg. 0b2h
CALL_FUNC dw ? ; call function for this particular SMI
RET_FLAG db ? ; return flag after servicing this SMI
SMI_ENTRY ENDS
首先,把B2读出的命名码和SMI_TYPE的值比较,然后跳用相应的处理函数。对于第一个函数,CS_ACPI_OEM_DSPTCH,这个函数处理OEM自己在ACPI表中定义的功能,这个函数会根据B3口读出的数据,调用相应ACPI_OEM_FUNC_TBL的功能。
ACPI_OEM_FUNC_TBL
DW OFFSET ACPI_SMI_IO_REQ ; Function 0
DW OFFSET ACPI_SMI_RESTORE ; Function 1
DW OFFSET ACPI_OEM_REQ ; Function 2
DW OFFSET Set_S4_Record ; Function 3 Make
DW OFFSET Clear_S4_Record ; Function 4
DW OFFSET pmRp0xRestore ; Function 5
DW OFFSET ACPI_CONFIG_IrDA ; Function 6
另外,在CS_map_ACPI_SMI跳进ACPICHIP.ASM还处理CPU的SMI,这个几个命令的定义如下:
OS_COMMAND EQU 080h ; OS indicates IST control.
INIT_COMMAND EQU 081h ; BIOS indicates IST init.
APP_COMMAND EQU 082h ; Applet indicates IST control.
OS_REQUEST EQU 083h ; OS transition request.
CSTATE_COMMAND EQU 085h ; OS indicates C state control.
_SMI_map_SWSMIfn、jCS_SWSMI_override_fn和_SMI_fnSWSMI这三个函数又来处理BIOS Trigger SMI、APM SMI等。其中_SMI_map_SWSMIfn提供对于不同入口的参数功能分析,jCS_SWSMI_override_fn运行BIOS对这些功能重载,_SMI_fnSWSMI是相应功能处理的实体。
这个部分是的SMI是通过往B2口写1产生的,进入以后,ACPI部分检查命令码1不是ACPI命令码,所以就归为这个部分处理。这个部分主要入口参数是EDI的值,EDI的高16位必须是0BEEFh。
EDI的低8位的相应BIT表示这个部分请求的功能,这些BIT定义如下:
CORRECT_TIME EQU 01H ; Time correction flag
SYS_INIT EQU 02H ; Ext-Smi (menu) generation
PM_LOADED EQU 04H ; Set if loaded…
APM_FUNCTION EQU 08H ; Apm function generation
OEM_FUNCTION EQU 10H ; OEM function generation
SYS_FUNCTION EQU 20H
BIOS处理需要一个Map的过程,根据相应的BIT,map到一个值,Map的方式如下
SYS_INIT :SWSMI_BIO_INIT :03h
SYS_FUNCTION :由EBX的本身定义
APM_FUNCTION :SWSMI_BIO_APM :02h
OEM_FUNCTION:SWSMI_BIO_OEM :08h
然后,BIOS根据映射值调用相应的函数:
CS_SWSMI_table DW OFFSET SW_rsm_SWSMI ; 0
DW OFFSET SW_internal_SWSMI ; 1
DW OFFSET SW_APM_SWSMI ; 2
DW OFFSET SW_bios_init_SWSMI ; 3
DW OFFSET CS_bios_spdchg_SWSMI ; 4
DW OFFSET SW_bios_vidchg_SWSMI ; 5
DW OFFSET OEM_Restore_State ; 6
DW OFFSET OEM_Save_CPU_State ; 7
DW OFFSET OEM_swsmi_function ; 8
DW OFFSET _PCMCIA_register ; 9
DW OFFSET _PCMCIA_suspend ; A
DW OFFSET _Resume_0v ; B
DW OFFSET CS_Save_S2D_Resume_State ; C
DW OFFSET CS_Resume_System ; D
DW OFFSET CS_Geyserville_Init ; E
DW OFFSET CS_USB_HC_INIT ; F
DW OFFSET DMI_FLASH_FUNC ; 10h
cCS_SMI_map_fn和_SMI_fnSMI是用来处理硬件SMI的,这两个函数都将引用到SMICHIP.ASM这个文件中,SMICHIP.ASM这个文件属于PMOEM部分。我们知道对于Intel的ICHX,系统所有硬件发出的SMI,将相应置位SMI_STS,所以SMI部分处理硬件中断必须首先Check这些寄存器,然后根据相应的置位状况,做相应的处理。cCS_SMI_map_fn检查是BIT几被置位,_SMI_fnSMI处理相应的BIT功能。_SMI_fnSMI主要使用表格驱动的方式处理各个硬件SMI,这个表格是CS_HWSMI_table,这个表格定义在SMICHIP.ASM中间,定义如下:
CS_HWSMI_table
DW OFFSET CS_NULL_Hook ; Bit 0
DW OFFSET CS_NULL_Hook ; Bit 1
DW OFFSET CS_NULL_Hook ; Bit 2
DW OFFSET TMP_CS_USB_Request ; Bit 3
DW OFFSET CS_SLP_SMI_Hook ; Bit 4
DW OFFSET CS_NULL_Hook ; Bit 5
DW OFFSET CS_SWSMI_TMR_Hook ; Bit 6
DW OFFSET CS_NULL_Hook ; Bit 7
DW OFFSET CS_PM1_Request ; Bit 8
DW OFFSET CS_GPE0_Request ; Bit 9
DW OFFSET CS_GPE1_Request ; Bit 10
DW OFFSET CS_NULL_Hook ; Bit 11
DW offset CS_DEVMON_Request ; Bit 12
DW offset CS_TCO_Request ; Bit 13
DW OFFSET CS_1MIN_Request ; Bit 14
DW OFFSET CS_SERIRQ_Hook ; Bit 15
DW OFFSET CS_NULL_Hook ; Bit 16
DW OFFSET ehciSmiHandler ; Bit 17 Legacy USB2
DW OFFSET CS_NULL_Hook ; Bit 18 Intel USB2
DW OFFSET CS_NULL_Hook ; Bit 19
DW OFFSET CS_NULL_Hook ; Bit 20
DW OFFSET CS_NULL_Hook ; Bit 21
DW OFFSET CS_NULL_Hook ; Bit 22
DW OFFSET CS_NULL_Hook ; Bit 23
DW OFFSET CS_NULL_Hook ; Bit 24
DW OFFSET CS_NULL_Hook ; Bit 25
DW OFFSET CS_NULL_Hook ; Bit 26
DW OFFSET CS_NULL_Hook ; Bit 27
DW OFFSET CS_NULL_Hook ; Bit 28
DW OFFSET CS_NULL_Hook ; Bit 29
DW OFFSET CS_NULL_Hook ; Bit 30
DW OFFSET CS_NULL_Hook ; Bit 31
CS_HWSMI_table_len DB ($-CS_HWSMI_table)
其中,
TMP_CS_USB_Request跳入USBCORE的UHCISMI.ASM中间,注意,虽然这个部分代码在USBCORE中间,但是这部分代码实际上是编译到PM.ROM中的,这个部分代码主要是重新扫描USB总线,检查是否有设备的插拔。
CS_SLP_SMI_Hook跳入SMITRAP.ASM中间,这个SMI是在系统进入SX的Sleep时候跳用,这个部分我们主要做的处理是在S3的时候保存部分设备寄存器的值,在S3 Resume的时候在写回去。
CS_SWSMI_TMR_Hook这是SMI定时器产生的中断,一般这个部分是用来处理HT CPU的CX状态。这部分代码跳入Chipset层的PPMHT.ASM文件。
CS_PM1_Request,这个SMI主要是在DOS下按Sleep Button的处理,因为这里的PM1的State会相应置位。
CS_GPE0_Request,这个部分代码主要处理又GPIO0产生的SMI,目前的代码一般都会处理Thermal Pin的动作,主要处理放在在PMOEM.ASM中的OEM_Process_Thermal主要是使用Throttle的方式。另外,一般我们的EC接到GPE0的GPIO Pin脚上,这里,一般我们的Fn+F?的处理都是在这里。
CS_GPE1_Request,这个部分代码主要处理又GPIO1产生的SMI,由于目前我们的Chipset都没有支持GPE1,所以这个函数都没有实现。
CS_DEVMON_Request,这个代码是读写IO地址Trap产生的SMI,这个定义在RCBA+1E00的TRCR寄存器。
CS_TCO_Request是TCO Timer产生的SMI,目前主要处理Y2K问题。
CS_1MIN_Request这是又一个周期性SMI时钟产生的SMI,这里主要处理DOS下一分钟电源处理问题。
CS_1MIN_Request这个出要处理SIRQ#产生的中断,这部分代码目前是空的。
ehciSmiHandler这个部分跳到跳入USBCORE的EHCISMI.ASM中间,注意,虽然这个部分代码在USBCORE中间,但是这部分代码实际上是编译到PM.ROM中的。
在主入口中间还有两个函数,jCS_cpu_spd_fast和jCS_cpu_spd_exit,目前,对于Intel的CPU,这两个函数都是空的。
下面我们研究PM.ROM的组成,在PMOEM中,PM_OEM.MAK中这样定义PM.ROM:
pm.rom: $(MISCMAKE_DIR)\misc.lib \
$(USBINIT_FILES) \
$(ISTMAKE_DIR)\htgv.lib \
pm.lib
echo PM_ORG EQU 0H > $(MAKE_DIR)\org.equ
ML $(SOURCE_DIR)\pmlink.asm
LINK @<<
pmlink +
$(**: = +^
),
pm.exe,
pm.map,
$(COMMON_DIR)\usb.lib;
<<
PMFILPAT pm.exe $(SOURCE_DIR)\pmfiller.asm org.equ 16 $(BIND_DIR)\ROMCFG\MAXSMI.CFG
ML $(SOURCE_DIR)\pmlink.asm
ML $(SOURCE_DIR)\pmfiller.asm
LINK @<<
pmlink +
$(USBINIT_FILES) +
$(ISTMAKE_DIR)\htgv.lib +
$(ISTMAKE_DIR)\acpicode.obj +
$(MISCMAKE_DIR)\misc.lib+
pmfiller +
pm.lib,
pm.exe,
maxsmi.map,
$(COMMON_DIR)\usb.lib;
<<
EXE2ROM pm /X
其中pmlink.asm的主要作用是ORG,PM.ROM的几个主要部分如下:misc.lib、usbinit.lib、htgv.lib和pm.lib。其中,主要部分是pm.lib,其中pm.lib的make语句如下:
pm.lib: $(CORE_DIR)\strat.obj \
$(CORE_DIR)\hwshell.obj \
$(CORE_DIR)\swshell.obj \
$(CORE_DIR)\devicex.obj \
smichip.obj \
acpichip.obj \
smichip1.obj \
oemsmi.obj \
$(CORE_DIR)\vpm.obj \
$(CORE_DIR)\apmsmi.obj \
pmoem.obj \
pmoem1.obj \
$(CORE_DIR)\pmdata.obj \
$(CORE_DIR)\pcmcia.obj \
$(CORE_DIR)\gfx_pm.obj \
pmcustom.obj
if EXIST $@ del $@
LIB /nologo @<<
$@ &
+$(**: = &^
+);
<<
可见pm.lib由两部分组成,一部分是PMCORE部分程序,一部分是PMOEM程序,PMCORE部分程序是:
PMCORE部分程序是:strat.obj、hwshell.obj、swshell.obj、devicex.obj、vpm.obj、apmsmi.obj、pmdata.obj、pcmcia.obj和gfx_pm.obj。
PMOEM部分程序是:smichip.obj、acpichip.obj、smichip1.obj、oemsmi.obj、pmoem.obj、pmoem1.obj和pmcustom.obj。
PMCORE部分的Strat.obj的SourceCode是Strat.asm,这个程序是PM部分的主程序,SMI的Dispatch就在这个文件中。
hwshell.obj的SourceCode是hwshell.asm,编译中还有atdevice.asm。
swshell.obj的SourceCode是swshell.asm,这个文件主要包含了几个SW SMI调用子程序表中的程序,包括:SW_rsm_SWSMI、SW_internal_SWSMI、SW_bios_init_SWSMI、SW_bios_vidchg_SWSMI,其余的部分定义在其他文件中。
devicex.obj的SourceCode是devicex.asm,这个程序主要提供一些设备Power、Enable和初始化等程序,供swshell.asm和APMSMI.asm部分的代码引用。
vpm.obj的SourceCode是vpm.asm,这部分代码主要是提供Video操作的库,被系统OEM文件直接引用,例如S3 Resume对Video初始化等。
apmsmi.obj的SourceCode是apmsmi.asm,主要是提供APM部分的代码,这些代码被一个表格驱动的SW SMI引用。
gfx_pm.obj的SourceCode是gfx_pm.asm,主要是提供S2D部分UI显示部分的代码。
pmdata.obj的SourceCode是pmdata.asm,主要是提供PM部分的一些标示变量,供PM代码部分所有文件引用。
pcmcia.obj的SourceCode是pcmcia.asm,主要是提供PM部分的PCMCIA部分的管理,例如Suspend时应该对PCMCIA的处理。
PMOEM的各个功能模块的描述如下:
smichip.obj的主程序文件是smichip.asm,这个部分主要是配合strat.asm,提供一些全局性的程序入口,例如SW Function Table、HW Function Table和SMI Entry Routine等。
acpichip.obj的主程序文件是acpichip.asm,这部分的代码主要是提供ACPI SMI的入口以及主要Routine。
smichip1.obj部分的主程序是smichip1.asm,其中的很多内容属于SMICHIP1的扩展,提供很多HWSHELL.ASM的Chipset和OEM相关层的内容,特别是进入Suspend的Save和Restore动作。
oemsmi.obj这个部分主要放的程序是oemsmi.asm,其中主要是Sleep Trap入口所做的动作给OEM层的接口。
pmoem.obj的主程序文件是pmoem.asm,和pmoem1.asm一起,提供PM部分的OEM Routine的Hook点。
pmcustom.obj的主程序文件是pmcustom.asm,这个部分主要功能是提供一个OEM可以定义的Routine。
PCI.ROM
下面我们讨论PCI.ROM,这个模块,我们知道BIOS.ROM中有五个地方会跳用这个ROM里的Routine:hcOEM_PCI_VIDEO_DETECT、hcOEM_PCI_IDE_DETECT、hcOEM_PCI_MAIN_CONFIG、hcOEM_PCI_ROMINIT和hcOEM_PCI_MAIN_INSTALL。所以,下面我们重点分析PCI.ROM这个模块。
PCI.ROM的MAK文件如下:
pci.rom: $(COMMON_DIR)\pcicore.lib \
$(PCIEMAKE_DIR)\pcie.lib \
pci32.obj \
pcioem.obj \
pcics.obj
LINK @<<
$(**: = +^
),
pci.exe,
pci.map ;
<<
EXE2ROM pci /S:5000
PCICORE.lib的mak语句如下:
pcicore.lib: $(SOURCEFILES:.asm=.obj)
if EXIST $@ del $@
LIB /NOLOGO @<<
$@ &
+$(**: = &^
+);
<<
所以PCICORE.lib是有PCICORE目录下的所有.ASM文件编译而成。这部分的主程序是PCICORE.ASM这个文件。
PCICORE定义了这个ROM的入口,根据AL的值跳用不同的功能,这些功能定义如下:
DW LOADPCI_RM ;0
DW PCI_VIDEO ;1
DW PCI_IDE_HUNT ;2
DW PCI_CONFIG ;3
DW OEM_DISPATCH ;4
DW PCI_ROMINIT ;5
五、开始新平台开发
如何阅读新平台的Datasheet与Spec
Sonoma平台
- MCH 915M Alviso EDS
- ICH6M EDS
- MCH 915M Alviso BIOS SPEC
- MCH 915M Alviso BIOS Porting Guide
- ICH6M BIOS SPEC
- ICH6M BIOS Porting Guide
- CPU datasheet
- 更新技术资料
如何写软件开发计划书
如何写BIOS Spec
如何做一个新项目BIOS
1 按照原理图、软硬件接口规范来配置BIOS
BIOS,作为硬件中的软件,和系统硬件紧密相关的。所以,如果把工板BIOS修改到我们的机型上,必须相应的配置,以符合我们项目的实际配置情况。
配置系统,首先我们需要明确的是我们系统几个主件的配置:CPU、北桥和南桥。特别是这些不同组件之间的细微差别。我们知道X86结构的处理器,主要是特征和信息由三个部分得到:内容空间(一般占据4G高端)、MSR和CPUID指令,不同类型的CPU,这些寄存器的使用方式不一样,如果我们不是针对不同种类的CPU采用特殊的配置,那么系统一定会出问题,例如BIOS POST不通过,或者电源管理失效,性能下降等问题,所以,我们一定把CPU的类型配置对。另一个对BIOS或者说对系统更重要,工作量最多的就是南北桥的配置一定要求正确。北桥的主要和内容控制器、内外置显卡、系统支持什么样CPU和南桥通讯部分相关,例如系统的PAR寄存器、PCIE虚拟通道配置、内存电源管理等部分、显卡电源管理,不同北桥这些寄存器都是不同,所以我们一定要配置对北桥。南桥主要和系统的IO设备有关,所以,正确的配置南桥,保证系统各种接口能否使用。这些配置不同决定了LPC解码方法、SMI Trap和SATA支持等。
除了几个大件以外,还有两个重要的组件也需要小心处理。一个是SuperIO,另一个EC。SuperIO主要包括了一些传统ISA的一部分功能,例如串口、并口和红外等功能。如果不正确配置,这些功能将无法实现。IO的Compatible PnP配置口,随着硬件的上拉等方式不同,其地址也是不同,我们一定要根据我们板子的配置去设定具体的IO地址。EC主要和键盘、电池和AC部分功能相关,所以我们要根据我们系统的具体实施,以及和EC工程师沟通,小心处理这几个部分内容。
除了系统几个主要部件配置以外,我们还需要配置几个主要功能,包括PCI系统配置、PnP系统配置、电源管理系统配置以及USB的配置。对于目前的BIOS属于PCI系统的BIOS,所以我们一定要支持PCI系统,而且笔记本系统一定使用板载PCI显卡,所以这个特征也要配置,如果系统支持PCI Express,就要配置PCI Express支持等等。除了配置支持Feature以外,另一个更重要的特征就是配置PCI的中断Routing部分,特别是传统使用的PIRQ表,ACPI部分PCI中断Routing,我们将在后面ACPI部分详细描述。
PnP部分主要包括是否支持PnP、是否支持DMI、是否支持PnP ISA等等Feature配置、由于现代系统BIOS都是从PnP系统BIOS演化来的,特别是为了支持Win 98等系统和支持SuperIO部分,我们一般都会配置这个功能。由于PnP ISA已经逐步从历史退出,特别是对于笔记本系统,更不可能有PnP ISA设备,所以一般我们不会配置PnP ISA特征的,这个特征主要是给一些比较古老的工控系统使用,由来配置支持PnP ISA设备的枚举和配置空间分析使用的。IPL是PnP BIOS规范定义的一个特征,一般笔记本BIOS都是不支持这个特征的。
电源管理部分是笔记本BIOS的最重要部分,所以相关的三个部分必须明确。这三个方面包括是否支持PM、是否支持APM和是否支持ACPI和ACPI2.0三个方面。一般而言,这些特征都是需要支持的。
USB的支持配置主要是配置USB控制器协议类型,是OHCI的还是UHCI的,一般Intel的都是UHCI的。另外,还需要的配置就是配置支持几个控制器和支持的USB设备类型。这些支持一般都是DOS下和POST过程中的支持,到了OS下这些配置是失效的。
还有一部分内容就是POST过程中间的内存使用配置和BootBlock的结构配置。由于BIOS初始化的时候没有内存管理程序加载,所以这一部分必须由BIOS工程师在编程的时候小心处理,主要配置保证这些内容在使用的时候不会被互相冲掉,否则调试的时候Debug需要很长的时间。关于BootBlock部分更是非常细致。
问题域 |
问题 |
变量 |
描述 |
CPU |
CPU类型 |
CONFIG_P4_SUPPORT |
|
CONFIG_BANIAS_SUPPORT |
|||
CPU Feature |
CONFIG_HT |
|
|
CONFIG_GEYSERVILLE_SUPPORT |
|||
CONFIG_XD_SUPPORT |
|||
CPU 微码 |
|
|
|
北桥 |
北桥类型 |
CFCM_NBR_ALVISO |
|
CFCM_NBR_SOLANO |
|||
CFCM_NBR_ODEM |
|||
南桥 |
南桥类型 |
CONFIG_ICH4_SUPPORT |
|
CONFIG_ICH4M_SUPPORT |
|||
CONFIG_ICH6_SUPPORT |
|||
CONFIG_ICH6M_SUPPORT |
|||
SuperIO |
是否有? |
CONFIG_SUPERIO_SUPPORT |
|
SuperIO类型 |
CONFIG_NS87393 |
||
CONFIG_ITE8712 |
|||
功能 |
CONFIG_EPP_SUPPORT |
||
CONFIG_ECP_SUPPORT |
|||
CONFIG_FIR_SUPPORT |
|||
CONFIG_COMX_FREE |
|||
EC |
类型 |
CONFIG_KBC_H8 |
|
CONFIG_INTEL_KBC_H8 |
|||
CONFIG_KBC_NS591 |
|||
Touch Pad |
CONFIG_PS2_MOUSE |
||
CONFIG_IRQ12_REMOVABLE |
|||
系统中断 |
APIC支持 |
CONFIG_IOAPIC |
|
PCI配置 |
PCI架构 |
CONFIG_PCI_SUPPORT |
|
PCI显卡 |
CONFIG_ONBOARD_PCI_VIDEO |
||
PCI Docking |
CONFIG_DOCKING_SUPPORT |
||
PCI Express |
CONFIG_PCIE_SUPPORT |
||
PnP配置 |
PnP Support |
CONFIG_PNP_SUPPORT? |
|
PnP ISA |
CONFIG_PNP_ISA_SUPPORT |
||
PnP IPL |
CONFIG_PNP_IPL_SUPPORT |
||
Unhook INT15 |
CONFIG_PNP_UNHOOK_INT15 |
||
DMI |
CONFIG_PNP_DMI_SUPPORT |
||
USB配置 |
支持USB |
CONFIG_USB_SUPPORT |
|
UHCI |
CONFIG_UHCI_SUPPORT |
||
EHCI |
CONFIG_EHCI_SUPPORT |
||
OHCI |
CONFIG_OHCI_SUPPORT |
||
第X个Controller |
CONFIG_X_USB_CONTROLLER |
||
支持USB FDD |
CONFIG_USBFDD_SUPPORT |
||
支持USB CD |
CONFIG_USBCD_SUPPORT |
||
支持USB HDD |
CONFIG_USBHD_SUPPORT |
||
支持USB ZIP |
CONFIG_USBZIP_SUPPORT |
||
支持USB Hub |
CONFIG_USBHUB_SUPPORT |
||
电源管理配置 |
PM |
CONFIG_PM_SUPPORT |
|
APM |
CONFIG_APM_SUPPORT |
||
ACPI |
CONFIG_ACPI_SUPPORT |
||
ACPI20 |
CONFIG_ACPI20_SUPPORT |
||
S4BIOS |
CONFIG_S4BIOS_SUPPORT |
||
RAM |
Bank |
CONFIG_64Mbit_DRAM |
|
Shared Memory |
CONFIG_SHARED_MEMORY_SUPPORT |
||
Up 64M |
CONFIG_64M_RAM_SUPPORT |
||
Up 512M |
CONFIG_512M_RAM_SUPPORT |
||
Up 1G |
CONFIG_1G_RAM_SUPPORT |
||
Up 4G |
CONFIG_4G_RAM_SUPPORT |
||
ROM结构 |
|
|
|
|
|
||
|
|
||
|
|
||
|
|
||
|
|
2 调试
-
-
xi. Windows Boot OK
-
x. DOS Post OK
-
ix. IDE Post Pass
-
viii. USB Post Pass
-
vii. PnP Post Pass
-
vi. VGA调试通过
-
v. PCI Routing调试通过
-
iv. Keyboard test Pass
-
iii. Memory Sizing Pass
-
ii. 第一条指令打通
-
i. 电源调试通
-
3 ACPI设计
1 结构空间
整个ASL名字空间如下:
Root
|—-\_PR
| |—-CPU0
|—-\_S0
|—-\_S3
|—-\_S4
|—-\_S5
|
|—-\_PTS
|—-\_WAK
|
|—-\_PIC
|
|—-\_GPE
|
|—-\_TZ
|
|—-\_SB
|—-ADP1
| |—-_PSR
| |—-_PCL
|—-BAT1
| |—-_HID
| |—-_STA
| |—-_BIF
| |—-_BST
| |—-_BTP
| |—-_PCL
|
|—-LID
| |—-_HID
| |—-_LID
| |—-_PRW
|
|—-PWRB
| |—-_HID
| |—-_PRW
|
|—-SLPB
| |—-_HID
| |—-_PRW
|
|—-PCI0
|
|—-\_INI
|—-\_SXD(X=134)
|—-\_HID
|—-\_CID
|—-\_ADR
|—-\_CRS
|—-\_PRT
|
|—-\PEGP
| |_ADR
| |_PRT
|
|—-\VGA
| |—-\_ADR
| |—-\_DOS
| |—-\_DOD
| |—-\_ROM
| |—-\_LCD
| | |—-\_ADR
| | |—-\_DCS
| | |—-\_DGS
| | |—-\_ASS
| |—-\_CRT
| |—-\_ADR
| |—-\_DCS
| |—-\_DGS
| |—-\_ASS
|—-\ AZAL
| |—-\_ADR
| |—-\_PRW
|—-\RP0X(X=1234)
| |—-\_ADR
| |—-\_PRW
| |—-\_PRT
| |—-\PXS1
| |—-\_ADR
| |—-\_RMV
|
|—-\USBX(X=01237)
| |—-\_ADR
| |—-\_PRW
| |—-\_PSW
| |—-\_S3D
| |—-\_S4D
|—-\PCIB
| |—-\ADR
| |—-\_PRT
|
|—-\AUDO
| |—-\_ADR
|—-\MODM
| |—-\_ADR
| |—-\_PRW
|—-\LPCB
| |—-\LNKX(X=ABCDEFGH)
| |—-\_HID
| |—-\_UID
| |—-\_DIS
| |—-\_PRS
| |—-\_CRS
| |—-\_SRS
| |—-\_STA
| |—-\DMAC
| | |—-\_HID
| | |—-\_CRS
| |—-\FWHD
| | |—-\_HID
| | |—-\_CRS
| |—-\IPIC
| | |—-\_HID
| | |—-\_CRS
| |—-\MATH
| | |—-\_HID
| | |—-\_CRS
| |—-\LDRC
| | |—-\_HID
| | |—-\_UID
| | |—-\_CRS
| |—-\RTC
| | |—-\_HID
| | |—-\_CRS
| |—-\TIMR
| | |—-\_HID
| | |—-\_CRS
| |—-\N393
| |—-\COMX(X=AB)
| | |—-\_HID
| | |—-\_UID
| | |—-\_STA
| | |—-\_CRS
| | |—-\_PRS
| | |—-\_DIS
| | |—-\_SRS
| | |—-\_PS0
| | |—-\_PS3
| |—-\PS2K
| | |—-\_HID
| | |—-\_CRS
| |—-\PS2M
| | |—-\_HID
| | |—-\_CRS
| |—-\EC0
| |—-\_HID
| |—-\_CRS
| |—-\_REG
| |—-\_QXX
|—-\PATA
| |—-\_ADR
| |—-\PRID
| |—-\_ADR
| |—-\_GTM
| |—-\_STM
| |—-\P_DX(X=01)
| |—-\_ADR
| |—-\_GTF
|—-\SATA
| |—-\_ADR
| |—-\PRTX(X=01)
| |—-\_ADR
| |—-\_SDD
| |—-\_GTF
|—-\SBUS
|—-\_ADR
|—-\XXXX(Method used to called by other ACPI ASL Code)
2 关键部分设计
VGA设计
VGA功能部分设计主要包括VGA的State切换部分和VGA亮度管理部分。下面我们就简单分析这部分的设计原理和方法。
VGA的状态切换有两种方法,一种方法是BIOS的CODE直接编程显卡寄存器的方法,可以使用SMI来做,也可以使用ACPI Code部分直接对显卡寄存器编程。但是,这样的方法违反的ACPI的精神,所以,我们不做推荐。这里我们重点减少ACPI的使用驱动程序的方法。
ACPI部分的VGA状态切换的主要思想是由用户请求切换,产生Q事件,在Q事件中间,第一按照某种算法,更新切换的状态,这个状态将被更新返回到_DGS的返回值,然后,发送NOTIFY事件,请求OS切换,OS将调用每个显示设备的_DGS的方法,根据_DGS方法返回的值,调用_DSS方法决定所选择的设备开关状态。当然,对于有些系统的设计,可能是根据_DGS的返回值,直接对于显示设备的实际编程,然后,调用_DSS方法只是用于通知系统固件当前显示的状态。
_DOS与_DOD的作用。
_DOS的作用是Disable Output Switch of (Automatic),我们知道,如果OS正对显卡寄存器进行操作,那么此时,如果系统BIOS,特别是SMI例程,如果对显卡进行操作的话,那么就会引起系统的紊乱。同样,如果系统正在调用某个方法,此时,系统BIOS却对某个方法进行更新,那么就会出现系统方法的紊乱。
_DOD的作用是ACPI告诉OS,当前系统中有哪些现实输出设备存在,例如,可能系统没有接CRT。这样,系统将不调用CRT所对应的Device的任何方法。这个方法将在OS初始化的时候调用。所以,如果系统中间我们需要实时检测某个显示输出设备的插拔的话,只有在其他的方法中间完成,这个方法无法完成。
Fn+F7->_QXX -》更改内部参数
-》Notify(VGA)
OS-》VGA._DOS -》VGA.LCD._DGS-》VGA.LCD._DSS VGA._DOS
-》VGA.CRT._DGS-》VGA.CRT._DSS
Q事件设计
Q事件原理:
Q事件原理是这样的,当EC想发送一个Q事件给OS时,EC有效它的SCI Pin,这个Pin被接到ACPI寄存器的GPIO上,这个GPIO将产生SCI中断,OS接到这个中断以后,OS将查询状态寄存器,以确定到底是那个事件发出的SCI,ACPI最终将确定是EC发出的,那么OS将Query EC,以确定是Q??,OS是这样查询的,OS先向66h口发送84h命令,然后从62h读Q值,然后调用相应的Q方法执行,就完成了Q事件相应处理。例如,如果读出的Q值是25,那么OS将调用_Q25的方法执行。
这个部分的设计主要有几个部分内容,一个部分是反馈控制EC,一个部分Notify OS,
另一个部分是通过WMI通知OS。例如,关掉Touch Pad、关掉FM、关掉篮牙和关掉Wireless Card部分都是通过EC控制GPIO Pin脚完成的。
下面举几个具体的例子说明这部分代码的编写。
例如电池插拔,我们只需要Notify AC Adapter和Battery。
Method(_Q20){
Notify(\_SB.AC,0x80)
Notify(\_SB.BAT0,0x80)
}
如果我们使用Legacy方式关闭背光,我们可以这样实现:
Method(_Q26){
If(LEqual(zDOS,0))
{
\_SB.GSMI(4)
}
Else
{
\_SB.GSMI(5)
}
}
SMI4和5将会在SMI下面调用INT10。
3 LPC和SuperIO设计
这部分设备信息比较简单,提供的目的就是统计系统资源的使用。由于这些设备都是属于传统的ISA设备,不像PCI设备,OS可以使用标准的驱动程序枚举系统资源需求,所以,这里必须提供这些设备的资源使用情况。而且,这些设备资源使用都是固定的,不需要配置,所以方法简单,只需要提供_HID和_CRS就可以。
相对而言,SuperIO是一个比较复杂的设备,这个设备需要我们进行配置,由于没有标准的OS驱动程序完成这个设备的资源配置,所以,我们只有使用ACPI的方法对这个设备进行资源分配。
SuperIO一般设计为一个PnP Compatible设备,一般采用一个配置口和一个数据口来访问配置寄存器,所以,这是使用INDEX类型的Field。所以,我们必须先定义Conf. Port和Conf. Data,然后定义Index域,例如4E,4F的Conf. Port和Conf. Data的配置空间可以这样定义:
OperationRegion(N393, SystemIO, 0x02E, 0x02)
Field(N393, ByteAcc, Lock, Preserve)
{
INDX, 8,
DATA, 8
}
IndexField(INDX, DATA, ByteAcc, Lock, Preserve)
{
Offset(0x07), // Logical Device Number.
R07H, 8,
Offset(0x20), // SIO Configuration and ID.
R20H, 8,
R21H, 8,
R22H, 8,
R23H, 8,
R24H, 8,
R25H, 8,
R26H, 8,
R27H, 8,
R28H, 8,
R29H, 8,
R2AH, 8,
Offset(0x30), // Logical Device Activate.
R30H, 8,
Offset(0x60), // I/O Space Configuration.
R60H, 8,
R61H, 8,
Offset(0x70), // Interrupt Configuration.
R70H, 8,
R71H, 8,
Offset(0x74), // DMA Configuration.
R74H, 8,
R75H, 8,
Offset(0xF0), // Special Logical Device Configuration.
RF0H, 8,
RF1H, 8
}
在定义了配置口之后,我们可以定义SuperIO上设备,我们以IrDA为例,描述如何写这部分代码。
对于FIR而言,为了配置资源,需要提供以下的方法:_STA/_DIS/_CRS/_PRS/_SRS
一般而言,如果一个设备使用传统设备资源,例如是3F8,2F8,IRQ3或者IRQ4等,我们都会在SCU中间提供配置的选项,例如FIR。这样,为了是系统其他接口,特别是PCMCIA接口有设备接入的时候,我们可以调节FIR的资源分配,或者是DISABLE它,来给其他设备让出资源。
_DIS方法是给OS提供一个Disable设备的方法,一般而言,当我们在控制面板中间选择停用的时候,将调用这个方法,如果我们选择卸载这个设备的时候,系统OS首先调用这个方法,然后在卸载这个设备的驱动程序。
_CRS是返回当前资源设置,_PRS返回是可能的资源,_SRS根据OS选择的资源,写入设备配置寄存器。
读取当前资源_CRS比较简单,我们需要做的就是建立一个资源模板,然后选定FIR设备,把FIR的资源配置写入资源模板建立的Buffer,在方法的最后返回这个Buffer给OS就是可以。
Method(_CRS,0,Serialized)
{
// Create the Buffer that stores the Resources to
// be returned.
Name(BUF0,ResourceTemplate()
{
IO(Decode16,0x02F8,0x02F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){} //v26+
})
// Set SIO to COM B.
Store(2,R07H)
// Skip this sequence if the COM B Port is Disabled
// in BIOS Setup.
If(CMBD)
{
// Create pointers to the specific byte.
CreateByteField(BUF0,0x02,IOL0)
CreateByteField(BUF0,0x03,IOH0)
CreateByteField(BUF0,0x04,IOL1)
CreateByteField(BUF0,0x05,IOH1)
CreateByteField(BUF0,0x07,LEN0)
CreateWordField(BUF0,0x09,IRQW)
CreateByteField(BUF0,0x0C,DMAW) //v26+
// Write IO and Length values into the Buffer.
Store(R60H,IOH0)
Store(R61H,IOL0)
Store(R60H,IOH1)
Store(R61H,IOL1)
Store(8,LEN0)
// Write the IRQ value into the Buffer.
And(R70H,0x0F,Local0)
If(Local0)
{
ShiftLeft(One,Local0,IRQW)
}
Else
{
Store(Zero,IRQW)
}
// Write the DMA value into the Buffer
Store(R74H, Local0)
If(LGreaterEqual(Local0, 0x04))
{
Store(Zero, Local0)
}
If(Local0)
{
ShiftLeft(One, Local0, DMAW)
}
Else
{
Store(Zero, DMAW)
}
}
Return(BUF0)
}
ACPI采用了EISA的资源描述的方法描述设备资源需求,为了编程的方便,ACPI提供了一些资源描述的宏,像ResourceTemplate等来帮助工程师写设备资源状态和需求程序。FIR的_PRS由于是固定的,所以这部分代码编写就直接使用一个静态的Buffer,将COM所有可能的资源组合List出来,然后返回就可以。
// Possible Resource Setting Method for COM B.
Method(_PRS,0,Serialized)
{
// Build a Buffer with all valid COM B Port Resources.
Name(BUF0,ResourceTemplate()
{
StartDependentFn(0,2)
{
IO(Decode16,0x2E8,0x2E8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2F8,0x2F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3E8,0x3E8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3F8,0x3F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2E8,0x2E8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2F8,0x2F8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3E8,0x3E8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3F8,0x3F8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){0}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2E8,0x2E8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2F8,0x2F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3E8,0x3E8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3F8,0x3F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2E8,0x2E8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2F8,0x2F8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3E8,0x3E8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3F8,0x3F8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){1}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2E8,0x2E8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2F8,0x2F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3E8,0x3E8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3F8,0x3F8,0x01,0x08)
IRQNoFlags(){3}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2E8,0x2E8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x2F8,0x2F8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3E8,0x3E8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
StartDependentFn(0,2)
{
IO(Decode16,0x3F8,0x3F8,0x01,0x08)
IRQNoFlags(){4}
DMA(Compatibility,NotBusMaster,Transfer8){3}
}
EndDependentFn()
})
Return(BUF0)
}
_SRS是一个相对复杂的方法,这个方法带有一个参数,这个参数是OS根据系统资源状况和设备的需求,而确定分配给设备的资源。这个方法中,将分析OS传递的参数,然后提取IO、Memory、IRQ和DMA等值,然后相应写入相应的配置寄存器。值得注意一点就是这个方法中间必须有Enable设备的方法,前面我们只提到了_DIS方法,但是没有相应的_ENA方法,其实Enable设备的动作就是在这里完成。
Method(_SRS,1,Serialized)
{
// Point to the specific information in the passed
// in Buffer.
CreateByteField(Arg0,0x02,IOLO)
CreateByteField(Arg0,0x03,IOHI)
CreateWordField(Arg0,0x09,IRQW)
CreateByteField(Arg0,0x0C,DMAW)
// Set the SIO to COM B.
Store(2,R07H)
// Disable the device.
Store(0,R30H)
// Set the Base IO Address.
Store(IOLO,R61H)
Store(IOHI,R60H)
// Set the IRQ.
FindSetRightBit(IRQW,Local0)
If(LNotEqual(IRQW,Zero))
{
Decrement(Local0)
}
Store(Local0,R70H)
// Set the DMA
FindSetRightBit(DMAW,Local0)
If(LNotEqual(DMAW,Zero))
{
Decrement(Local0)
}
Store(Local0,R74H)
Store(Local0,R75H)
// Set the Decode Range so COM B works on ICH6 and
// future platforms.
And(IOD0,0x8F,IOD0) // Clear all bits.
If(LEqual(IOHI,0x03)) // Address = 0x3xx
{
If(LEqual(IOLO,0xF8)) // Address = 0x3F8
{
Or(IOD0,0x00,IOD0)
}
Else // Address = 0x3E8
{
Or(IOD0,0x70,IOD0)
}
}
Else // Address = 0x2xx
{
If(LEqual(IOLO,0xF8)) // Address = 0x2F8
{
Or(IOD0,0x10,IOD0)
}
Else // Address = 0x2E8
{
Or(IOD0,0x50,IOD0)
}
}
// Enable the device.
Store(1,R30H)
}
4 平台Thermal Zone设计
ACPI的热域与风扇控制方案的精髓是把风扇控制的决策给OSPM来做,系统固件提供参数、方法和具体的执行(EC)。下面讨论Thermal Zone部分的ACPI原理。
-
-
- i. 主动散热方法和被动散热方法
-
对于一个系统,或者热域而言,有两种散热热方法,一种是主动散热方法,一种是被动散热方法。所谓的主动方案就是利用系统风扇散热方案,所谓被动方案就是让发热的处理器或者设备降低性能从而降低热量的产生。主动散热表现在几个方法_ACx和_ALx上,_ACx是给OSPM提供系列的温度阈值,_ALx是在相应阈值点上的主动冷却方案,一般虚拟为设备,当温度高于ACx时,会打开ALx对于的设备(执行这个设备或者虚拟设备的_ON方法),当温度低于对应ACx时,会关掉这个设备(执行这个设备或者虚拟设备的_OFF方法)。当然,当前温度是从_TMP的方法得到。被动散热是通过_PSV和_PSL实现的,_PSV返回的是温度阈值,_PSL返回的是一系列处理器。我们在P_BLK或者是_PTC中会定义处理器的Throttle方法,系统会根据一定的算法公式计算出所要执行的Throttle。
-
-
- ii. 主动散热方式和被动散热方式
-
OSPM根据当前温度(from _TMP)和各个阈值点(From _ACx和_PSV)确定当前某个散热方法激活,如果(_TMP)>(_ACx),则主动方法激活,如果(_TMP)>(_PSV)则被动方法激活,当然,两种方法也可以被同时激活。所以,通过提供不同的_ACx和_PSV,系统固件可以指导系统选用那一种散热方式,例如,如果固件设置_ACX很低,而_PSV很高,那么,主动方式肯定先于被动方式激活,这样等于设置系统为主动模式,反之,则为被动模式。
OSPM来确定当前其希望采用主动方式还是被动方式散热,OSPM使用一个_SCP的方法来通知系统固件其希望执行主动散热还是执行被动散热,当系统硬件收到这个_SCP执行的影响之后,将切换_ACx和_PSV的阈值,之后,OSPM将重新评估_ACx和_PSV的阈值,以此来确定激活某种散热策略。
-
-
- iii. 轮询和主动通知
-
当系统温度变化的时候,OSPM有两种方式可以检测到这个变化,第一种方式是通过查询的方式,第二种是通过中断消息的方法。系统推荐的设计是采用中断消息的方法,例如,我们设计系统的温度变化的通知变化大小是5度,也就是说30、35、40、45、50、……的时候EC发出一个Q Event的SCI,Q Event的ACPI部分将执行Notify(Thermal Zone,0x80),这个通知消息将通知OSPM执行读当前温度。当OSMP收到这个消息以后,将执行_TMP的ACPI方法,通过这个方法OSPM可以得到当前温度。
如果系统硬件(或固件)设计为不支持主动通知的方式,那么,OSPM必须采用轮询的方式,也就是说OSPM每隔一个固定的事件执行一次_TMP方法,这样OSPM可以及时的得到系统温度值。轮询方式定义需要一个_TSP的Object,OSPM执行这个方法将得到硬件期望的查询间隔,如果返回为零表示为不要求OSPM查询。当然,这两种方式也可以和在一起使用,例如,系统只有一个主动报警点,当温度穿过这个报警点是,硬件产生Q Event的SCI,当温度低于这个温度的时候,系统靠查询的方法实时得到系统温度。
-
-
- iv. _CRT和_HOT
-
_CRT返回一个值,表示当_TMP大于该值的时候,OSPM会执行将系统关掉,进入S5。 _HOT回一个值,表示当_TMP大于该值的时候,OSPM会执行将系统进入S4。
这里值得注意的一点就是_CRT的关机,系统是有序的进入S5,这与硬件关机不同,如果是硬件关机,那么系统重新开机的时候会检测硬盘,如果是_CRT关机的话,系统不会重新检测硬盘。
-
-
- v. 滞后设计
- _CRT当温度增加到一个指定的散热点阈值,OSPM将打开主动散热设备,或者是被动散热。
- 于是平台重新把散热点阈值设置成一个较低的温度值(为了滞后),同时通知OSPM这个改变。
- 因为这个新的散热点阈值,这个风扇将在一个比它打开的时候更低的温度点上停转(所以实施一个负向滞后)。
- 当温度达到这个低的散热点阈值,OSPM将关闭相关的主动散热设备,或者停止被动散热。
- 硬件将把_ACx到其原来的值,然后通知OSPM这个温度点又被改变了。
- v. 滞后设计
-
示例方案设计:
5 处理器电源管理和SpeedStep设计
处理器的电源管理包括三个部分内容,处理器的电源状态Cx、处理器性能状态Px和处理器的Trottle三个方面内容。这三者之间关系如下:
CPU到底处在那种状态,以及调用哪些处理器ACPI方法,由OS确定。目前的XP系统提供了一种简单选择方法。就是利用“电源选项”中的“电源使用方案”的UI方法。这种方法可以简单的设计OS处理器的策略,这些策略将根据不同的应用场景,协调以下目标:
-
-
-
-
- 处理器性能
- 处理器耗电
- 热需求
- 风扇噪音等。
-
-
-
当然,我们可以根据我们的使用,定义出我们自己的UI策略,例如我们APP可以制定会议场景、影视场景等,来调整CPU三个方法特征。
1. 处理器的电源状态
ACPI C0 状态:
全速运行:在这种状态下,CPU全速运行,个别设备可能进入低电状态;
ACPI C1 状态:
Auto-Halt:处理器执行一条Autohalt指令,进入休眠,不在执行代码。处理器此时还在侦测处理器总线,保持Cache一致性。
ACPI C2 状态:
快速启动状态:ICHX使用STPCLK#信号停住处理器指令流,保持这种状态,直到STPCLK#无效。在这种状态下,处理器侦测处理器总线,保持Cache一致性。
ACPI C3 状态:
停止处理器时钟:
在这种状态下,STPCLK#信号被拉低,处理器执行一个STOP-GRANT周期,停止指令流。ICH4M接着有效STP-CPU#信号,这个信号输出给时钟发生器,时钟发生器停止处理器的时钟。在这种状态下,不允许访问内存,ARB_DIS位将被置位,所以,如果有设备访问内存,那么设备讲退出C3状态。
ACPI C4 State:可以降低处理器的供电电压.
停止处理器时钟的同时降低处理的电压:这种状态非常象C3状态。在处理器有效给处理器的STP_CPU#信号之后,接着降低处理器的电压。这减少处理器上的漏电,在退出C4之前,ICH4M会提高处理器的电压。
CPU时钟控制信号
STPCLK#:用来停止处理器的指令流。
C3_STAT#:用来通知AGP设备系统将退出或者进入C3状态。
STP_CPU#:用来通知时钟发生器停止处理器时钟信号。
DPSLP#:用来强制处理器进入深度休眠状态。
DPRSLPVR#:用来在C4状态下降低VRM的电压。
如何进入Cx状态:
怎样退出Cx状态:
Cx转变时序图:
C0-C2-C0
C3-C0-C3
C4-C0-C2
系统BIOS对于Cx状态的支持:使用_CST对象描述C状态的支持
ACPI 2.0引入了一个可选的方法_CST对象的方法向OS描述系统对于Cx状态的支持,作为ACPI 1.0b的描述方法的扩充。关于这个方法的细节内容请参考ACPI 2.0的规范内容。Windows XP和Windows Server 2003产品对于ACPI 2.0的_CST对象支持。当系统既有_CST定义,又有CSTATE_TYPE定义,Windows将选择最低的耗电消费状态进入。下面是我们的BIOS定义。
Method (_CST, 0)
{
If(\_SB.PCI0.LPCB.EC0.ADP) //;(PWRS)
{
// Report C1, C2, and C3 only in AC Mode.
Return( Package() {
3,
Package()
{
Buffer(){0x82,0x0C,0,0x7F,0x08,0,0,
0,0,0,0,0,0,0,0,0×79,0x00},
1,
1,
1000
},
Package()
{
Buffer(){0x82,0x0C,0,0×01,0x08,0,0,
0x14,0x10,0,0,0,0,0,0,0×79,0x00},
2,
1,
500
},
Package()
{
Buffer(){0x82,0x0C,0,0×01,0x08,0,0,
0x15,0x10,0,0,0,0,0,0,0×79,0x00},
3,
85,
250
}
})
}
Else
{
// Report C1, C2, C3, and C4 in Battery Mode.
Return( Package() {
4,
Package()
{
Buffer(){0x82,0x0C,0,0x7F,0x08,0,0,
0,0,0,0,0,0,0,0,0×79,0x00},
1,
1,
1000
},
Package()
{
Buffer(){0x82,0x0C,0,0×01,0x08,0,0,
0x14,0x10,0,0,0,0,0,0,0×79,0x00},
2,
1,
500
},
Package()
{
Buffer(){0x82,0x0C,0,0×01,0x08,0,0,
0x15,0x10,0,0,0,0,0,0,0×79,0x00},
3,
85,
250
},
Package()
{
Buffer(){0x82,0x0C,0,0×01,0x08,0,0,
0x16,0x10,0,0,0,0,0,0,0×79,0x00},
4,
185,
100
}
})
}
}
6 处理器的性能状态
处理器的性能状态对于不同厂商的处理器有不同的处理方式。Intel的技术叫做SpeedStep和Enhanced SpeedStep。下面我们分析一下Intel的SpeedStep技术。
我们知道,处理器的性能状态是由离散的电压和频率对定义的。在不同的频率和电压下,处理器的执行性能也是不同。Intel的EIST提供了一个全新的方法来改变CPU的性能状态,这种方法本身是基于CPU MSR寄存器,而不是基于Chipset的,所以这种方法具有硬件软件的转变过渡时间少、系统设计简单等特点。
Intel的奔腾M处理器提供了两个MSR寄存器来实现EIST功能,一个是IA32_PERF_CTL和IA32_PERF_STS,我们通过写IA32_PERF_CTL来实现CPU性能的状态转变,主要是设置BUS_RATIO_SEL和VID_SEL位域。一旦设置完之后,CPU随后马上进行RATIO和VID的切换,如果在切换的过程中间有新的值更新IA32_PERF_CTL寄存器,那么,在CPU完成系统状态切换之后,CPU马上进行本次新的性能切换。
如果我们读取IA32_PERF_CTL,这只表明上次更新所期望的CPU性能置,并不表示CPU的当前性能值,因为,如果CPU处在thermal throttle的过程中,那么,CPU会暂停性能的切换,直到thermal throttle的结束。
如果我们想确定CPU的当前性能状态,我们可以通过读取IA32_PERF_STS寄存器的值来确定。这个寄存器包括一些主要信息,如BUS_RATIO_STS和VID_STS域表明当前的频率和电压值,这些信息是动态更新的。这个寄存器同样也包括了系统BOOT时和系统所支持的最大性能点。
ACPI部分要求必须提供两个方法,一个是_PSS,一个是_PCT。_PSS是返回一个表,这个表是由系列的Package组成,每个Package对应了一个处理器状态相关信息,这个包的格式如下:
Package()
{
CoreFreq,
Power,
TransitionLatency,
BusMasterLatency,
Control,
Status
}
其中,CoreFreq是指当前处理器状态下的处理器频率,Power是指当前处理器状态下的处理器功耗,TransitionLatency是指切换到该状态所需要的时间,例如,如果我们使用SMI方式切换,比如这个SMI Latency是100us,CPU本身切换是10us,那么这项值就是110us。
7 调试
-
- WMI部分代码
-
- 如何在POST过程更新ACPI代码。
- 一致性Check
- 平台Power Resource设计
PowerResource是设备的电源管理,主要是给风扇部分使用。
-
- 平台Wake设计
-
- Resource一致性
-
- 检查中断Routing的一致性
-
- 检查EISA PnP ID的正确性。
-
- 功能调试
- i. USB 1.1 & 2.0 OK。
- ii. IDE Hard Disk & CDROM OK。
- iii. PC Card
- iv. SD/MMC/XD等
- v. SpeedStep(Optional)
- vi. SuperIO(IrDA和COM、LPT等)
- vii. Fn+Key
- viii. OSB(Optional)
- 如何解Bug
- i. 对比法
- 与别家机器对比
- 与其它机型对比
- 不同条件下的对比
- ii. 排除法
- 功能排除
- 代码排除
- iii. 顺藤摸瓜
- Example:Lan wake up fail。
- iv. 跟踪法
- Example:USB Floppy Sometime fail。
- v. 分析法
- Example:System halt when refresh battery information while opening speedstep。
- Example:System lost it’s mouse when pressing F2 to enter SCU。
- i. 对比法
- 功能调试
注意事项
- 一定建立在对系统和各种Spec的理解的基础上。
- 尽量寻求供应商的帮助。
- 多交流。
- 尽量不要打补丁,要找到RootCause。
六、PCI总线简介
1992年,Intel公司决定放弃了对于VESA VL等总线标准的支持,因为当时出现的一些总线只是对于ISA总线的一些头痛医头,脚痛医脚的修改,这些修改的时间跨度并不是很长,所以Intel发布了一个PCI 1.0的规范,93年发布了2.0版本,95年发布了2.1版本,99年发布了2.2版本,2002年发布了2.3版本。现在该规范的管理由一个工业厂商协会来管理,该组织叫PCI Special Interest Group。
PCI,意思是周边组件互联局部总线(Peripheral Component Interconnect Local Bus)。Intel 最初设计它的目的就是解决传统总线(例如ISA,EISA等)的三个缺点:其一,总线容量不够,其二,地址宽度不够,其三,不能支持即插即用。对于容量的不足,是通过改进系统总线的电气,例如提高总线时钟频率,将PCI的时钟提高到33MHZ,这比ISA的8M总线时钟已经有很大的提高,而且,将数据线的宽度增加到32位(64位扩展),在数据交易的原则上,由于PCI是地址/数据复用的,PCI提供了BURST传输类型,这些措施使得PCI总线的系统吞吐率有很大的提高。对于地址容量问题的解决,主要采用了地址扩展和双位址交易的方案,使得PCI系统可以支持64位地址空间。
PCI提出的即插即用的思想,奠定了其在主板上发展的基础。在PCI之前,曾经出现过对于ISA总线的改进─PnP ISA,PnP ISA在系统固件下,可以达到对于设备的自动配置,其主要思想是根据设备的唯一Serial Number,一块一块的孤立(isolate)每一个设备,这个孤立的过程与SMBus 2.0的ARP协议非常相像,然后根据设备的资源请求描述符,系统固件可以知道设备对于系统资源(内存地址、IO地址、中断和DMA请求)的需求,然后系统固件根据系统资源的分配和使用情况,给设备分配一定的系统资源,然后写进设备的配置描述符中,这样就完成了对于一块设备的配置,然后循环这个过程,直到所有卡都完成了配置。PCI继承和发展了这种配置思想,相应的地方做了一些改进,例如,由于PCI采用了树形分级的总想拓扑结构,相应的PCI设备就是每一个节点或叶子,这样,我们就可以通过遍历树的方法来访问每一个设备,这样比PnP ISA的孤立的方法使得系统更加的Robust,因为PnP ISA系统的孤立过程建立在每一个PnP ISA设备统一遵循孤立原则,如果有一块卡破坏了这个原则的话,那么这个系统就可能崩溃。所以,PCI把设备的访问建立在树形架构本身之中,使得系统更加牢固。在可以独立的访问每一设备之后,然后系统固件(BIOS和操作系统)通过PCI配置空间,可以得到每一设备的系统资源需求,同样,也是通过这个配置空间,完成对于设备请求资源的分配工作。这就是PCI的配置机制。
PCI强大的生命力就是因为它的配置机制,在PCI发展过程以及将来发展的趋势,PCI的电气信号可能会作相应的改变,但是PCI配置机制可能会长久存在的,这一点在Intel的Hub link总线和PCI的下一代PCI Express已经得到验证。
在本节中,我们简单回顾PCI相关规范内容,为以后讨论提供一个基础。
- 电气信号
(图1.1 PCI 2.2信号)
由于我们研究PCI架构主要是为了提供研究主板架构内的ASF实现提供背景知识,所以我们对于PCI的讨论主要针对逻辑和功能部分,在此我们讨论PCI系统只是为了概念上的完整性。
PCI协议,作为一个典型的并行总线,其具有典型的并行总线信号特征。为了实现并行总线的交易,PCI信号按功能主要包含以下几组:第一组,系统信号,主要有时钟信号(CLK)和PCI复位信号(PCIRST#);第二组,地址/数据和命令/总线使能信号,这一个部分主要用来编码总线命令的,是理解PCI原理的核心部分;第三组是传输控制信号,就是总线交易中的主设备和从设备的握手信号,主要包括FRAME#、TRDY#、IRDY#、STOP#、DEVSEL#和IDSEL信号;第四组是仲裁请求和仲裁允许信号,因为PCI总线是一个多设备共享的总线,所以每个设备如果想使用总线的话,必须首先申请总线的使用,在等到仲裁其的允许之后,方可启动一次交易,仲裁器一般在HOST桥和PCI to PCI桥中实现的,这组信号主要包括REQ#和GNT#;第五组是终端信号;第六组是错误校验信号;第七组是为了满足PCI64扩展的64位扩展信号。
- PCI总线操作
PCI总线操作的一个典型特点就是BURST传输,这种传输是针对PCI地址和数据总线复用的特点而提出的一种提高总线效率的方法。如果没有BURST传输,PCI总线操作只有在一次地址周期之后传输一个四字节的数据周期,但是如果采用BURST传输之后,在放出4N的之地之后的数据项,可以以此传输4N、4(N+1)、4(N+2)……地址的内容,提高了数据段在传输周期中的比例,提高了总线的效率。
(图1.2 PCI基本操作示意)
如图1.2,一个PCI总线操作典型的分成两个部分,第一部分是在传输开始的第一阶段,称为地址相,在地址相,此时C/BE(3:0)编码交易类型,而AD(31:0)编码传输的数据。而随后的数据相中,此时C/BE(3:0)上传输AD(31:0)的四字节中哪一个字节有效信息,而在AD(31:0)上传递实际数据编码。
在地址相时,其中的C/BE(3:0)的交易编码如下:
(图1.3 PCI基本操作编码)
一般的交易过程是这样的,交易的开始,是从主设备从发出仲裁申请REQ#信号申请总线的使用开始,到主设备被仲裁允许,即GNT#有效。接下来的地址相中,从主设备有效其FRAME#信号标志地址相的开始,主设备把本次交易的地址和交易类型放到总线上,而从设备对这些命令和地址进行译码,如果某个从设备译码成功,即发现该命令寻址到该设备,那么该设备就有效DEVSEL信号,标志地址相的结束。在随后的数据相,如果主设备和从设备都处于准备就绪状态,那么该交易顺利完成。
但是如果交易的目标在PCI to PCI桥的后面,那么此时的交易就稍微复杂一些,去需要一个叫做重试的操作。当一个交易的目的在一个PCI to PCI桥的后面的时候,那么该桥知道交易目的在其后面(桥是怎样知道,见后面桥的讨论),此时桥会在地址相时有效其DEVSEL信号,进入数据相;但是在数据相,桥并没有目的的数据,所以此时桥使用一种控制信号的有意义组合,表示一个重试的操作。主设备在收到该重试之后,将结束本次交易,但是,在稍后的时刻,主设备会重新发送本次交易。在本次交易结束之后,桥将记住其使用重试结束的交易,然后,其作为主设备在其下级总线上启动一次新的交易从其下游得到这个交易的数据,然后保存在其内部的缓冲中间。在下一个时刻,当被重试结束的主设备再次启动这个交易的时候,桥已经有了其交易目的的数据了,那么本次交易将顺利完成,桥将把目的的数据传送给主设备。这就是跨总线交易的基本思想。
- PCI配置机制
前面的概述中间我们已经讨论过PCI的配置空间是PCI提供的一个与IO空间和Memory空间并列的独立空间,这个空间是由以系列的寄存器组成,通过使用PCI配置命令,我们可以读取或设置其寄存器的内容,这256个字节的寄存器是在每个PCI设备上实现的,这样可以获取设备相关信息和配置设备的资源使用。而且从图1.3的交易编码中我们也可以看出在PCI架构中间,配置交易和内存和IO的读写是完全并列的指令,但是我们知道对于X86家族的处理器而言,其只有IO和Memory相关的指令,没有响应的配置指令的实现,那么对于处理器而言,其究竟是怎样访问PCI配置空间的呢?
这一项翻译工作主要是由一个PCI重要设备部件,HOST桥完成的,HOST一般由叫做北桥,其翻译过程是这样的:在北桥上提供两个IO寄存器,一个地址为0CF8H的四字节寄存器,叫做PCI配置地址寄存器,一个是地址为0CFCH的四字节寄存器,叫做PCI配置数据寄存器。当我们访问PCI配置数据寄存器的时候,HOST将作为主设备,申请使用总线,在申请获得批准之后,其将在总线上启动PCI配置周期,最后将获得的配置内容写入配置数据寄存器。
配置地址寄存器是一个32比特的寄存器,其使用格式如下:
(图1.4 PCI配置地址寄存器的格式)
在地址相时的数据信息从PCI配置地址寄存器中得到,其地址编码方式可能有两种,第一种就是其寻址的设备就是HOST下面的BUS 0上,第二种是其寻址的设备不在当前HOST下面的BUS 0上,以上两种编码分别成为类型0编码和类型1编码方式。如果是类型1的编码,那么北桥的动作就非常简单,其就是直接把配置地址寄存器的编码放到AD[31:0]上就可以,但是对于类型0的编码就比较复杂,其编码如图1.5
(图1.5 HOST桥的类型0的配置交易AD信号线编码方式)
从1.5中我们可以看出,AD[10:0]上仍然保留设备功能号和配置空间寄存器偏移信息,这些信息将被从设备译码,选定指定的配置寄存器。而AD[31:11]的使用就不同,因为HOST桥已经判断该目标设备就在当前总线上,所以其总线号信息已经没有任何意义,所以,可以不需要这些信息,而这些数据线将被用来选定一个设备,而这些信号线将与每个设备的IDSEL信号连接在一起,如何连接,则对于不同厂商实现的方式可能不同,例如对于INTEL的实现是AD16用作DEV 0,一次类推,而VIA则是使用AD11作为DEV 0,这样,HOST通过有效其中一根数据线可以选中一个设备。这样就寻址到一个特定设备的某个特定功能的某个特定配置空间寄存器。
关于PCI to PCI桥是如何对配置支持和翻译的,在桥的部分我们将在做讨论。总之,经过
HOST桥的翻译,在PCI总线上产生配置周期,最终可以寻址到我们需要的配置寄存器。
PCI配置空间是由256字节的配置寄存器,其使用可以和布局PCI对其有一定的规范,如图1.6是一个典型PCI设备的配置空间Layout:
(图1.6 类型00h的设备PCI配置空间头部格式)
从图1.6我们可以看出,PCI配置空间可以可以分成如下几个部分
- 设备信息部分:这一部分主要包括Vendor ID、Device ID、Class Code、Subvendor ID和Subsystem ID等,这信息主要是用来检查设备存在与否和由于加载对于设备驱动程序使用的。例如,如果设备的Vendor ID返回是0FFFFFFFFh的话,那么就表明当前DEVICE上没有设备存在。
- 设备配置寄存器:这一部分的寄存器主要包括BAR基址寄存器、Interrput Pin、Interrupt Line、Expansion ROM Base Register等,这一部分寄存器主要是给系统初始化配置或者是BIOS用来分配设备所需要的内存空间、IO空间和中断使用的资源的。
- 设备控制寄存器:主要是指Command、Max_Lat、Min_GNT等寄存器,这个用来控制设备的内容译码器、IO译码器等是否工作等设备行为的寄存器。
- 设备状态寄存器:反映设备运行错误等一些信息。
下面,我们将讨论某些寄存器的使用。
- 中断系统
我们知道,ASF控制器集成在Intel的网络控制芯片82547EI等芯片中,这些芯片,在逻辑上又是一个PCI设备,为了PCI设备正常工作,我们必须给其分配中断资源,在此我们简单讨论PCI的中断系统。
PCI的中断系统有两个明显的特征,一个是中断共享特性、另一个是可以动态分配。PCI中断之所以能够共享,是因为PCI对于原有的ISA做了一定的改进,例如该ISA中断的边缘触发为电平触发、同时在操作系统内容中断服务程序采用链式结构、在设备上实施一个中断未决位的寄存器。PCI中断的动态分配主要是在设备与中断控制其中间加了一个中断路由器,可以通过对中断路由器的编程使得设备的中断路由到不同的中断控制器的输入引脚上。PCI中断系统示意如图1.7
(图1.7 PCI中断系统示意图)
当某个PCI需要中断CPU时,其将它的中断线拉低,这个拉低表现在通过中断路由器对于中断控制器的某个引脚的拉低。中断控制器中断CPU的执行,CPU在其FSB上发出去中断号的指令,HOST将其翻译成PCI的中断确认交易,取出当前的中断向量,然后CPU跳到该中断向量处执行,调用某个设备的驱动程序,该设备驱动程序首先会取检查其中断未决位是否置位,如果置位,表明当前设备需要服务例程服务,如果没有则直接跳到驱动程序链式结构的下一个驱动程序上执行。
在PCI配置空间有两个字节寄存器用于中断配置,其中Interrupt Pin寄存器表明该功能使用的式那中断引脚A、B、C、D中的哪一个,而Interrupt Line寄存器用来填分配的中断号的。
- Memory和IO
Memory和IO指令是CPU和外设进行信息交互的基本指令,所以为了实现设备的基本功能,设备一定会实现一些Memory和IO资源,这些资源是CPU可以访问的,当CPU访问这些资源时,其在其FSB上发起Memory或IO指令,这些指令被HOST直接翻译成PCI总线上的相应指令,设备对这些指令的地址进行译码,以确定其是否是寻址的目标。
在ISA时代,设备的资源需求是固定的,所以其译码器的译码范围和基址是固定,但是对于PCI而言,其译码器的基址却不是固定的,而是我们可以通过PCI空间对其进行配置和设置的,这些设置的寄存器就是配置空间中的10h到24h的6个基址寄存器BAR。我们可以通过BAR来设置其译码器的起始地址,例如一个设备实现了1000 0000h大小的Memory资源,我们将其基址分配到3 0000 0000h处,那么以后所有Memory指令如果其寻址范围在3 0000 0000h到3 FFFF FFFFh中,那么该设备就会声明其实交易的从设备,而完成本次交易。
这样,系统软件或者BIOS可以统筹考虑系统资源的需求,给设备分配其所需的资源,避免其发生冲突。
其实BAR寄存器还有一个作用就是反映了设备资源的需求信息。一个BAR由两个部分组成,以其中间没个比特分开,在其上是可读可写的,我们配置BAR是就是利用其可写特性,而在其下的所有比特都是只读的而且除了PCI定义的低几个有一定意义的比特可以为1外,其他的都必须为零。根据这个特性,我们可以往其中写进全1字节,就是0FFFF FFFFh,然后在读出来,可以判断BAR需求的信息。如图1.8所示。
所以,我们可以根据最后读出的结果的比特0是否为1判断其BAR是一个Memory译码器的实现,还是一个IO译码器的实现。进一步可以知道其Memory译码器的特性,另外,我们也可以根据其第一位不为零位确定译码器宽度。例如,我们向一个BAR写入全1,也就是0FFFF FFFFh,返回的结果是0FF80 0000h,那么我们可以该Memory需求宽度是00080 0000h。
另外我们也可以看出,我们给一个设备分配基址的时候必须满足一个特点就是边界对齐条件,所谓的边界对齐就是其分配的地址必须在其宽度的整数倍边界上,因为如果不是分配在边界上,那么由于BAR特性就可能会出现低位没有写进去,导致了其实际解码的边界对齐与其算法统计上的不一直,造成分配上的冲突的可能。
(图1.8 PCI BAR寄存器格式)
- ROM
许多PCI设备都会具有一个ROM,该ROM主要是用来存放设备的驱动程序的,如果设备实现了一个硬件ROM,那么为了访问这个ROM我们一定要给这个ROM分配一定的Memory资源,这是通过配置Expansion ROM Base Address寄存器完成的,其操作方法和BAR大同小异,不再累述。
值得注意的一点就是设备的ROM不一定放在其本身的ROM中,特别主板上自带的一个PCI设备,可以将其放在BIOS中,如我们82547EI的ROM就是压缩在BIOS中,这样可以支持PXE启动。其实在BIOS POST过程中不论是压缩在BIOS中的映像,还是ROM译码器带的ROM影响,其都在一个地方被拷贝到系统的C8000h到E0000h的某个位置,然后执行。
关于ROM的详细格式请参考PCI 规格书。
- PCI to PCI桥
我们知道PCI to PCI是两条不同级别的PCI总线的连接,所以PCI桥是总线分级的关键。一般而言,一个桥连接上级总线和一个下级总线,而桥本身作为上级总线的一个设备而存在。总线号的指定,是系统软件或者BIOS指定,但是除了HOST桥的下级总线为零外,其他的没有特别强制的要求,一般要求其上级的总线号要小于其所有下级总线号,而我们一般的指定都是采用按照设备号的大小遍历树的方法指定。
(图1.9 PCI总线拓扑抽象)
PCI桥作为个PCI设备,也有其配置空间,其配置空间头部格式如图1.10所示。其中的18h(Primary Bus Number)、19h(Secondary Bus Number)、1Ah(Subordinate Bus Number)是关于PCI to PCI桥对于总线拓扑和配置命令过滤非常重要的寄存器,其中19h确定了其下级总线号,这个值使用BIOS或者系统软件的PCI初始化程序写入的。而1Ah寄存器是其后所有总线号最大的值。
我们知道,从一个桥上级传过来的配置命令,有可能是类型0的,也有可能是类型1的,如果是类型0的,PCI to PCI桥作为一个普通的PCI设备和其他PCI设备一样遵守0的寻址规则。PCI桥不会考虑是否将其过滤、传递或者翻译。如果该配置命令是一个类型1的配置命令,这表明该配置命令所寻址的设备不在这个桥的上游总线,而是其上游总线的一个桥下面,所以此时这个PCI to PCI就得侦听这个配置命令的BUS NO字段内容,并将其于其19h(Secondary Bus Number)和1Ah(Subordinate Bus Number)的内容比较,如果这个Bus No<19h寄存器的值或者是>1Ah寄存器的值,这表明该配置命令寻址的设备不在该PCI to PCI
(图1.10 PCI to PCI配置空间首部格式)
的后面,此时该桥对这个配置命令不作任何响应。否则,则表示该配置命令寻址的设备在桥的后面,此时桥一个“重试”回应该配置命令。然后,桥还需要继续进一步确认这个配置命令寻址的设备是在其下游总线,还是在其下下游总线上,如果是配置命令的BUS No=19h寄存器的值,表明其寻址设备就在该桥的下游总线上,那么该桥使用上级的类型1中的内容,按照类型0的生成法则,在其下游生成类型0的配置命令。否则,表示其寻址的设备在该桥的下下游,该桥只需对该命令做简单的传递即可。
最后,我们讨论一个桥对于IO和Memory命令的支持,也就是对Memory和IO命令的过滤。我们在PCI总线枚举的过程中间我们可以统计到其下游和下下游的所有设备所需求的IO和Memory资源的基址和大小,我们按照桥的要求计算成上下限的格式分别写入1Ch和1Dh的IO基和限寄存器、以及24h和26h的Memory基和限寄存器,这样对于其上游的一个IO操作或者是Memory操作,桥判断其寻址的目标地址是否落在以上的基和限之间,如果是那么表示该寻址的目标在其下游或者下下游,此时,该桥将以一个重试回应该命令,并在其下游启动这个命令,否则,表示该寻址设备,不在其下游或者是下下游,该桥不作任何回应。
上面我们简单讨论PCI相关知识,有了这些基础我们可以进一步讨论865/875最新芯片组的特征。
今天的文章bios工程师有前景吗_BIOS研发工程师[通俗易懂]分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/58670.html