seteuid0's blog
Themed by Diary.
【zz】linux内核e820来源及相关知识

http://hi.baidu.com/ballmillsap/item/c2f3cf6f43d3220aa1cf0fe4e820简介操作系统内存布局中断 int 0x15e820简介 收藏本文系转载,原文地址:http://wangcong.org/blog/?p=320,其中附录部分 为本人所加…    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;}这个函数执行完毕后,boot_params.e820_map 就含有了系统内存布局图。函数关键部分解释如下:07 获取启动参数boot_params里的e820_map数 组首地址。15-18 通过中断0x15调用bios例程获得一个内存段的信息,这条语句是按照AT&T的汇编语法格式写的,具体语法可以查看相关资料。当然了,在arch/x86/boot/memory.c里,你还会看到另外两个利用int 0×15查询内存的函数,不过用途不一样了。凡是弄过操作系统启动这块的,肯定都有这么一个感慨:我的东西该往哪里放啊!怎么个放法啊!恩,或许Linux这种方式值得我们借鉴,它的虽然很科学,但也很复杂。那有啥办法呢,BIOS这块本来就已经很乱了!