Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | // 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)); 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) { 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 + 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)); 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); |