Linux通过BIOS的中断向量0x15来探测物理内存布局,有三种常见的方式,0xe820、0x801、0x88三种,默认Linux会同时使用这三者来进行探测。

void detect_memory(void) {
	detect_memory_e820();
	detect_memory_e801();
	detect_memory_88();
}

e820探测

/*
 * The E820 memory region entry of the boot protocol ABI:
 */
struct boot_e820_entry {
	__u64 addr;
	__u64 size;
	__u32 type;
} __attribute__((packed));

// 最多128个
struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]; /* 0x2d0 */

static void detect_memory_e820(void)
{
	int count = 0;
	struct biosregs ireg, oreg;
	struct boot_e820_entry *desc = boot_params.e820_table;
	static struct boot_e820_entry buf; /* static so it is zeroed */

	initregs(&ireg);
	ireg.ax  = 0xe820;
	ireg.cx  = sizeof(buf);
	ireg.edx = SMAP;
	ireg.di  = (size_t)&buf;

	/*
	 * Note: at least one BIOS is known which assumes that the
	 * buffer pointed to by one e820 call is the same one as
	 * the previous call, and only changes modified fields.  Therefore,
	 * we use a temporary buffer and copy the results entry by entry.
	 *
	 * This routine deliberately does not try to account for
	 * ACPI 3+ extended attributes.  This is because there are
	 * BIOSes in the field which report zero for the valid bit for
	 * all ranges, and we don't currently make any use of the
	 * other attribute bits.  Revisit this if we see the extended
	 * attribute bits deployed in a meaningful way in the future.
	 */

	do {
		intcall(0x15, &ireg, &oreg);
		ireg.ebx = oreg.ebx; /* for next iteration... */

		/* 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 (oreg.eflags & X86_EFLAGS_CF)
			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 (oreg.eax != SMAP) {
			count = 0;
			break;
		}

		*desc++ = buf;
		count++;
	} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_table));
	
	// 记录E820探测到的内存块数目
	boot_params.e820_entries = count;
}

e820探测到的内存信息:

root@tianqian:~/code/rust-for-linux# dmesg |grep "e820"
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000009fffffff] usable
[    0.000000] BIOS-e820: [mem 0x00000000a0000000-0x00000000a111ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000a1120000-0x00000000bffcefff] usable
[    0.000000] BIOS-e820: [mem 0x00000000bffcf000-0x00000000bfffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fffbc000-0x00000000ffffffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000100000000-0x0000001f3fffffff] usable
[    0.000000] BIOS-e820: [mem 0x0000001f40000000-0x000000203fffffff] reserved
[    0.000608] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
[    0.000611] e820: remove [mem 0x000a0000-0x000fffff] usable
[    0.916047] e820: reserve RAM buffer [mem 0x0009fc00-0x0009ffff]
[    0.916049] e820: reserve RAM buffer [mem 0xbffcf000-0xbfffffff]
root@tianqian:~/code/rust-for-linux#

只有内存类型为usable的才能被操作系统所使用。