| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | /* -*- 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 	int count = 0; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 	u32 next = 0; | 
					
						
							|  |  |  | 	u32 size, id; | 
					
						
							|  |  |  | 	u8 err; | 
					
						
							|  |  |  | 	struct e820entry *desc = boot_params.e820_map; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		size = sizeof(struct e820entry); | 
					
						
							| 
									
										
										
										
											2007-09-27 17:17:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Important: %edx is clobbered by some BIOSes,
 | 
					
						
							|  |  |  | 		   so it must be either used for the error output | 
					
						
							|  |  |  | 		   or explicitly marked clobbered. */ | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 		asm("int $0x15; setc %0" | 
					
						
							| 
									
										
										
										
											2007-09-27 17:17:12 -07:00
										 |  |  | 		    : "=d" (err), "+b" (next), "=a" (id), "+c" (size), | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 		      "=m" (*desc) | 
					
						
							| 
									
										
										
										
											2007-09-27 17:17:12 -07:00
										 |  |  | 		    : "D" (desc), "d" (SMAP), "a" (0xe820)); | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 11:16:46 -08:00
										 |  |  | 		/* 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 (err) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 		/* 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; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 		count++; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 		desc++; | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 	} while (next && count < E820MAX); | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 	return boot_params.e820_entries = count; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 	int err = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 	if (detect_memory_e820() > 0) | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 		err = 0; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!detect_memory_e801()) | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 		err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!detect_memory_88()) | 
					
						
							|  |  |  | 		err = 0; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-26 14:11:43 -07:00
										 |  |  | 	return err; | 
					
						
							| 
									
										
										
										
											2007-07-11 12:18:50 -07:00
										 |  |  | } |