| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 1 | /* -*- linux-c -*- ------------------------------------------------------- * | 
 | 2 |  * | 
 | 3 |  *   Copyright (C) 1991, 1992 Linus Torvalds | 
 | 4 |  *   Copyright 2007 rPath, Inc. - All Rights Reserved | 
 | 5 |  * | 
 | 6 |  *   This file is part of the Linux kernel, and is made available under | 
 | 7 |  *   the terms of the GNU General Public License version 2. | 
 | 8 |  * | 
 | 9 |  * ----------------------------------------------------------------------- */ | 
 | 10 |  | 
 | 11 | /* | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 12 |  * Memory detection code | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | #include "boot.h" | 
 | 16 |  | 
 | 17 | #define SMAP	0x534d4150	/* ASCII "SMAP" */ | 
 | 18 |  | 
 | 19 | static int detect_memory_e820(void) | 
 | 20 | { | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 21 | 	int count = 0; | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 22 | 	u32 next = 0; | 
 | 23 | 	u32 size, id; | 
 | 24 | 	u8 err; | 
 | 25 | 	struct e820entry *desc = boot_params.e820_map; | 
 | 26 |  | 
 | 27 | 	do { | 
 | 28 | 		size = sizeof(struct e820entry); | 
| H. Peter Anvin | 4ee5b10 | 2007-09-27 17:17:12 -0700 | [diff] [blame] | 29 |  | 
 | 30 | 		/* Important: %edx is clobbered by some BIOSes, | 
 | 31 | 		   so it must be either used for the error output | 
 | 32 | 		   or explicitly marked clobbered. */ | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 33 | 		asm("int $0x15; setc %0" | 
| H. Peter Anvin | 4ee5b10 | 2007-09-27 17:17:12 -0700 | [diff] [blame] | 34 | 		    : "=d" (err), "+b" (next), "=a" (id), "+c" (size), | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 35 | 		      "=m" (*desc) | 
| H. Peter Anvin | 4ee5b10 | 2007-09-27 17:17:12 -0700 | [diff] [blame] | 36 | 		    : "D" (desc), "d" (SMAP), "a" (0xe820)); | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 37 |  | 
| H. Peter Anvin | 829157b | 2008-02-13 11:16:46 -0800 | [diff] [blame] | 38 | 		/* BIOSes which terminate the chain with CF = 1 as opposed | 
 | 39 | 		   to %ebx = 0 don't always report the SMAP signature on | 
 | 40 | 		   the final, failing, probe. */ | 
 | 41 | 		if (err) | 
 | 42 | 			break; | 
 | 43 |  | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 44 | 		/* Some BIOSes stop returning SMAP in the middle of | 
 | 45 | 		   the search loop.  We don't know exactly how the BIOS | 
 | 46 | 		   screwed up the map at that point, we might have a | 
 | 47 | 		   partial map, the full map, or complete garbage, so | 
 | 48 | 		   just return failure. */ | 
 | 49 | 		if (id != SMAP) { | 
 | 50 | 			count = 0; | 
 | 51 | 			break; | 
 | 52 | 		} | 
 | 53 |  | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 54 | 		count++; | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 55 | 		desc++; | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 56 | 	} while (next && count < E820MAX); | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 57 |  | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 58 | 	return boot_params.e820_entries = count; | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 59 | } | 
 | 60 |  | 
 | 61 | static int detect_memory_e801(void) | 
 | 62 | { | 
 | 63 | 	u16 ax, bx, cx, dx; | 
 | 64 | 	u8 err; | 
 | 65 |  | 
 | 66 | 	bx = cx = dx = 0; | 
 | 67 | 	ax = 0xe801; | 
 | 68 | 	asm("stc; int $0x15; setc %0" | 
 | 69 | 	    : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); | 
 | 70 |  | 
 | 71 | 	if (err) | 
 | 72 | 		return -1; | 
 | 73 |  | 
 | 74 | 	/* Do we really need to do this? */ | 
 | 75 | 	if (cx || dx) { | 
 | 76 | 		ax = cx; | 
 | 77 | 		bx = dx; | 
 | 78 | 	} | 
 | 79 |  | 
 | 80 | 	if (ax > 15*1024) | 
 | 81 | 		return -1;	/* Bogus! */ | 
 | 82 |  | 
 | 83 | 	/* This ignores memory above 16MB if we have a memory hole | 
 | 84 | 	   there.  If someone actually finds a machine with a memory | 
 | 85 | 	   hole at 16MB and no support for 0E820h they should probably | 
 | 86 | 	   generate a fake e820 map. */ | 
 | 87 | 	boot_params.alt_mem_k = (ax == 15*1024) ? (dx << 6)+ax : ax; | 
 | 88 |  | 
 | 89 | 	return 0; | 
 | 90 | } | 
 | 91 |  | 
 | 92 | static int detect_memory_88(void) | 
 | 93 | { | 
 | 94 | 	u16 ax; | 
 | 95 | 	u8 err; | 
 | 96 |  | 
 | 97 | 	ax = 0x8800; | 
 | 98 | 	asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); | 
 | 99 |  | 
 | 100 | 	boot_params.screen_info.ext_mem_k = ax; | 
 | 101 |  | 
 | 102 | 	return -err; | 
 | 103 | } | 
 | 104 |  | 
 | 105 | int detect_memory(void) | 
 | 106 | { | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 107 | 	int err = -1; | 
 | 108 |  | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 109 | 	if (detect_memory_e820() > 0) | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 110 | 		err = 0; | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 111 |  | 
 | 112 | 	if (!detect_memory_e801()) | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 113 | 		err = 0; | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 114 |  | 
| H. Peter Anvin | 2efa33f | 2007-09-26 14:11:43 -0700 | [diff] [blame] | 115 | 	if (!detect_memory_88()) | 
 | 116 | 		err = 0; | 
 | 117 |  | 
 | 118 | 	return err; | 
| H. Peter Anvin | 449f2ab | 2007-07-11 12:18:50 -0700 | [diff] [blame] | 119 | } |