利用BIOS中断获取内存容量

来源:互联网 发布:mac os x 10.10.5下载 编辑:程序博客网 时间:2024/06/10 06:25

    • e820h子功能
      • 地址范围描述符
      • e820h的调用参数
      • 案例实现内存探测
        • 定义数据存放结构约定探测结果存放于0x8000处
        • 调用中断并判断是否发生错误是否探测完所有内存块
        • 完整代码

机器启动后有2种方式探测物理内存:直接探测、通过BIOS中断探测。
这里我们关注如何通过BIOS的0x15中断探测物理内存。

由于BIOS中断需要在实模式下调用,所以我们在bootloader中探测物理内存比较合适。

BIOS的0x15中断有3个子功能:e820h、e801h、88h。这三个子功能区别是:

  • e820h返回内存布局,信息量大
  • e801h返回内存容量
  • 88h最简单,功能也最弱

e820h子功能

这是功能最强大的子功能,使用也最复杂。

地址范围描述符

系统内存分为很多内存块,这些内存块加起来才构成系统的总内存。而这些内存块有不同的类型:有的OS可以使用、有的OS不能使用。

每次调用这个中断都会得到一块内存的描述,要通过多次调用才能得到系统所有内存块的描述。

e820将物理内存探测的结果以地址范围描述符的格式放在内存中。地址范围描述符共计20字节,格式是:

字节范围 描述 0~7 内存块基地址 8~15 这块内存的大小 16~20 这块内存的类型

我们将在内存中开辟一个数组来存放所有地址范围描述符。
在c语言中可以这样定义:

struct {    uint64_t addr;    uint64_t size;    uint32_t type;};

e820h的调用参数

显然在保护模式下用int $0x15来调用15h中断。

但在这之前我们要将参数放置在寄存器中:

  • eax:子功能编号,这里我们填入0xe820
  • edx:534D4150h(ascii字符”SMAP”),签名,约定填”SMAP”
  • ebx:每调用一次int $0x15,ebx会加1。当ebx为0表示所有内存块检测完毕。(重要!看后面的案例会明白如何使用)
  • ecx:存放地址范围描述符的内存大小,至少要设置为20。
  • es:di:告诉BIOS要把地址描述符写到这个地址。

中断的返回值如下:

  • CF标志位:若中断执行失败,则置为1。
  • eax:值是534D4150h(“SMAP”)
  • es:di:中断不改变该值,值与参数传入的值一致
  • ebx:下一个中断描述符的计数值(见后面的案例)
  • ecx:返回BIOS写到cs:di处的地址描述符的大小(应该就是20吧?)
  • ah:若发生错误,表示错误码

案例:实现内存探测

定义数据存放结构,约定探测结果存放于0x8000处

约定在bootloader中将内存探测结果放到0x8000地址处。
数据存放的格式在c中定义了:

struct e820map {    int nr_map;  // 表示数组元素个数,该字段是为了方便后续OS,不是BIOS访问的    struct {        uint64_t addr;        uint64_t size;        uint32_t type;    } map[E820MAX];};

调用中断,并判断是否发生错误、是否探测完所有内存块

通过int $0x15调用中断。如果发生错误,CF位为1。那么可以尝试使用其它子功能进行探测,或者就直接关机(连内存容量都没探测肯定无法启动OS了)

根据ebx判断是否完成所有内存块的探测,如果没有则继续int $0x15探测下一个内存块,如此反复直到所有内存块探测完毕。

Created with Raphaël 2.1.0开始探测内存初始化ebx=0、es:di设置eax、ecx、edx调用0x15中断,会将结果写入es:di处CF位为1?尝试其它方法探测内存di自增20ebx为0?探测完毕yesnoyesno

完整代码

c代码

#define E820MAX 20struct e820map {    int nr_map;  // 表示元素个数    struct {        uint64_t addr;        uint64_t size;        uint32_t type;    } map[E820MAX];};

汇编

probe_memory:    movl $0, 0x8000    xorl %ebx, %ebx    movw $0x8004, %distart_probe:    movl $0xE820, %eax    movl $20, %ecx    movl $SMAP, %edx    int $0x15    jnc cont    movw $12345, 0x8000  # 在ucore中表示出错,与BIOS无关    jmp finish_probecont:    addw $20, %di    # 控制BIOS该将“地址描述符”写到哪里    incl 0x8000      # nr_map成员自增1,该变量与BIOS无关    cmpl $0, %ebx    # 如果ebx是0,探测就成功结束    jnz start_probe  # 如果ebx不是0,继续探测下一个内存块finish_probe:
0 0
原创粉丝点击