2008-10-05 11:33:42 -07:00
# include <linux/module.h>
# include <linux/sched.h>
2008-10-05 12:09:03 -07:00
# include <linux/kthread.h>
# include <linux/workqueue.h>
2008-10-05 11:33:42 -07:00
# include <asm/e820.h>
# include <asm/proto.h>
/*
* Some BIOSes seem to corrupt the low 64 k of memory during events
* like suspend / resume and unplugging an HDMI cable . Reserve all
* remaining free memory in that area and fill it with a distinct
* pattern .
*/
# define MAX_SCAN_AREAS 8
static int __read_mostly memory_corruption_check = - 1 ;
static unsigned __read_mostly corruption_check_size = 64 * 1024 ;
static unsigned __read_mostly corruption_check_period = 60 ; /* seconds */
static struct e820entry scan_areas [ MAX_SCAN_AREAS ] ;
static int num_scan_areas ;
2008-10-05 12:21:32 -07:00
static __init int set_corruption_check ( char * arg )
2008-10-05 11:33:42 -07:00
{
char * end ;
memory_corruption_check = simple_strtol ( arg , & end , 10 ) ;
return ( * end = = 0 ) ? 0 : - EINVAL ;
}
early_param ( " memory_corruption_check " , set_corruption_check ) ;
2008-10-05 12:21:32 -07:00
static __init int set_corruption_check_period ( char * arg )
2008-10-05 11:33:42 -07:00
{
char * end ;
corruption_check_period = simple_strtoul ( arg , & end , 10 ) ;
return ( * end = = 0 ) ? 0 : - EINVAL ;
}
early_param ( " memory_corruption_check_period " , set_corruption_check_period ) ;
2008-10-05 12:21:32 -07:00
static __init int set_corruption_check_size ( char * arg )
2008-10-05 11:33:42 -07:00
{
char * end ;
unsigned size ;
size = memparse ( arg , & end ) ;
if ( * end = = ' \0 ' )
corruption_check_size = size ;
return ( size = = corruption_check_size ) ? 0 : - EINVAL ;
}
early_param ( " memory_corruption_check_size " , set_corruption_check_size ) ;
void __init setup_bios_corruption_check ( void )
{
u64 addr = PAGE_SIZE ; /* assume first page is reserved anyway */
if ( memory_corruption_check = = - 1 ) {
memory_corruption_check =
# ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
1
# else
0
# endif
;
}
if ( corruption_check_size = = 0 )
memory_corruption_check = 0 ;
if ( ! memory_corruption_check )
return ;
corruption_check_size = round_up ( corruption_check_size , PAGE_SIZE ) ;
while ( addr < corruption_check_size & & num_scan_areas < MAX_SCAN_AREAS ) {
u64 size ;
addr = find_e820_area_size ( addr , & size , PAGE_SIZE ) ;
2009-03-12 13:07:23 +00:00
if ( ! ( addr + 1 ) )
2008-10-05 11:33:42 -07:00
break ;
2009-03-14 14:32:41 -07:00
if ( addr > = corruption_check_size )
break ;
2008-10-05 11:33:42 -07:00
if ( ( addr + size ) > corruption_check_size )
size = corruption_check_size - addr ;
e820_update_range ( addr , size , E820_RAM , E820_RESERVED ) ;
scan_areas [ num_scan_areas ] . addr = addr ;
scan_areas [ num_scan_areas ] . size = size ;
num_scan_areas + + ;
/* Assume we've already mapped this early memory */
memset ( __va ( addr ) , 0 , size ) ;
addr + = size ;
}
printk ( KERN_INFO " Scanning %d areas for low memory corruption \n " ,
num_scan_areas ) ;
update_e820 ( ) ;
}
void check_for_bios_corruption ( void )
{
int i ;
int corruption = 0 ;
if ( ! memory_corruption_check )
return ;
for ( i = 0 ; i < num_scan_areas ; i + + ) {
unsigned long * addr = __va ( scan_areas [ i ] . addr ) ;
unsigned long size = scan_areas [ i ] . size ;
for ( ; size ; addr + + , size - = sizeof ( unsigned long ) ) {
if ( ! * addr )
continue ;
printk ( KERN_ERR " Corrupted low memory at %p (%lx phys) = %08lx \n " ,
addr , __pa ( addr ) , * addr ) ;
corruption = 1 ;
* addr = 0 ;
}
}
2008-10-05 12:21:32 -07:00
WARN_ONCE ( corruption , KERN_ERR " Memory corruption detected in low memory \n " ) ;
2008-10-05 11:33:42 -07:00
}
2008-10-05 12:09:03 -07:00
static void check_corruption ( struct work_struct * dummy ) ;
static DECLARE_DELAYED_WORK ( bios_check_work , check_corruption ) ;
static void check_corruption ( struct work_struct * dummy )
2008-10-05 11:33:42 -07:00
{
check_for_bios_corruption ( ) ;
2008-10-05 12:09:03 -07:00
schedule_delayed_work ( & bios_check_work ,
round_jiffies_relative ( corruption_check_period * HZ ) ) ;
2008-10-05 11:33:42 -07:00
}
2008-10-05 12:09:03 -07:00
static int start_periodic_check_for_corruption ( void )
2008-10-05 11:33:42 -07:00
{
if ( ! memory_corruption_check | | corruption_check_period = = 0 )
2008-10-05 12:09:03 -07:00
return 0 ;
2008-10-05 11:33:42 -07:00
printk ( KERN_INFO " Scanning for low memory corruption every %d seconds \n " ,
corruption_check_period ) ;
2008-10-05 12:09:03 -07:00
/* First time we run the checks right away */
schedule_delayed_work ( & bios_check_work , 0 ) ;
return 0 ;
2008-10-05 11:33:42 -07:00
}
2008-10-05 12:09:03 -07:00
module_init ( start_periodic_check_for_corruption ) ;
2008-10-05 11:33:42 -07:00