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

1.为什么要有BIOS这个东西

利用BIOS的硬件信息编程(1)_第1张图片

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.什么是启动扇区?

利用BIOS的硬件信息编程(1)_第2张图片

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

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

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

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

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

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

利用BIOS的硬件信息编程(1)_第3张图片

现在设置磁盘的第一个扇区为启动扇区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  ---> 
   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的硬件信息编程(1)_第4张图片

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


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

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

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

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

利用BIOS的硬件信息编程(1)_第5张图片

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

利用BIOS的硬件信息编程(1)_第6张图片

键盘链接ps2插头:

利用BIOS的硬件信息编程(1)_第7张图片

电脑ps2控制器连接图:


利用BIOS的硬件信息编程(1)_第8张图片

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

利用BIOS的硬件信息编程(1)_第9张图片

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

利用BIOS的硬件信息编程(1)_第10张图片

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

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

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

IO Port Access Type Purpose
0x60 Read/Write Data Port
0x64 Read Status Register
0x64 Write Command 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

0x30 B pressed
0xB0 B released
0x12 E pressed

0x92 E released
0x1E A pressed
0x9E A released
0x13 R pressed
0x93 R released
0x1D left control pressed
0x38 left alt pressed
0xB8 left alt released
0x9D left control released







你可能感兴趣的:(利用BIOS的硬件信息编程(1))