Blame view
arch/s390/kernel/lgr.c
4.34 KB
a17ae4c3a
|
1 |
// SPDX-License-Identifier: GPL-2.0 |
3ab121ab1
|
2 3 4 5 6 7 |
/* * Linux Guest Relocation (LGR) detection * * Copyright IBM Corp. 2012 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> */ |
8ba8b05f5
|
8 9 |
#include <linux/init.h> #include <linux/export.h> |
3ab121ab1
|
10 11 |
#include <linux/timer.h> #include <linux/slab.h> |
1e3cab2f2
|
12 |
#include <asm/facility.h> |
3ab121ab1
|
13 14 |
#include <asm/sysinfo.h> #include <asm/ebcdic.h> |
3ab121ab1
|
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#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 */ |
2b7547578
|
49 |
static char lgr_page[PAGE_SIZE] __aligned(PAGE_SIZE); |
3ab121ab1
|
50 51 52 53 54 |
static struct lgr_info lgr_info_last; static struct lgr_info lgr_info_cur; static struct debug_info *lgr_dbf; /* |
3ab121ab1
|
55 56 57 58 59 60 61 62 63 64 65 66 67 |
* 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) { |
2b7547578
|
68 |
struct sysinfo_1_1_1 *si = (void *) lgr_page; |
3ab121ab1
|
69 |
|
caf757c60
|
70 |
if (stsi(si, 1, 1, 1)) |
3ab121ab1
|
71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
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) { |
2b7547578
|
85 |
struct sysinfo_2_2_2 *si = (void *) lgr_page; |
3ab121ab1
|
86 |
|
caf757c60
|
87 |
if (stsi(si, 2, 2, 2)) |
3ab121ab1
|
88 89 90 91 92 93 94 95 96 97 98 |
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) { |
2b7547578
|
99 |
struct sysinfo_3_2_2 *si = (void *) lgr_page; |
3ab121ab1
|
100 |
int i; |
caf757c60
|
101 |
if (stsi(si, 3, 2, 2)) |
3ab121ab1
|
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
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) { |
caf757c60
|
117 |
int level; |
3ab121ab1
|
118 119 |
memset(lgr_info, 0, sizeof(*lgr_info)); stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list)); |
caf757c60
|
120 121 122 |
level = stsi(NULL, 0, 0, 0); lgr_info->level = level; if (level >= 1) |
3ab121ab1
|
123 |
lgr_stsi_1_1_1(lgr_info); |
caf757c60
|
124 |
if (level >= 2) |
3ab121ab1
|
125 |
lgr_stsi_2_2_2(lgr_info); |
caf757c60
|
126 |
if (level >= 3) |
3ab121ab1
|
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
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 */ |
5cd79d6ab
|
154 |
static void lgr_timer_fn(struct timer_list *unused) |
3ab121ab1
|
155 156 157 158 |
{ lgr_info_log(); lgr_timer_set(); } |
5cd79d6ab
|
159 |
static struct timer_list lgr_timer; |
3ab121ab1
|
160 161 162 163 164 165 |
/* * Setup next LGR timer */ static void lgr_timer_set(void) { |
0188d08a4
|
166 |
mod_timer(&lgr_timer, jiffies + msecs_to_jiffies(LGR_TIMER_INTERVAL_SECS * MSEC_PER_SEC)); |
3ab121ab1
|
167 168 169 170 171 172 173 |
} /* * Initialize LGR: Add s390dbf, write initial lgr_info and setup timer */ static int __init lgr_init(void) { |
3ab121ab1
|
174 |
lgr_dbf = debug_register("lgr", 1, 1, sizeof(struct lgr_info)); |
2b7547578
|
175 |
if (!lgr_dbf) |
3ab121ab1
|
176 |
return -ENOMEM; |
3ab121ab1
|
177 178 179 |
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)); |
5cd79d6ab
|
180 |
timer_setup(&lgr_timer, lgr_timer_fn, TIMER_DEFERRABLE); |
3ab121ab1
|
181 182 183 |
lgr_timer_set(); return 0; } |
8ba8b05f5
|
184 |
device_initcall(lgr_init); |