2007-07-11 23:18:50 +04:00
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
* Copyright 2007 rPath , Inc . - All Rights Reserved
2009-03-28 23:53:26 +03:00
* Copyright 2009 Intel Corporation ; author H . Peter Anvin
2007-07-11 23:18:50 +04:00
*
* This file is part of the Linux kernel , and is made available under
* the terms of the GNU General Public License version 2.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
* Memory detection code
*/
# include "boot.h"
# define SMAP 0x534d4150 /* ASCII "SMAP" */
static int detect_memory_e820 ( void )
{
2007-09-27 01:11:43 +04:00
int count = 0 ;
2009-04-02 05:13:46 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:50 +04:00
struct e820entry * desc = boot_params . e820_map ;
2009-05-21 22:46:16 +04:00
static struct e820entry buf ; /* static so it is zeroed */
2007-07-11 23:18:50 +04:00
2009-04-02 05:13:46 +04:00
initregs ( & ireg ) ;
ireg . ax = 0xe820 ;
ireg . cx = sizeof buf ;
ireg . edx = SMAP ;
ireg . di = ( size_t ) & buf ;
2009-04-01 22:35:00 +04:00
/*
2009-05-21 22:46:16 +04:00
* 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 .
2009-04-01 22:35:00 +04:00
*/
2007-07-11 23:18:50 +04:00
do {
2009-04-02 05:13:46 +04:00
intcall ( 0x15 , & ireg , & oreg ) ;
ireg . ebx = oreg . ebx ; /* for next iteration... */
2007-07-11 23:18:50 +04:00
2008-02-13 22:16:46 +03: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 . */
2009-04-02 05:13:46 +04:00
if ( oreg . eflags & X86_EFLAGS_CF )
2008-02-13 22:16:46 +03:00
break ;
2007-09-27 01:11:43 +04: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 . */
2009-04-02 05:13:46 +04:00
if ( oreg . eax ! = SMAP ) {
2007-09-27 01:11:43 +04:00
count = 0 ;
2007-07-11 23:18:50 +04:00
break ;
2007-09-27 01:11:43 +04:00
}
2007-07-11 23:18:50 +04:00
2009-05-21 22:46:16 +04:00
* desc + + = buf ;
2007-09-27 01:11:43 +04:00
count + + ;
2009-04-02 05:13:46 +04:00
} while ( ireg . ebx & & count < ARRAY_SIZE ( boot_params . e820_map ) ) ;
2007-07-11 23:18:50 +04:00
2007-09-27 01:11:43 +04:00
return boot_params . e820_entries = count ;
2007-07-11 23:18:50 +04:00
}
static int detect_memory_e801 ( void )
{
2009-04-02 05:13:46 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:50 +04:00
2009-04-02 05:13:46 +04:00
initregs ( & ireg ) ;
ireg . ax = 0xe801 ;
intcall ( 0x15 , & ireg , & oreg ) ;
2007-07-11 23:18:50 +04:00
2009-04-02 05:13:46 +04:00
if ( oreg . eflags & X86_EFLAGS_CF )
2007-07-11 23:18:50 +04:00
return - 1 ;
/* Do we really need to do this? */
2009-04-02 05:13:46 +04:00
if ( oreg . cx | | oreg . dx ) {
oreg . ax = oreg . cx ;
oreg . bx = oreg . dx ;
2007-07-11 23:18:50 +04:00
}
2009-04-02 05:13:46 +04:00
if ( oreg . ax > 15 * 1024 ) {
2007-07-11 23:18:50 +04:00
return - 1 ; /* Bogus! */
2009-04-02 05:13:46 +04:00
} else if ( oreg . ax = = 15 * 1024 ) {
2011-04-26 01:52:37 +04:00
boot_params . alt_mem_k = ( oreg . bx < < 6 ) + oreg . ax ;
2009-04-02 05:13:46 +04:00
} else {
/*
* This ignores memory above 16 MB if we have a memory
* hole there . If someone actually finds a machine
* with a memory hole at 16 MB and no support for
* 0E820 h they should probably generate a fake e820
* map .
*/
boot_params . alt_mem_k = oreg . ax ;
}
2007-07-11 23:18:50 +04:00
return 0 ;
}
static int detect_memory_88 ( void )
{
2009-04-02 05:13:46 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:50 +04:00
2009-04-02 05:13:46 +04:00
initregs ( & ireg ) ;
ireg . ah = 0x88 ;
intcall ( 0x15 , & ireg , & oreg ) ;
2007-07-11 23:18:50 +04:00
2009-04-02 05:13:46 +04:00
boot_params . screen_info . ext_mem_k = oreg . ax ;
2007-07-11 23:18:50 +04:00
2009-04-02 05:13:46 +04:00
return - ( oreg . eflags & X86_EFLAGS_CF ) ; /* 0 or -1 */
2007-07-11 23:18:50 +04:00
}
int detect_memory ( void )
{
2007-09-27 01:11:43 +04:00
int err = - 1 ;
2007-07-11 23:18:50 +04:00
if ( detect_memory_e820 ( ) > 0 )
2007-09-27 01:11:43 +04:00
err = 0 ;
2007-07-11 23:18:50 +04:00
if ( ! detect_memory_e801 ( ) )
2007-09-27 01:11:43 +04:00
err = 0 ;
if ( ! detect_memory_88 ( ) )
err = 0 ;
2007-07-11 23:18:50 +04:00
2007-09-27 01:11:43 +04:00
return err ;
2007-07-11 23:18:50 +04:00
}