e820 is shorthand to refer to the facility by which the BIOS of x86-based computer systems reports the memory map to the operating system or boot loader.
It is accessed via the int 15h call, by setting the AX register to value E820 in hexadecimal. It reports which memory address ranges are usable and which are reserved for use by the BIOS.
static int detect_memory_e820(void)
{
int count = 0;
struct biosregs ireg, oreg;
struct e820entry *desc = boot_params.e820_map;
static struct e820entry 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_map));
return boot_params.e820_entries = count;
}
char *__init e820__memory_setup_default(void)
{
char *who = "BIOS-e820";
/*
* Try to copy the BIOS-supplied E820-map.
*
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
if (append_e820_table(boot_params.e820_table, boot_params.e820_entries) < 0) {
u64 mem_size;
/* Compare results from other methods and take the one that gives more RAM: */
if (boot_params.alt_mem_k < boot_params.screen_info.ext_mem_k) {
mem_size = boot_params.screen_info.ext_mem_k;
who = "BIOS-88";
} else {
mem_size = boot_params.alt_mem_k;
who = "BIOS-e801";
}
e820_table->nr_entries = 0;
e820__range_add(0, LOWMEMSIZE(), E820_TYPE_RAM);
e820__range_add(HIGH_MEMORY, mem_size << 10, E820_TYPE_RAM);
}
/* We just appended a lot of ranges, sanitize the table: */
e820__update_table(e820_table);
return who;
}
/* Retrieve the e820 table from BIOS */
main(), arch/x86/boot/main.c, called from arch/x86/boot/header.S
detect_memory()
detect_memory_e820(), save bios info to boot_params.e820_entries
/* store and sort e820 table to kernel area */
start_kernel()
setup_arch()
e820__memory_setup(),
x86_init.resources.memory_setup() -> e820__memory_setup_default(), store and sort e820 table
e820__print_table()
e820__reserve_setup_data();
e820__finish_early_params();
e820_add_kernel_range();
e820__memblock_setup();
0 type1 type2 type1 0 type3 type4 type4 type3
| | | | | | | |
v v v v v v v v
(start1, start2, end2, end1, start3, start4, end4, end3)
e820_search_gap()
这个函数用来搜索e820中最大的空洞。在x86平台上用来查找设备的MMIO空间。
逻辑比较简单
i = nr_map - 1;
end last
|<--- gap --->|
(start1, end2) (start2, end2) (start3, end3)
i = nr_map - 2;
end last
|<--- gap --->|
(start1, end2) (start2, end2) (start3, end3)
i = nr_map - 3;
end last
|<--- gap --->|
(start1, end2) (start2, end2) (start3, end3)
From: Wei Yang <richard.weiyang@gmail.com>
Date: Sun, 18 Dec 2016 22:16:52 +0800
Subject: [PATCH] x86/e820: fix e820_search_gap() in case the start_addr is a
gap
For example, we have a e820 map like this:
e820: [mem 000000000000000000-0x000000000000000f] usable
e820: [mem 0x00000000e0000000-0x00000000e000000f] usable
The biggest gap should be:
[mem 0x00000010-0xdfffffff]
While if start_addr is passed by a value bigger than 0x10, current version
will tell following range is the biggest gap:
[mem 0xe0000010-0xffffffff]
The patch fixes this by set end to start_addr, when start_addr is bigger
than end.
Signed-off-by: Wei Yang <richard.weiyang@gmail.com>
---
arch/x86/kernel/e820.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index f4fb197..1826591 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -602,7 +602,7 @@ __init int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize,
unsigned long long end = start + e820->map[i].size;
if (end < start_addr)
- continue;
+ end = start_addr;
/*
* Since "last" is at most 4GB, we know we'll
--
2.5.0