【我所认知的BIOS】->反汇编BIOS之Bootblock(9)
-- Memory initial 函数
By Lightseed
06/24/2010
为什么会有bootblock和非bootblock这么一说呢?其实就是因为有没有真正的内存可以用的区别。这个章节里我们一起来看看经过之前那些章节的讨论后,BIOS在初始化memory之前会做的一些动作。(稍微比较琐碎点,看起来比较枯燥。)Memory initial这个函数里面,会再做一些前期的准备工作。比如8259的中断控制器的初始化,PCIE的初始化,等等然后进入到intel提供的MRC里面去。那么这节就讲讲这个函数里面具体接触的东西。
图1 BIOS主流程
_F000:36E0 CT_Memory_Init: ; CODE XREF: _F000:E377j
Call Power_Managment_init;伪代码
_F000:370C Power_Managment_init_Exit: ; CODE XREF: _F000:8850j
Call PCIExpressInit ;伪代码
Call Memory_INIT ; There are too many registers which intel do not release, so I have not plan to comment it.
; We usually call these code "MRC", it means memory refrence code. As I know, Award and AMI
; MRC are the same.
;中间省略N行
Call IGDDetect ; 伪代码
Ret
上面是memory initial的整个函数反汇编出来的代码。架构也很清晰,正如之前说的那些,8259初始化好了后,继续Power management的初始化,然后再是PCIE的初始化,然后是真正进入到intel的MRC去detect和初始化内存。一条线,比较简单。
从_F000:3709这行中可以看出,在8259初始化好了以后,程序就会进入到初始化power management的函数里去。那我们来看看这个函数里面具体都做了什么。
_F000:8834 Power_Managment_init: ; CODE XREF: _F000:3709j
_F000:8834 mov si, 880Ch
_F000:8837 mov dh, 40h ; '@' ; PM IO
_F000:8839
_F000:8839 Power_Managment_init_loop: ; CODE XREF: _F000:884Ej
_F000:8839 mov dl, cs:[si] ; offset
_F000:883C mov al, cs:[si+1] ; get the value to store
_F000:8840 out dx, al ; set
_F000:8841 out 0EBh, al
_F000:8843 out 0EBh, al
_F000:8845 out 0EBh, al
_F000:8847 add si, 2
_F000:884A cmp si, 8834h ; Table end?
_F000:884E jnz Power_Managment_init_loop
看到上面的函数是不是大家都有比较熟悉的感觉呀?一开始进来就是给SI找到一个table的offset,然后就是按照结构体的元素来初始化各个寄存器。而这个结构体就比较简单了,总共两个byte。第一个byte是在Power management base address(还记得么?在前面的文章里有讲,PM IO base address是4000H)上的偏移。关于table里面的东西,我就不多收了,我觉得我写的挺详细的,只要你对照ICH的datasheet一切都能查到对应的描述。
_F000:880C ;[]--------------------------------------------[]
_F000:880C This table is very easy, so I will not comment them one by one.
_F000:880C I choose the important register to comment. :)
_F000:880C ;[]--------------------------------------------[]
_F000:880C Power_Managment_init_table db 2 ; ; Power Management 1 Enable Register Low byte
_F000:880D db 0 ; ;
_F000:880D ;
_F000:880E db 3 ; ; Power Management 1 Enable Register High byte
_F000:880F db 0 ; ;
_F000:880F ;
_F000:8810 db 0 ; ; Power Management 1 Status Register low byte
_F000:8811 db 0FFh ; ; clear all status
_F000:8811 ;
_F000:8812 db 1 ;
_F000:8813 db 0FFh ; ; Power Management 1 Status Register high byte
_F000:8813 ;
_F000:8814 db 11h ; ; Processor Control Register
_F000:8815 db 0 ; ; No forced throttling, No clock throttling is occurring (maximum processor performance).
_F000:8815 ;
_F000:8816 db 28h ; ( ; General Purpose Event 0 Status Register low byte
_F000:8817 db 0FFh ; ;
_F000:8817 ;
_F000:8818 db 29h ; ) ; General Purpose Event 0 Status Register high byte
_F000:8819 db 0FFh ; ;
_F000:8819 ;
_F000:881A db 2Ah ; *
_F000:881B db 0FFh ; ;
_F000:881B ;
_F000:881C db 2Bh ; +
_F000:881D db 0FFh ; ; clear all GPE0 status
_F000:881D ;
_F000:881E db 2Ch ; , ; General Purpose Event 0 Enables Register the 1st byte
_F000:881F db 0 ; ;
_F000:881F ;
_F000:8820 db 2Dh ; -
_F000:8821 db 0 ; ;
_F000:8821 ;
_F000:8822 db 2Eh ; .
_F000:8823 db 0 ; ;
_F000:8823 ;
_F000:8824 db 2Fh ; /
_F000:8825 db 0 ; ; all GPE0 disable
_F000:8825 ;
_F000:8826 db 30h ; 0 ; SMI Control and Enable Register 1st byte
_F000:8827 db 20h ; ; Enables writes to the APM_CNT register to cause an SMI#.
_F000:8827 ;
_F000:8828 db 31h ; 1
_F000:8829 db 0 ; ; Other function disable
_F000:8829 ;
_F000:882A db 34h ; 4 ; SMI Status Register 1st byte
_F000:882B db 0FFh ; ;
_F000:882B ;
_F000:882C db 35h ; 5 ; SMI Status Register 2nd byte
_F000:882D db 0FFh ; ; clear all SMI status
_F000:882D ;
_F000:882E db 41h ; A ; I can not find the remark of this register in datasheet...:(
_F000:882F db 30h ; 0 ;
_F000:882F ;
_F000:8830 db 44h ; D ; Device Activity Status Register low byte
_F000:8831 db 0FFh ; ;
_F000:8831 ;
_F000:8832 db 45h ; E ; Device Activity Status Register high byte
_F000:8833 db 0FFh ; ; clear all status
关于这部分的说明,我觉得很惭愧,据我所了解,这部分的代码绝对多数是为了解决一些bug才放在这里的。而且这些bug应该还都是北桥那面的一些东西,intel也没公布相关的说明,所以我也不知道究竟这里面的寄存器是有啥用。这下这段代码就是PCIE初始化的函数被反汇编出来的源码。不过我把这个函数的地址贴出来,而且这里的基本上BIOS是不会动的,所以平时我也没有深入进去研究。
_F000:394A ;[]----------------------------------------[]
_F000:394A PCIExpressInit:
_F000:394A The function of this subrutine is to initial Northbridge
_F000:394A I think you can check with the datasheet by yourself.
_F000:394A Because some of the register, I do not research before .
_F000:394A What's more, we do not touch these code. If you are
_F000:394A interest into it, you can dig into.
_F000:394A ;[]----------------------------------------[]
_F000:394A
_F000:394A PCIExpressInit: ; CODE XREF: _F000:3745j
_F000:394A mov cx, 54h ; 'T' ; bus 0# dev 0# func 0# Reg 54h
_F000:3A90 jmp di ; PCIExpressInit return
在PCIE初始化后面,还有关于北桥那面的寄存器操作,比如:MMIO base address的初始化(后面要用到的哦,尤其是intel的MRC也会频繁使用的。),MCHBAR的打开呀什么的,都比较简单啦。不过要提的一下是PCIE的访问方法:(我想网上应该有不少的说明,怎么去访问PCIE的配置空间。)
Register Location = PCI Express Base Address + Bus number* 100000h + device
number * 8000h + function * 1000h + Address Offset
BIOS就真正进入到intel的MRC,有两点需要说明一下:
第一,我们再看看_F000:37A8之前有三个byte的数据是没有用到的(如果你反汇编了BIOS,如果没有,直接跳过具体的地址)。也许有人会很纳闷,为什么会这样呀?答案是在BIOS中,写code的时候用了aglin 4的动作。从而代指有三个byte是无意义的。所以我们应该跳过去看。
第二,正如我上面的注释里有说,Memory_INIT这个函数里面有太多的寄存器是intel没有release的了,所以我也不想挨着挨着深入。如果您有兴趣可以看看intel的一本橘皮书,好像是ICH BIOS spec吧。具体我不记得了。里面有详细的描述。
内存初始化完了以后,在award的bios里面还会继续detect IGD。代码很简单:
主要是操作北桥的相关寄存器(52H,详细的说明查一下北桥的datasheet)来做的。
都挺简单的,对照着北桥的datasheet就可以查清楚了。(你们查不到的寄存器,我也查不到了。。。。所以有些东西,我其实也不清楚,究竟某些寄存器有啥用。)
至此关于BIOS的bootblock部分最最重要的目的就算是完成了。因为BIOS在rom里面跑而且还没有堆栈,真的很费事,而且速度还慢。所以才要尽快地把内存初始化好,然后把放在ROM里面的BIOS源码都copy到内存中来执行。这样的话不仅提高了BIOS初始化主板的速度,还显得让程序更好写。大致的过程可以看看图1所示。
图1 BIOS在不同时期的运行过程图
我要简单描述一下图1的意思。左边其实就是BIOS一直在ROM里面跑的过程。(由于北桥开关的原因,FFFF_0000会被映射到F_0000上去,之前有讲在这里再提一下。)所以在图上仍然以F000段开说明,不要混淆哦。然后到内存初始化好了以后,bootblock的解压缩函数就会把BIOS解压缩到内存中去,然后继续执行(这个时候的BIOS就是真的在实模式下执行的了哦,北桥的开关也就是真的把内存和实际的CPU低端寻址对应了。)。
Bootblock的这部分,我们基本就要结束了,后续我们会看到BIOS的copy呀什么的操作。让我们拭目以待吧!!