e820是和BIOS的一个中断相关的,具体说是int 0x15。之所以叫e820是因为在用这个中断时ax必须是0xe820。这个中断的作用是得到系统的内存布局。因为系统内存会有很多段,每段的类型属性也不一样,所以这个查询是“迭代式”的,每次求得一个段。
我们看内核源代码。主要涉及两个文件:arch/x86/boot/memory.c和arch/x86/kernel/e820_32.c。我们 已经很幸运了,这部分代码已经用C重写过了。你可能会奇怪,启动调用e820时我们还在实模式,怎么能用C呢?答案是,这里用的是16位的C。gcc早已 经支持.code16 gcc模式了。
看detect_memory_e820()函数,里面就是e820的本质。它把int 0x15放到一个do-while循环里,每次得到的一个内存段放到struct e820entry里,而struct e820entry的结构正是e820返回结果的结构!而像其它启动时获得的结果一样,最终都会被放到boot_params里,e820被放到了 boot_params.e820_map。
如果你对struct e820entry还有疑问,你可以看一下arch/x86/kernel/e820_32.c::print_memory_map(),看看里面是怎么使用它的。
当然了,在arch/x86/boot/memory.c里,你还会看到另外两个利用int 0x15查询内存的函数,不过用途不一样了。
附:
boot_params结构体定义,其中E820MAX定义为128:
- struct e820entry {
__u64 addr; /* start of memory segment */
__u64 size; /* size of memory segment */
__u32 type; /* type of memory segment */
} __attribute__((packed));
-
- struct boot_params {
- struct screen_info screen_info; /* 0x000 */
- struct apm_bios_info apm_bios_info; /* 0x040 */
- __u8 _pad2[12]; /* 0x054 */
- struct ist_info ist_info; /* 0x060 */
- __u8 _pad3[16]; /* 0x070 */
- __u8 hd0_info[16]; /* obsolete! */ /* 0x080 */
- __u8 hd1_info[16]; /* obsolete! */ /* 0x090 */
- struct sys_desc_table sys_desc_table; /* 0x0a0 */
- __u8 _pad4[144]; /* 0x0b0 */
- struct edid_info edid_info; /* 0x140 */
- struct efi_info efi_info; /* 0x1c0 */
- __u32 alt_mem_k; /* 0x1e0 */
- __u32 scratch; /* Scratch field! */ /* 0x1e4 */
- __u8 e820_entries; /* 0x1e8 */
- __u8 eddbuf_entries; /* 0x1e9 */
- __u8 edd_mbr_sig_buf_entries; /* 0x1ea */
- __u8 _pad6[6]; /* 0x1eb */
- struct setup_header hdr; /* setup header */ /* 0x1f1 */
- __u8 _pad7[0x290-0x1f1-sizeof(struct setup_header)];
- __u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 0x290 */
- struct e820entry e820_map[E820MAX]; /* 0x2d0 */
- __u8 _pad8[48]; /* 0xcd0 */
- struct edd_info eddbuf[EDDMAXNR]; /* 0xd00 */
- __u8 _pad9[276]; /* 0xeec */
- } __attribute__((packed));
通过bios获取系统内存布局代码如下:
- static int detect_memory_e820(void)
- {
- int count = 0;
- u32 next = 0;
- u32 size, id;
- u8 err;
- struct e820entry *desc = boot_params.e820_map;
- do {
- size = sizeof(struct e820entry);
- /* Important: %edx is clobbered by some BIOSes,
- so it must be either used for the error output
- or explicitly marked clobbered. */
- asm("int $0x15; setc %0"
- : "=d" (err), "+b" (next), "=a" (id), "+c" (size),
- "=m" (*desc)
- : "D" (desc), "d" (SMAP), "a" (0xe820));
- /* BIOSes which terminate the chain with CF = 1 as opposed
- to %ebx = 0 don't always report the SMAP signature on
- the final, failing, probe. */
- if (err)
- break;
- /* Some BIOSes stop returning SMAP in the middle of
- the search loop. We don't know exactly how the BIOS
- screwed up the map at that point, we might have a
- partial map, the full map, or complete garbage, so
- just return failure. */
- if (id != SMAP) {
- count = 0;
- break;
- }
- count++;
- desc++;
- } while (next && count < E820MAX);
- return boot_params.e820_entries = count;
- }
函数关键部分解释如下:
07 获取启动参数 boot_params里的 e820_map 数组首地址。
15-18 通过中断0x15调用bios例程获得一个内存段的信息,这条语句是按照AT&T的汇编语法格式写的,具体语法可以查看相关资料。