戏说BIOS之CMOS

戏说BIOSCMOS

 

1. Introduction

 

     CMOS全称为complementary metal oxide semiconductor, 翻译成中文就是互补金属氧化物半导体,它是由一颗小的纽扣电池供电的128/256 bytes ram(现在的chipset通常提供256 bytes或者更大的空间)。它主要用于存放RTC以及一些oem的系统配置信息,所以除了RTC等部分其它的很多信息都是undocumented& non-standardRTC 标准的(documented&standard) ram bank如下表 1 所示:

 

Index

Name

00h

Seconds

01h

Seconds Alarm

02h

Minutes

03h

Minutes Alarm

04h

Hours

05h

Hours Alarm

06h

Day of Week

07h

Day of Month

08h

Month

09h

Year

0Ah

Register A

0Bh

Register B

0Ch

Register C

0Dh

Register D

0Eh-7Fh

114 Bytes of User RAM

                                           1

 

2. Access Cmos

 

     访问cmos通常是透过70h,71h这两个IO port实现的,有些chipset支援256 bytescmos ram,访问128bytes以后的空间需要开启chipset的始能register,有些chipset使用72h73h访问扩展的空间如intel chipset,有些仍然使用70h71hsis chipset,因为这部分是非标准的,故后面的练习程序就不去读写这部分ram space。读写cmos的过程非常简单,读特定的index的内容只需要将index送给70h,然后就可以从71h读出对应的数据,具体过程如下述code所示:

;-------------------------------------------------------------

;     read_cmos

;     read the contents of a specific CMOS register

;     call with:      al = CMOS address to read

;     returns:        ah = Contents of register

;    used registers: ax

;-------------------------------------------------------------

 

read_cmos       proc    near

 

     cli

     or      al,80h ;disable NMI

     out     70h, al

     call    io_delay

     in      al, 71h

     call    io_delay

     mov     ah, al

     xor     al,al

     out     70h,al ;enable        NMI

     sti

     ret

       

read_cmos       endp

写操作和读类似,只是要将待写入的数据送给71h即可代码如下所示:

;-------------------------------------------------------------

;    write_cmos

;    write the contents of a specific CMOS register

;    call with:      al = CMOS address to write

;                       ah = Contents of register

;    returns:       NULL

;    used registers: ax

;-------------------------------------------------------------

write_cmos       proc    near

 

     cli

     or      al,80h ;disable NMI

     out     70h,al

     call    io_delay

     mov    al,ah

     out     71h,al

     call    io_delay

     xor     al,al

     out     70h,al ;enable        NMI

     sti

     ret

       

write_cmos       endp        

    

另外有些细节需要注意:a.读写过程中都需要关掉中断以防止,中断处理程序访问CMOS以及RTC更新过程中可能会导致并发访问。b.NMI(non-maskable interrupt)是一种中断向量为2的中断,但是与常规中断不同的是它不能通过mask register屏蔽掉而且sticli指令也对它无效;NMI通常用于一些无法恢复的硬件错误,访问CMOS时也可能产生NMI,所以需要关掉。NMI可以通过70h bit7做开关。c.状态寄存器A bit7记录了RTC是否正在更新,如果正在更新则等到更新结束再去读RTC(我写的cmosdump因为偷懒没有检查这一个bitJ)。

 

3. Msg Based Event Driven

 

     知道了以上的知识,我们就有能力写一个类似RUdump cmos的工具了,下图1就是我写的cmosdump.exe

 

 

我觉得访问cmos本身并不困难,画个UI倒是挺费劲的,一个劲狂call vbios;在完成这支tool的过程中我更深刻的体会到知识是相通的了,windows编程的经验在这里发挥了优势,为了能够动态更新,实时修改我就借鉴了windows下的“基于消息,事件驱动”的机制Mainloop->GetMsg->TranslateMsg->DispatchMsg一路下来好不快活!这部分的代码如下所示:

 

 

mainloop:

              call    show_index

              call    show_cmos

input_msg:

              mov    ah,0

              int      16h

              cmp    ah,01h          ;esc

              je       exit

             

              cmp    ah,48h          ;up arrow

              je       up

 

              cmp    ah,50h          ;down arrow

              je       down

             

              cmp    ah,4bh          ;left arrow

              je       left

 

              cmp    ah,4dh          ;right arrow

              je       right

 

              call    input_byte

              cmp    bl,1

              jne     msg_loop

              mov    ch,ah

              mov    ah,0

              int      16h

              cmp    al,0dh           ;enter

              je       enter

              jmp    msg_loop

enter:              

              call    get_index

              mov    ah,ch

              mov    al,INDEX

              call    write_cmos

                                 

msg_loop:                  

              jmp    mainloop

             

up:         

              cmp    ROW,MINROW

              jbe     roll_up

              dec     ROW

              jmp    bypass_up

roll_up:  

              mov    ROW,MAXROW

bypass_up:

              call    set_cursor

              jmp    mainloop

down:               

              cmp    ROW,MAXROW

              jae     roll_down

              inc     ROW

              jmp    bypass_down

roll_down:

              mov    ROW,MINROW

bypass_down:

              call    set_cursor

              jmp    mainloop

left:                

              cmp    COL,MINCOL

              jbe     roll_left

              sub     COL,3

              jmp    bypass_left

roll_left:

              mov    COL,MAXCOL

bypass_left:

              call    set_cursor

              jmp    mainloop

right:

              cmp    COL,MAXCOL

              jae     roll_right

              add    COL,3

              jmp    bypass_right

roll_right:

              mov    COL,MINCOL

bypass_right:

              call    set_cursor

             

              jmp    mainloop

    

exit:       

              call    clr_screen

              mov    ax,4c00h

              int      21h    

以上就是cmosdump.exe的核心架构J,完成以后觉得使用asm好别扭啊,可能是c/c++写的太多了,有点适应不过来了,以后还是要多写asm,增强驾驭asm的能力,让我的asmc/c++一样熟练。最后开放cmosdump.exe完整的source code供有兴趣的朋友参考,source code和可执行文件在这里下载

 

Enjoy it

 

 

That’s all!

 

Peter

你可能感兴趣的:(BIOS,Internals)