diff options
Diffstat (limited to 'arch/x86/boot/memory.c')
| -rw-r--r-- | arch/x86/boot/memory.c | 118 | 
1 files changed, 118 insertions, 0 deletions
diff --git a/arch/x86/boot/memory.c b/arch/x86/boot/memory.c new file mode 100644 index 00000000000..378353956b5 --- /dev/null +++ b/arch/x86/boot/memory.c @@ -0,0 +1,118 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + *   Copyright (C) 1991, 1992 Linus Torvalds + *   Copyright 2007 rPath, Inc. - All Rights Reserved + * + *   This file is part of the Linux kernel, and is made available under + *   the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/memory.c + * + * Memory detection code + */ + +#include "boot.h" + +#define SMAP	0x534d4150	/* ASCII "SMAP" */ + +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)); + +		/* 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; +		} + +		if (err) +			break; + +		count++; +		desc++; +	} while (next && count < E820MAX); + +	return boot_params.e820_entries = count; +} + +static int detect_memory_e801(void) +{ +	u16 ax, bx, cx, dx; +	u8 err; + +	bx = cx = dx = 0; +	ax = 0xe801; +	asm("stc; int $0x15; setc %0" +	    : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); + +	if (err) +		return -1; + +	/* Do we really need to do this? */ +	if (cx || dx) { +		ax = cx; +		bx = dx; +	} + +	if (ax > 15*1024) +		return -1;	/* Bogus! */ + +	/* 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 = (ax == 15*1024) ? (dx << 6)+ax : ax; + +	return 0; +} + +static int detect_memory_88(void) +{ +	u16 ax; +	u8 err; + +	ax = 0x8800; +	asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); + +	boot_params.screen_info.ext_mem_k = ax; + +	return -err; +} + +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; +}  |