您的位置:首页 > 移动开发 > IOS开发

利用BIOS的硬件信息编程(1)

2016-12-30 23:40 246 查看
1.为什么要有BIOS这个东西



BIOS实际就是ROM里面的一段小程序(芯片总容量大概就是几M),在主板通电时候会硬件加载执行。基本功能就是进行硬件检测,检查没问题就引导bootloader和系统。

为什么需要这个?

第一个原因,即使只有一块主板,什么外部连接的存储介质(磁盘,U盘等存储)都没有(更别说操作系统),这种情况下,一个主板其实也应该能进行一些硬件检查( 甚至是设置),这就是BIOS存在并且嵌入到主板的一个原因。再者,不同主板不一样的硬件配置,对应的bios代码也不一样。不过两个不同bios,对外提供统一的一些统一的操作硬件的接口,他们遵循bios规范【http://blog.chinaunix.net/uid-27033491-id-3239348.html】,以前的操一些操作系统例如DOS就是直接使用bios提供的硬件接口访问硬件,现在的linux访问硬件就是绕过bios访问硬件,下面我们会测试通过bios去访问硬件。

2.bios加载MBR扇区

首先主板通电,硬件自动加载bios程序。bios程序检查硬件,初始化和设置硬件,然后加载磁盘(启动盘)的MBR扇区到0x7c00,然后跳到0x7c00执行。为什么是0x7c00?可以看【http://www.ruanyifeng.com/blog/2015/09/0x7c00.html】。主要是兼容历史的原因。

3.什么是启动扇区?



一个磁盘分为多个磁面,一个磁面一个head(读写磁头),一个磁面多个磁道(track),一个磁道多个扇区(sector),一个扇区512字节。其中0磁头0磁盘第一个扇区称为这个磁盘的第一个扇区。如果这个扇区(512字节)最后两个字节是0xaa55.识别这个扇区是MBR主启动扇区。

现在我们就来测试一下bios去加载MBR扇区的流程。

我们这里使用redhat7.2的qemu虚拟化来做实验最为简单。

首先新建一块1G的虚拟磁盘:

qemu-img create -f qcow2 /home/disk.qcow2 2G


使用已有磁盘新建一台虚拟机,启动虚拟机,查看屏幕输出信息:



现在设置磁盘的第一个扇区为启动扇区MBR.

#vim mbr.asm

start:
times (510-($-$$)) db 0 ;510字节前面都设置为0,$表示当前地址,$$表示起始地址
dw 0xaa55               ;511和512字节设置为AA55,表示启动扇区


编译:nasm mbr.asm -o mbr

这个文件现在我们的磁盘的第一个扇区:

由于qcow2是一个虚拟磁盘文件。我们需要通过nbd把虚拟磁盘文件挂在为真正可以访问的磁盘的。默认centos是没有nbd 安装nbd驱动。

这里需要编译一下内核的nbd驱动:

查看内核版本:

# uname -a

Linux bogon 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

#wget http://vault.centos.org/7.2.1511/os/Source/SPackages/kernel-3.10.0-327.el7.src.rpm
#rpm2cpio kernel-3.10.0-327.el7.src.rpm |cpio -imd

#tar xvf linux-3.10.0-327.el7.tar.xz

增加内核编译选项

Device Drivers  --->
[*] Block devices  --->
<M>   Network block device support
# make
#insmod drivers/block/nbd.ko max_part=8
# qemu-nbd -c /dev/nbd0 /home/disk.qcow2     ----------------虚拟磁盘文件挂在为真正的磁盘设备
# dd if=./mbr of=/dev/nbd0 bs=1 count=512     ----------------把上面的程序mbr写入磁盘的头512字节,也就是第一个扇区
#qemu-nbd -d /dev/nbd0

重启启动虚拟机,虚拟机的屏幕输出:



这说明bios已经识别到磁盘的MBR分区并且已经把MBR分区加载到了0x7c00地址执行。

3.利用执行MBR扇区的代码实现键盘操作功能。

现在处理器的控制权已经在mbr扇区代码的手里。现在我们就可以通过mbr扇区的代码去控制我们的处理器,进而操作我们的主板。

现在我们就实现键盘的操作功能,其实bios已经有相关的键盘的接口提供。但是我们目前先不用它的接口。我们直接操作我
4000
们的键盘。

目前我的键盘是ps2接口的键盘, 使用的ps2控制器是Intel的8042.



所以我们先链接一下ps2接口,以及连接的电路图。



键盘链接ps2插头:



电脑ps2控制器连接图:



上面两个图整合为一个整体就是:



我们cpu可以控制ps2控制器8042进而控制我们的键盘。所以我们需要了解一下ps2的协议以及8042的芯片资料。其中ps2协议可以了解一下时钟信号和数据信号(电信号)的一些传输协议,当然这些信号我们只需要了解就行,因为这些信号ps2控制器可以帮我们处理,我们只要操作ps2控制器。ps2协议可以自己百度,这里只提供一下数据传输的信号:



电信号采集数据要不就是高电平(二进制1),要不就是低电平(二进制0),上面8个数据刚好可以组成一个字节的数据。控制器就是通过这些信号和键盘进行通信的。当然,这是控制器的工作,我们可以重点不放在这里【有兴趣可以自己百度一下"ps2协议"或者"ps2 protocol"】。我们重点看如何操作ps2控制器。现在我们看一下8042控制器的操作:

这里有一篇不错的介绍文章:http://blog.chinaunix.net/uid-25099259-id-3409632.html

8042控制器的寄存器有四个:状态寄存器,输出缓存控制器,输入缓冲控制器,控制寄存器。我们通过处理器操作端口就可以控制8042寄存器:

IO PortAccess TypePurpose
0x60Read/WriteData Port
0x64ReadStatus Register
0x64WriteCommand Register
关于寄存器的 每一个位的意思,可以看一下这个文章【http://wiki.osdev.org/%228042%22_PS/2_Controller】,里面有详细的寄存器信息介绍,还有如何初始化和使用控制器。我们就参考这边文章来操作的我们的寄存器。由于汇编不好,代码可能写的有点不精简。

开始之前我们需要先要调试工具。例如读取到数据我们如何知道数据是什么?

通过显示是最直观的。显示暂时没有去研究,我们可以直接使用bios提供的显示接口。例如打印0xaa,怎么打印?代码如下

代码写的有点挫,熟悉的人可以修改一下:

start: jmp run
tran_ascii:;数字转为对应的ASCII
cmp al,9
jle add_30h
jmp add_57h
add_30h:
add al,0x30;eg:0的ASCII 0x30
ret
add_57h:
add al,0x57;eg:a的ASCII 0x61
ret
print_8bit:;dl has data need print,eg:0xAB
push ax
push bx
push cx
push dx
xor ax,ax ;clear
xor bx,bx
mov al,dl ;被除数
mov bl,16 ;除数
div bl;AL存储除法操作的商,AH存储除法操作的余数
;把要打印的数据先保存起来
mov di,need_print
stosw

;显示高四位
mov ah,0x09
mov bh,0 ;显示页码
mov cx,1 ;重复输出字符的次数
call tran_ascii
int 0x10;功能描述:在当前光标处按指定属性显示字符

;移动光标到下一处
mov ah,0x03;读取当前光标位置
int 0x10
add dl,1;移动光标到下一处
mov ah,0x02
mov bh,0
int 0x10

;显示低四位
mov si,need_print
lodsw
mov al,ah
mov ah,0x09
mov cx,1 ;
call tran_ascii
int 0x10
pop dx
pop cx
pop bx
pop ax
ret

need_print: DW 0x00
run:
mov dl,0x1b
call print_8bit
times (510-($-$$)) db 0



有了这个我们就可以打印出控制器端口读出来的数据了。下面开始真正编码操作ps2控制器:

start: jmp run
;
;数字转为对应的ASCII
tran_ascii:
cmp al,9
jle add_30h
jmp add_57h
add_30h:
add al,0x30;eg:0的ASCII 0x30
ret
add_57h:
add al,0x57;eg:a的ASCII 0x61
ret

print_8bit:
push ax
push bx
push cx
push dx

;dl has data need print,eg:0xAB
xor ax,ax ;clear
xor bx,bx
mov al,dl ;被除数
mov bl,16 ;除数
div bl;AL存储除法操作的商,AH存储除法操作的余数
;save
mov di,need_print
stosw

;移动光标到下一处
mov ah,0x03;读取当前光标位置
int 0x10
add dl,1;移动光标到下一处
mov ah,0x02
mov bh,0
int 0x10

;显示high四位
mov si,need_print
lodsw
mov ah,0x09
mov bh,0 ;显示页码
mov cx,1 ;重复输出字符的次数
call tran_ascii
int 0x10;功能描述:在当前光标处按指定属性显示字符
;移动光标到下一处
mov ah,0x03;读取当前光标位置
int 0x10
add dl,1;移动光标到下一处
mov ah,0x02
mov bh,0
int 0x10

;显示low四位
mov si,need_print
lodsw
mov al,ah
mov ah,0x09
mov cx,1 ;
call tran_ascii
int 0x10

pop dx
pop cx
pop bx
pop ax
ret
wait_data:
in al,0x64
and al,1b
jz wait_data
in al,0x60
ret

need_print: DW 0x00

run:
cli
;Disable Devices
mov al,0xad
out 0x64,al
;Flush The Output Buffer
flush_data:
in al,0x64
and al,0x01
jz flush_ok
in al,0x60
jmp flush_data
flush_ok:
;Set the Controller Configuration Byte
mov al,0x20;read configure
out 0x64,al
in al,0x60
mov dl,al
add dl,11101100b;disabled interrupt,use poll mode
mov al,0x60;set configure
out 0x64,al

mov al,dl
out 0x60,al
;Perform Controller Self Test
mov al,0xaa
out 0x64,al
call wait_data
cmp al,0x55
jne err
;Enable Devices
mov al,0xae ;first port for keyboard
out 0x64,al
test_port_1:
mov al,0xAB
out 0x64,al
call wait_data
cmp al,0x00
jne err
read_input:
call wait_data
mov dl,al
call print_8bit
jmp read_input
err:
times (510-($-$$)) db 0
dw 0xaa55




上面就是键盘输入:bear和ctl+alt的扫描码输出。

各个按键的扫描码可以查看:http://wiki.osdev.org/PS/2_Keyboard

0x30B pressed
0xB0B released
0x12E pressed
0x92E released
0x1EA pressed
0x9EA released
0x13R pressed
0x93R released
0x1Dleft control pressed
0x38left alt pressed
0xB8left alt released
0x9Dleft control released
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: