diff options
Diffstat (limited to 'arch/x86/boot/memory.c')
| -rw-r--r-- | arch/x86/boot/memory.c | 108 | 
1 files changed, 52 insertions, 56 deletions
diff --git a/arch/x86/boot/memory.c b/arch/x86/boot/memory.c index 5054c2ddd1a..cae3feb1035 100644 --- a/arch/x86/boot/memory.c +++ b/arch/x86/boot/memory.c @@ -17,43 +17,41 @@  #define SMAP	0x534d4150	/* ASCII "SMAP" */ -struct e820_ext_entry { -	struct e820entry std; -	u32 ext_flags; -} __attribute__((packed)); -  static int detect_memory_e820(void)  {  	int count = 0; -	u32 next = 0; -	u32 size, id, edi; -	u8 err; +	struct biosregs ireg, oreg;  	struct e820entry *desc = boot_params.e820_map; -	static struct e820_ext_entry buf; /* static so it is zeroed */ +	static struct e820entry buf; /* static so it is zeroed */ + +	initregs(&ireg); +	ireg.ax  = 0xe820; +	ireg.cx  = sizeof buf; +	ireg.edx = SMAP; +	ireg.di  = (size_t)&buf;  	/* -	 * Set this here so that if the BIOS doesn't change this field -	 * but still doesn't change %ecx, we're still okay... +	 * 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.  	 */ -	buf.ext_flags = 1;  	do { -		size = sizeof buf; - -		/* Important: %edx and %esi are clobbered by some BIOSes, -		   so they must be either used for the error output -		   or explicitly marked clobbered.  Given that, assume there -		   is something out there clobbering %ebp and %edi, too. */ -		asm("pushl %%ebp; int $0x15; popl %%ebp; setc %0" -		    : "=d" (err), "+b" (next), "=a" (id), "+c" (size), -		      "=D" (edi), "+m" (buf) -		    : "D" (&buf), "d" (SMAP), "a" (0xe820) -		    : "esi"); +		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 (err) +		if (oreg.eflags & X86_EFLAGS_CF)  			break;  		/* Some BIOSes stop returning SMAP in the middle of @@ -61,66 +59,64 @@ static int detect_memory_e820(void)  		   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) { +		if (oreg.eax != SMAP) {  			count = 0;  			break;  		} -		/* ACPI 3.0 added the extended flags support.  If bit 0 -		   in the extended flags is zero, we're supposed to simply -		   ignore the entry -- a backwards incompatible change! */ -		if (size > 20 && !(buf.ext_flags & 1)) -			continue; - -		*desc++ = buf.std; +		*desc++ = buf;  		count++; -	} while (next && count < ARRAY_SIZE(boot_params.e820_map)); +	} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_map));  	return boot_params.e820_entries = count;  }  static int detect_memory_e801(void)  { -	u16 ax, bx, cx, dx; -	u8 err; +	struct biosregs ireg, oreg; -	bx = cx = dx = 0; -	ax = 0xe801; -	asm("stc; int $0x15; setc %0" -	    : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); +	initregs(&ireg); +	ireg.ax = 0xe801; +	intcall(0x15, &ireg, &oreg); -	if (err) +	if (oreg.eflags & X86_EFLAGS_CF)  		return -1;  	/* Do we really need to do this? */ -	if (cx || dx) { -		ax = cx; -		bx = dx; +	if (oreg.cx || oreg.dx) { +		oreg.ax = oreg.cx; +		oreg.bx = oreg.dx;  	} -	if (ax > 15*1024) +	if (oreg.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; +	} else if (oreg.ax == 15*1024) { +		boot_params.alt_mem_k = (oreg.dx << 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)  { -	u16 ax; -	u8 err; +	struct biosregs ireg, oreg; -	ax = 0x8800; -	asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); +	initregs(&ireg); +	ireg.ah = 0x88; +	intcall(0x15, &ireg, &oreg); -	boot_params.screen_info.ext_mem_k = ax; +	boot_params.screen_info.ext_mem_k = oreg.ax; -	return -err; +	return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */  }  int detect_memory(void)  |