深入探究Linux下int86 BIOS操作 (linux int86 bios)
在计算机领域,BIOS(基本输入输出系统)是指固化在计算机主板上的一组程序,用于在计算机启动期间执行硬件初始化和自主检查,以确保计算机硬件能够正常工作。在PC架构中,BIOS通常具有访问低级硬件和设置计算机配置的能力。在Linux操作系统下,我们可以通过int86函数来调用BIOS的功能,从而实现对硬件的访问和配置。
int86函数的定义如下:
“`c
int int86(int intno, union REGS *in, union REGS *out);
“`
其中,intno为BIOS的中断号;in为输入参数的寄存器,包括AX、BX、CX、DX等;out为输出参数的寄存器。
在Linux下使用int86函数需要注意以下几点:
1. int86函数需要使用内核支持的__KERNEL__宏定义来编译,否则会出现编译错误。
2. int86函数的寄存器结构体REGS定义在linux/a/syscalls.h头文件中。
3. 在Linux下,BIOS的中断号需要加上0x80来调用,即INT 0x80。
下面我们将针对常见的BIOS操作进行深入探究。
1.显示字符串
在BIOS中,显示字符串的中断号为0x10,我们可以通过int86函数调用BIOS来显示字符串。
下面是显示字符串的代码实现:
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef unsigned char byte;
typedef unsigned short word;
struct videoinfo {
byte mode;
byte page;
word col;
word row;
byte attr;
byte ch;
};
struct videoinfo SavePara2[2];
struct videoinfo SavePara[1];
int mn()
{
char *str = “Hello World!”; // 要显示的字符串
int str_len = strlen(str); // 字符串长度
__a__ __volatile__(
“movw $0x0003,%%ax\n\t”
“int $0x10\n\t”
:
:
: “%eax”
); // 将显示模式设置为80×25,00为文本模式,02和03为CGA模式
__a__ __volatile__(
“movw $0x0000,%%ax\n\t”
“movw $0x0000,%%bx\n\t”
“movw $0x0007,%%cx\n\t”
“movw $0x0000,%%dx\n\t”
“int $0x10\n\t”
:
:
: “%eax”,”%ebx”,”%ecx”,”%edx”
); // 保存当前显示参数
__a__ __volatile__(
“movw $0x0000,%%ax\n\t”
“movw %%ax,%%es\n\t”
“movw $0x0000,%%bx\n\t”
“movw $0x0000,%%dx\n\t”
“movw %2,%%cx\n\t”
“movw $0x0009,%%ah\n\t”
“int $0x10\n\t”
:
: “a” (0x0e00), “b” (0x0000), “c” (str_len), “d” ((int)str)
:
); // 在文本模式下,将字符串输出到屏幕
__a__ __volatile__(
“movw $0x0000,%%ax\n\t”
“movw $0x0000,%%bx\n\t”
“movw $0x0007,%%cx\n\t”
“movw $0x0000,%%dx\n\t”
“int $0x10\n\t”
:
:
: “%eax”,”%ebx”,”%ecx”,”%edx”
); // 恢复原来的显示参数
}
“`
上述代码中,我们首先将显示模式设置为文本模式(80×25),然后保存当前的显示参数,接着在屏幕上显示字符串,最后恢复原来的显示参数。
2.读写磁盘
BIOS提供了读写磁盘的功能,其中读磁盘操作的中断号为0x13,写磁盘操作的中断号为0x19。
下面是读写磁盘的代码实现:
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef unsigned char byte;
typedef unsigned short word;
struct diskinfo {
byte head;
byte sect_cyl;
byte cyl;
byte sect;
void *buf;
};
struct diskinfo SavePara[1];
struct diskinfo SavePara2[1];
int mn()
{
short count = 1; // 读取扇区的个数
byte sector = 1; // 起始扇区
byte head = 0; // 磁头号
byte cyl = 0; // 柱面号
byte feature = 0x80; // 定义控制字节,指定使用LBA模式
byte read_cmd = 0x42; // 定义读取命令号
byte drive_id = 0x80; // 定义磁盘驱动器号,80h表示0号磁盘驱动器
char buf[512]; // 存放读取到的数据
__a__ __volatile__(
“movw $0x0000,%%bx\n\t”
“movw $0x0000,%%es\n\t”
“movw $0x0000,%%di\n\t”
“int $0x13\n\t”
:
:
:”%eax”,”%ebx”
); // 保存磁盘参数
__a__ __volatile__(
“movw $0x0000,%%di\n\t”
“movw $0x0000,%%es\n\t”
“movw $0x0000,%%bx\n\t”
“movw %4,%%cx\n\t”
“movb %3,%%cl\n\t”
“movb %2,%%dh\n\t”
“movb %1,%%ch\n\t”
“movb %0,%%ah\n\t”
“movb %5,%%dl\n\t”
“int $0x13\n\t”
:
:”m”(read_cmd), “m”(feature),”m”(head),”m”(sector),”m”(count),”m”(drive_id),”d”((int)buf)
:”%eax”,”%ebx”
); // 读取磁盘
__a__ __volatile__(
“movw $0x0000,%%bx\n\t”
“movw $0x0000,%%es\n\t”
“movw $0x0000,%%di\n\t”
“int $0x13\n\t”
:
:
:”%eax”,”%ebx”
); // 恢复原来的磁盘参数
}
“`
上述代码中,我们首先保存当前的磁盘参数,然后通过int86函数调用BIOS,读取指定位置的磁盘数据,最后恢复原来的磁盘参数。
3.设置中断向量
BIOS为每个中断提供了一个中断向量,其中每个中断向量的寻址方式为中断号*4,比如中断号为0x10,那么对应的中断向量地址就是0x40。
下面是设置中断向量的代码实现:
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef unsigned char byte;
typedef unsigned short word;
void set_intvect(byte intno, void (*handler)(void));
void int_timer(void);
void set_intvect(byte intno, void (*handler)(void))
{
word offset = (word)handler; // 中断处理程序的段内偏移
word segment = (word)handler >> 16; // 中断处理程序的段地址
*(word *)(intno * 4) = offset;
*(word *)(intno * 4 + 2) = segment; // 设置中断向量
}
void int_timer(void)
{
printf(“Timer Interrupt!\n”);
__a__ __volatile__(
“pushl %eax\n\t”
“pushl %ebx\n\t”
“movw $0x0028,%%ax\n\t”
“movw %%ax,%%ds\n\t”
“movw %%ax,%%es\n\t”
“movw $0x0000,%%bx\n\t”
“movb $0x20,%%al\n\t”
“outb %%al,$0x20\n\t”
“popl %ebx\n\t”
“popl %eax\n\t”
“iret\n\t”
:
:
:
); // 定义中断处理程序
}
int mn()
{
set_intvect(0x08, int_timer); // 设置中断向量
while(1)
{
__a__ __volatile__(
“hlt\n\t”
:
:
:
); // 等待中断
}
}
“`
上述代码中,我们首先定义了一个中断处理程序,用于处理时钟中断。然后通过set_intvect函数设置中断向量,即将中断号为0x08的中断向量设置为int_timer函数。最后我们无限循环等待时钟中断的到来,以确保中断处理程序能够被正确地执行。