diff options
Diffstat (limited to 'arch/x86/boot/memory.c')
-rw-r--r-- | arch/x86/boot/memory.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/arch/x86/boot/memory.c b/arch/x86/boot/memory.c new file mode 100644 index 000000000..d9c28c87e --- /dev/null +++ b/arch/x86/boot/memory.c @@ -0,0 +1,136 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * Copyright 2009 Intel Corporation; author H. Peter Anvin + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * Memory detection code + */ + +#include "boot.h" + +#define SMAP 0x534d4150 /* ASCII "SMAP" */ + +static int 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)); + + return boot_params.e820_entries = count; +} + +static int detect_memory_e801(void) +{ + struct biosregs ireg, oreg; + + initregs(&ireg); + ireg.ax = 0xe801; + intcall(0x15, &ireg, &oreg); + + if (oreg.eflags & X86_EFLAGS_CF) + return -1; + + /* Do we really need to do this? */ + if (oreg.cx || oreg.dx) { + oreg.ax = oreg.cx; + oreg.bx = oreg.dx; + } + + if (oreg.ax > 15*1024) { + return -1; /* Bogus! */ + } else if (oreg.ax == 15*1024) { + boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax; + } else { + /* + * This ignores memory above 16MB if we have a memory + * hole there. If someone actually finds a machine + * with a memory hole at 16MB and no support for + * 0E820h they should probably generate a fake e820 + * map. + */ + boot_params.alt_mem_k = oreg.ax; + } + + return 0; +} + +static int detect_memory_88(void) +{ + struct biosregs ireg, oreg; + + initregs(&ireg); + ireg.ah = 0x88; + intcall(0x15, &ireg, &oreg); + + boot_params.screen_info.ext_mem_k = oreg.ax; + + return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */ +} + +int detect_memory(void) +{ + int err = -1; + + if (detect_memory_e820() > 0) + err = 0; + + if (!detect_memory_e801()) + err = 0; + + if (!detect_memory_88()) + err = 0; + + return err; +} |