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>
2010-08-25 13:39:17 -07:00
# include <linux/memblock.h>
2008-10-05 11:33:42 -07:00
# 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 */
2010-08-25 13:39:17 -07:00
static struct scan_area {
u64 addr ;
u64 size ;
} scan_areas [ MAX_SCAN_AREAS ] ;
2008-10-05 11:33:42 -07:00
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 )
{
2011-07-12 11:16:00 +02:00
phys_addr_t start , end ;
u64 i ;
2008-10-05 11:33:42 -07:00
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 ) ;
2011-07-12 11:16:00 +02:00
for_each_free_mem_range ( i , MAX_NUMNODES , & start , & end , NULL ) {
start = clamp_t ( phys_addr_t , round_up ( start , PAGE_SIZE ) ,
PAGE_SIZE , corruption_check_size ) ;
end = clamp_t ( phys_addr_t , round_down ( end , PAGE_SIZE ) ,
PAGE_SIZE , corruption_check_size ) ;
if ( start > = end )
continue ;
2008-10-05 11:33:42 -07:00
2011-07-12 11:16:00 +02:00
memblock_x86_reserve_range ( start , end , " SCAN RAM " ) ;
scan_areas [ num_scan_areas ] . addr = start ;
scan_areas [ num_scan_areas ] . size = end - start ;
2008-10-05 11:33:42 -07:00
/* Assume we've already mapped this early memory */
2011-07-12 11:16:00 +02:00
memset ( __va ( start ) , 0 , end - start ) ;
2008-10-05 11:33:42 -07:00
2011-07-12 11:16:00 +02:00
if ( + + num_scan_areas > = MAX_SCAN_AREAS )
break ;
2008-10-05 11:33:42 -07:00
}
2011-02-25 20:31:55 +00:00
if ( num_scan_areas )
printk ( KERN_INFO " Scanning %d areas for low memory corruption \n " , num_scan_areas ) ;
2008-10-05 11:33:42 -07:00
}
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 ,
2011-02-25 20:31:55 +00:00
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
{
2011-02-25 20:31:55 +00:00
if ( ! num_scan_areas | | ! 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