2017-11-24 17:00:32 +03:00
// SPDX-License-Identifier: GPL-2.0
2012-03-11 19:59:32 +04:00
/*
* Linux Guest Relocation ( LGR ) detection
*
* Copyright IBM Corp . 2012
* Author ( s ) : Michael Holzheu < holzheu @ linux . vnet . ibm . com >
*/
2016-10-30 23:37:31 +03:00
# include <linux/init.h>
# include <linux/export.h>
2012-03-11 19:59:32 +04:00
# include <linux/timer.h>
# include <linux/slab.h>
2012-03-30 11:40:55 +04:00
# include <asm/facility.h>
2012-03-11 19:59:32 +04:00
# include <asm/sysinfo.h>
# include <asm/ebcdic.h>
# include <asm/debug.h>
# include <asm/ipl.h>
# define LGR_TIMER_INTERVAL_SECS (30 * 60)
# define VM_LEVEL_MAX 2 /* Maximum is 8, but we only record two levels */
/*
* LGR info : Contains stfle and stsi data
*/
struct lgr_info {
/* Bit field with facility information: 4 DWORDs are stored */
u64 stfle_fac_list [ 4 ] ;
/* Level of system (1 = CEC, 2 = LPAR, 3 = z/VM */
u32 level ;
/* Level 1: CEC info (stsi 1.1.1) */
char manufacturer [ 16 ] ;
char type [ 4 ] ;
char sequence [ 16 ] ;
char plant [ 4 ] ;
char model [ 16 ] ;
/* Level 2: LPAR info (stsi 2.2.2) */
u16 lpar_number ;
char name [ 8 ] ;
/* Level 3: VM info (stsi 3.2.2) */
u8 vm_count ;
struct {
char name [ 8 ] ;
char cpi [ 16 ] ;
} vm [ VM_LEVEL_MAX ] ;
} __packed __aligned ( 8 ) ;
/*
* LGR globals
*/
2012-07-20 13:15:01 +04:00
static char lgr_page [ PAGE_SIZE ] __aligned ( PAGE_SIZE ) ;
2012-03-11 19:59:32 +04:00
static struct lgr_info lgr_info_last ;
static struct lgr_info lgr_info_cur ;
static struct debug_info * lgr_dbf ;
/*
* Copy buffer and then convert it to ASCII
*/
static void cpascii ( char * dst , char * src , int size )
{
memcpy ( dst , src , size ) ;
EBCASC ( dst , size ) ;
}
/*
* Fill LGR info with 1.1 .1 stsi data
*/
static void lgr_stsi_1_1_1 ( struct lgr_info * lgr_info )
{
2012-07-20 13:15:01 +04:00
struct sysinfo_1_1_1 * si = ( void * ) lgr_page ;
2012-03-11 19:59:32 +04:00
2012-09-06 16:42:13 +04:00
if ( stsi ( si , 1 , 1 , 1 ) )
2012-03-11 19:59:32 +04:00
return ;
cpascii ( lgr_info - > manufacturer , si - > manufacturer ,
sizeof ( si - > manufacturer ) ) ;
cpascii ( lgr_info - > type , si - > type , sizeof ( si - > type ) ) ;
cpascii ( lgr_info - > model , si - > model , sizeof ( si - > model ) ) ;
cpascii ( lgr_info - > sequence , si - > sequence , sizeof ( si - > sequence ) ) ;
cpascii ( lgr_info - > plant , si - > plant , sizeof ( si - > plant ) ) ;
}
/*
* Fill LGR info with 2.2 .2 stsi data
*/
static void lgr_stsi_2_2_2 ( struct lgr_info * lgr_info )
{
2012-07-20 13:15:01 +04:00
struct sysinfo_2_2_2 * si = ( void * ) lgr_page ;
2012-03-11 19:59:32 +04:00
2012-09-06 16:42:13 +04:00
if ( stsi ( si , 2 , 2 , 2 ) )
2012-03-11 19:59:32 +04:00
return ;
cpascii ( lgr_info - > name , si - > name , sizeof ( si - > name ) ) ;
memcpy ( & lgr_info - > lpar_number , & si - > lpar_number ,
sizeof ( lgr_info - > lpar_number ) ) ;
}
/*
* Fill LGR info with 3.2 .2 stsi data
*/
static void lgr_stsi_3_2_2 ( struct lgr_info * lgr_info )
{
2012-07-20 13:15:01 +04:00
struct sysinfo_3_2_2 * si = ( void * ) lgr_page ;
2012-03-11 19:59:32 +04:00
int i ;
2012-09-06 16:42:13 +04:00
if ( stsi ( si , 3 , 2 , 2 ) )
2012-03-11 19:59:32 +04:00
return ;
for ( i = 0 ; i < min_t ( u8 , si - > count , VM_LEVEL_MAX ) ; i + + ) {
cpascii ( lgr_info - > vm [ i ] . name , si - > vm [ i ] . name ,
sizeof ( si - > vm [ i ] . name ) ) ;
cpascii ( lgr_info - > vm [ i ] . cpi , si - > vm [ i ] . cpi ,
sizeof ( si - > vm [ i ] . cpi ) ) ;
}
lgr_info - > vm_count = si - > count ;
}
/*
* Fill LGR info with current data
*/
static void lgr_info_get ( struct lgr_info * lgr_info )
{
2012-09-06 16:42:13 +04:00
int level ;
2012-03-11 19:59:32 +04:00
memset ( lgr_info , 0 , sizeof ( * lgr_info ) ) ;
stfle ( lgr_info - > stfle_fac_list , ARRAY_SIZE ( lgr_info - > stfle_fac_list ) ) ;
2012-09-06 16:42:13 +04:00
level = stsi ( NULL , 0 , 0 , 0 ) ;
lgr_info - > level = level ;
if ( level > = 1 )
2012-03-11 19:59:32 +04:00
lgr_stsi_1_1_1 ( lgr_info ) ;
2012-09-06 16:42:13 +04:00
if ( level > = 2 )
2012-03-11 19:59:32 +04:00
lgr_stsi_2_2_2 ( lgr_info ) ;
2012-09-06 16:42:13 +04:00
if ( level > = 3 )
2012-03-11 19:59:32 +04:00
lgr_stsi_3_2_2 ( lgr_info ) ;
}
/*
* Check if LGR info has changed and if yes log new LGR info to s390dbf
*/
void lgr_info_log ( void )
{
static DEFINE_SPINLOCK ( lgr_info_lock ) ;
unsigned long flags ;
if ( ! spin_trylock_irqsave ( & lgr_info_lock , flags ) )
return ;
lgr_info_get ( & lgr_info_cur ) ;
if ( memcmp ( & lgr_info_last , & lgr_info_cur , sizeof ( lgr_info_cur ) ) ! = 0 ) {
debug_event ( lgr_dbf , 1 , & lgr_info_cur , sizeof ( lgr_info_cur ) ) ;
lgr_info_last = lgr_info_cur ;
}
spin_unlock_irqrestore ( & lgr_info_lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( lgr_info_log ) ;
static void lgr_timer_set ( void ) ;
/*
* LGR timer callback
*/
2017-10-05 02:27:00 +03:00
static void lgr_timer_fn ( struct timer_list * unused )
2012-03-11 19:59:32 +04:00
{
lgr_info_log ( ) ;
lgr_timer_set ( ) ;
}
2017-10-05 02:27:00 +03:00
static struct timer_list lgr_timer ;
2012-03-11 19:59:32 +04:00
/*
* Setup next LGR timer
*/
static void lgr_timer_set ( void )
{
mod_timer ( & lgr_timer , jiffies + LGR_TIMER_INTERVAL_SECS * HZ ) ;
}
/*
* Initialize LGR : Add s390dbf , write initial lgr_info and setup timer
*/
static int __init lgr_init ( void )
{
lgr_dbf = debug_register ( " lgr " , 1 , 1 , sizeof ( struct lgr_info ) ) ;
2012-07-20 13:15:01 +04:00
if ( ! lgr_dbf )
2012-03-11 19:59:32 +04:00
return - ENOMEM ;
debug_register_view ( lgr_dbf , & debug_hex_ascii_view ) ;
lgr_info_get ( & lgr_info_last ) ;
debug_event ( lgr_dbf , 1 , & lgr_info_last , sizeof ( lgr_info_last ) ) ;
2017-10-05 02:27:00 +03:00
timer_setup ( & lgr_timer , lgr_timer_fn , TIMER_DEFERRABLE ) ;
2012-03-11 19:59:32 +04:00
lgr_timer_set ( ) ;
return 0 ;
}
2016-10-30 23:37:31 +03:00
device_initcall ( lgr_init ) ;