mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 03:59:00 +00:00
ba2d394c60
It is quite pointless to use memcpy to copy two bytes, besides that this construct will also partially remove type and size sanity checks. Therefore simply use an assignment. Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
188 lines
4.3 KiB
C
188 lines
4.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Linux Guest Relocation (LGR) detection
|
|
*
|
|
* Copyright IBM Corp. 2012
|
|
* Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/export.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/facility.h>
|
|
#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
|
|
*/
|
|
static char lgr_page[PAGE_SIZE] __aligned(PAGE_SIZE);
|
|
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)
|
|
{
|
|
struct sysinfo_1_1_1 *si = (void *) lgr_page;
|
|
|
|
if (stsi(si, 1, 1, 1))
|
|
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)
|
|
{
|
|
struct sysinfo_2_2_2 *si = (void *) lgr_page;
|
|
|
|
if (stsi(si, 2, 2, 2))
|
|
return;
|
|
cpascii(lgr_info->name, si->name, sizeof(si->name));
|
|
lgr_info->lpar_number = si->lpar_number;
|
|
}
|
|
|
|
/*
|
|
* Fill LGR info with 3.2.2 stsi data
|
|
*/
|
|
static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
|
|
{
|
|
struct sysinfo_3_2_2 *si = (void *) lgr_page;
|
|
int i;
|
|
|
|
if (stsi(si, 3, 2, 2))
|
|
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)
|
|
{
|
|
int level;
|
|
|
|
memset(lgr_info, 0, sizeof(*lgr_info));
|
|
stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
|
|
level = stsi(NULL, 0, 0, 0);
|
|
lgr_info->level = level;
|
|
if (level >= 1)
|
|
lgr_stsi_1_1_1(lgr_info);
|
|
if (level >= 2)
|
|
lgr_stsi_2_2_2(lgr_info);
|
|
if (level >= 3)
|
|
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
|
|
*/
|
|
static void lgr_timer_fn(struct timer_list *unused)
|
|
{
|
|
lgr_info_log();
|
|
lgr_timer_set();
|
|
}
|
|
|
|
static struct timer_list lgr_timer;
|
|
|
|
/*
|
|
* Setup next LGR timer
|
|
*/
|
|
static void lgr_timer_set(void)
|
|
{
|
|
mod_timer(&lgr_timer, jiffies + msecs_to_jiffies(LGR_TIMER_INTERVAL_SECS * MSEC_PER_SEC));
|
|
}
|
|
|
|
/*
|
|
* 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));
|
|
if (!lgr_dbf)
|
|
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));
|
|
timer_setup(&lgr_timer, lgr_timer_fn, TIMER_DEFERRABLE);
|
|
lgr_timer_set();
|
|
return 0;
|
|
}
|
|
device_initcall(lgr_init);
|