/* -*- linux-c -*- ------------------------------------------------------- *1*2* Copyright (C) 1991, 1992 Linus Torvalds3* Copyright 2007 rPath, Inc. - All Rights Reserved4* Copyright 2009 Intel Corporation; author H. Peter Anvin5*6* This file is part of the Linux kernel, and is made available under7* the terms of the GNU General Public License version 2.8*9* ----------------------------------------------------------------------- */1011/*12* Memory detection code13*/1415#include "boot.h"1617#define SMAP 0x534d4150 /* ASCII "SMAP" */1819static int detect_memory_e820(void)20{21int count = 0;22struct biosregs ireg, oreg;23struct e820entry *desc = boot_params.e820_map;24static struct e820entry buf; /* static so it is zeroed */2526initregs(&ireg);27ireg.ax = 0xe820;28ireg.cx = sizeof buf;29ireg.edx = SMAP;30ireg.di = (size_t)&buf;3132/*33* Note: at least one BIOS is known which assumes that the34* buffer pointed to by one e820 call is the same one as35* the previous call, and only changes modified fields. Therefore,36* we use a temporary buffer and copy the results entry by entry.37*38* This routine deliberately does not try to account for39* ACPI 3+ extended attributes. This is because there are40* BIOSes in the field which report zero for the valid bit for41* all ranges, and we don't currently make any use of the42* other attribute bits. Revisit this if we see the extended43* attribute bits deployed in a meaningful way in the future.44*/4546do {47intcall(0x15, &ireg, &oreg);48ireg.ebx = oreg.ebx; /* for next iteration... */4950/* BIOSes which terminate the chain with CF = 1 as opposed51to %ebx = 0 don't always report the SMAP signature on52the final, failing, probe. */53if (oreg.eflags & X86_EFLAGS_CF)54break;5556/* Some BIOSes stop returning SMAP in the middle of57the search loop. We don't know exactly how the BIOS58screwed up the map at that point, we might have a59partial map, the full map, or complete garbage, so60just return failure. */61if (oreg.eax != SMAP) {62count = 0;63break;64}6566*desc++ = buf;67count++;68} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_map));6970return boot_params.e820_entries = count;71}7273static int detect_memory_e801(void)74{75struct biosregs ireg, oreg;7677initregs(&ireg);78ireg.ax = 0xe801;79intcall(0x15, &ireg, &oreg);8081if (oreg.eflags & X86_EFLAGS_CF)82return -1;8384/* Do we really need to do this? */85if (oreg.cx || oreg.dx) {86oreg.ax = oreg.cx;87oreg.bx = oreg.dx;88}8990if (oreg.ax > 15*1024) {91return -1; /* Bogus! */92} else if (oreg.ax == 15*1024) {93boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;94} else {95/*96* This ignores memory above 16MB if we have a memory97* hole there. If someone actually finds a machine98* with a memory hole at 16MB and no support for99* 0E820h they should probably generate a fake e820100* map.101*/102boot_params.alt_mem_k = oreg.ax;103}104105return 0;106}107108static int detect_memory_88(void)109{110struct biosregs ireg, oreg;111112initregs(&ireg);113ireg.ah = 0x88;114intcall(0x15, &ireg, &oreg);115116boot_params.screen_info.ext_mem_k = oreg.ax;117118return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */119}120121int detect_memory(void)122{123int err = -1;124125if (detect_memory_e820() > 0)126err = 0;127128if (!detect_memory_e801())129err = 0;130131if (!detect_memory_88())132err = 0;133134return err;135}136137138