1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Hypervisor filesystem for Linux on s390. Diag 204 and 224 4 * implementation. 5 * 6 * Copyright IBM Corp. 2006, 2008 7 * Author(s): Michael Holzheu <holzheu@de.ibm.com> 8 */ 9 10 #define KMSG_COMPONENT "hypfs" 11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 12 13 #include <linux/types.h> 14 #include <linux/errno.h> 15 #include <linux/slab.h> 16 #include <linux/string.h> 17 #include <linux/vmalloc.h> 18 #include <linux/mm.h> 19 #include <asm/machine.h> 20 #include <asm/diag.h> 21 #include <asm/ebcdic.h> 22 #include "hypfs_diag.h" 23 #include "hypfs.h" 24 25 #define TMP_SIZE 64 /* size of temporary buffers */ 26 27 static char *diag224_cpu_names; /* diag 224 name table */ 28 static int diag224_idx2name(int index, char *name); 29 30 /* 31 * DIAG 204 member access functions. 32 * 33 * Since we have two different diag 204 data formats for old and new s390 34 * machines, we do not access the structs directly, but use getter functions for 35 * each struct member instead. This should make the code more readable. 36 */ 37 38 /* Time information block */ 39 40 static inline int info_blk_hdr__size(enum diag204_format type) 41 { 42 if (type == DIAG204_INFO_SIMPLE) 43 return sizeof(struct diag204_info_blk_hdr); 44 else /* DIAG204_INFO_EXT */ 45 return sizeof(struct diag204_x_info_blk_hdr); 46 } 47 48 static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr) 49 { 50 if (type == DIAG204_INFO_SIMPLE) 51 return ((struct diag204_info_blk_hdr *)hdr)->npar; 52 else /* DIAG204_INFO_EXT */ 53 return ((struct diag204_x_info_blk_hdr *)hdr)->npar; 54 } 55 56 static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr) 57 { 58 if (type == DIAG204_INFO_SIMPLE) 59 return ((struct diag204_info_blk_hdr *)hdr)->flags; 60 else /* DIAG204_INFO_EXT */ 61 return ((struct diag204_x_info_blk_hdr *)hdr)->flags; 62 } 63 64 /* Partition header */ 65 66 static inline int part_hdr__size(enum diag204_format type) 67 { 68 if (type == DIAG204_INFO_SIMPLE) 69 return sizeof(struct diag204_part_hdr); 70 else /* DIAG204_INFO_EXT */ 71 return sizeof(struct diag204_x_part_hdr); 72 } 73 74 static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr) 75 { 76 if (type == DIAG204_INFO_SIMPLE) 77 return ((struct diag204_part_hdr *)hdr)->cpus; 78 else /* DIAG204_INFO_EXT */ 79 return ((struct diag204_x_part_hdr *)hdr)->rcpus; 80 } 81 82 static inline void part_hdr__part_name(enum diag204_format type, void *hdr, 83 char *name) 84 { 85 if (type == DIAG204_INFO_SIMPLE) 86 memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name, 87 DIAG204_LPAR_NAME_LEN); 88 else /* DIAG204_INFO_EXT */ 89 memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name, 90 DIAG204_LPAR_NAME_LEN); 91 EBCASC(name, DIAG204_LPAR_NAME_LEN); 92 name[DIAG204_LPAR_NAME_LEN] = 0; 93 strim(name); 94 } 95 96 /* CPU info block */ 97 98 static inline int cpu_info__size(enum diag204_format type) 99 { 100 if (type == DIAG204_INFO_SIMPLE) 101 return sizeof(struct diag204_cpu_info); 102 else /* DIAG204_INFO_EXT */ 103 return sizeof(struct diag204_x_cpu_info); 104 } 105 106 static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr) 107 { 108 if (type == DIAG204_INFO_SIMPLE) 109 return ((struct diag204_cpu_info *)hdr)->ctidx; 110 else /* DIAG204_INFO_EXT */ 111 return ((struct diag204_x_cpu_info *)hdr)->ctidx; 112 } 113 114 static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr) 115 { 116 if (type == DIAG204_INFO_SIMPLE) 117 return ((struct diag204_cpu_info *)hdr)->cpu_addr; 118 else /* DIAG204_INFO_EXT */ 119 return ((struct diag204_x_cpu_info *)hdr)->cpu_addr; 120 } 121 122 static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr) 123 { 124 if (type == DIAG204_INFO_SIMPLE) 125 return ((struct diag204_cpu_info *)hdr)->acc_time; 126 else /* DIAG204_INFO_EXT */ 127 return ((struct diag204_x_cpu_info *)hdr)->acc_time; 128 } 129 130 static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr) 131 { 132 if (type == DIAG204_INFO_SIMPLE) 133 return ((struct diag204_cpu_info *)hdr)->lp_time; 134 else /* DIAG204_INFO_EXT */ 135 return ((struct diag204_x_cpu_info *)hdr)->lp_time; 136 } 137 138 static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr) 139 { 140 if (type == DIAG204_INFO_SIMPLE) 141 return 0; /* online_time not available in simple info */ 142 else /* DIAG204_INFO_EXT */ 143 return ((struct diag204_x_cpu_info *)hdr)->online_time; 144 } 145 146 /* Physical header */ 147 148 static inline int phys_hdr__size(enum diag204_format type) 149 { 150 if (type == DIAG204_INFO_SIMPLE) 151 return sizeof(struct diag204_phys_hdr); 152 else /* DIAG204_INFO_EXT */ 153 return sizeof(struct diag204_x_phys_hdr); 154 } 155 156 static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr) 157 { 158 if (type == DIAG204_INFO_SIMPLE) 159 return ((struct diag204_phys_hdr *)hdr)->cpus; 160 else /* DIAG204_INFO_EXT */ 161 return ((struct diag204_x_phys_hdr *)hdr)->cpus; 162 } 163 164 /* Physical CPU info block */ 165 166 static inline int phys_cpu__size(enum diag204_format type) 167 { 168 if (type == DIAG204_INFO_SIMPLE) 169 return sizeof(struct diag204_phys_cpu); 170 else /* DIAG204_INFO_EXT */ 171 return sizeof(struct diag204_x_phys_cpu); 172 } 173 174 static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr) 175 { 176 if (type == DIAG204_INFO_SIMPLE) 177 return ((struct diag204_phys_cpu *)hdr)->cpu_addr; 178 else /* DIAG204_INFO_EXT */ 179 return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr; 180 } 181 182 static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr) 183 { 184 if (type == DIAG204_INFO_SIMPLE) 185 return ((struct diag204_phys_cpu *)hdr)->mgm_time; 186 else /* DIAG204_INFO_EXT */ 187 return ((struct diag204_x_phys_cpu *)hdr)->mgm_time; 188 } 189 190 static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr) 191 { 192 if (type == DIAG204_INFO_SIMPLE) 193 return ((struct diag204_phys_cpu *)hdr)->ctidx; 194 else /* DIAG204_INFO_EXT */ 195 return ((struct diag204_x_phys_cpu *)hdr)->ctidx; 196 } 197 198 /* 199 * Functions to create the directory structure 200 * ******************************************* 201 */ 202 203 static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info) 204 { 205 struct dentry *cpu_dir; 206 char buffer[TMP_SIZE]; 207 void *rc; 208 209 snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_get_info_type(), 210 cpu_info)); 211 cpu_dir = hypfs_mkdir(cpus_dir, buffer); 212 if (IS_ERR(cpu_dir)) 213 return PTR_ERR(cpu_dir); 214 rc = hypfs_create_u64(cpu_dir, "mgmtime", 215 cpu_info__acc_time(diag204_get_info_type(), cpu_info) - 216 cpu_info__lp_time(diag204_get_info_type(), cpu_info)); 217 if (IS_ERR(rc)) 218 return PTR_ERR(rc); 219 rc = hypfs_create_u64(cpu_dir, "cputime", 220 cpu_info__lp_time(diag204_get_info_type(), cpu_info)); 221 if (IS_ERR(rc)) 222 return PTR_ERR(rc); 223 if (diag204_get_info_type() == DIAG204_INFO_EXT) { 224 rc = hypfs_create_u64(cpu_dir, "onlinetime", 225 cpu_info__online_time(diag204_get_info_type(), 226 cpu_info)); 227 if (IS_ERR(rc)) 228 return PTR_ERR(rc); 229 } 230 diag224_idx2name(cpu_info__ctidx(diag204_get_info_type(), cpu_info), buffer); 231 rc = hypfs_create_str(cpu_dir, "type", buffer); 232 return PTR_ERR_OR_ZERO(rc); 233 } 234 235 static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr) 236 { 237 struct dentry *cpus_dir; 238 struct dentry *lpar_dir; 239 char lpar_name[DIAG204_LPAR_NAME_LEN + 1]; 240 void *cpu_info; 241 int i; 242 243 part_hdr__part_name(diag204_get_info_type(), part_hdr, lpar_name); 244 lpar_name[DIAG204_LPAR_NAME_LEN] = 0; 245 lpar_dir = hypfs_mkdir(systems_dir, lpar_name); 246 if (IS_ERR(lpar_dir)) 247 return lpar_dir; 248 cpus_dir = hypfs_mkdir(lpar_dir, "cpus"); 249 if (IS_ERR(cpus_dir)) 250 return cpus_dir; 251 cpu_info = part_hdr + part_hdr__size(diag204_get_info_type()); 252 for (i = 0; i < part_hdr__rcpus(diag204_get_info_type(), part_hdr); i++) { 253 int rc; 254 255 rc = hypfs_create_cpu_files(cpus_dir, cpu_info); 256 if (rc) 257 return ERR_PTR(rc); 258 cpu_info += cpu_info__size(diag204_get_info_type()); 259 } 260 return cpu_info; 261 } 262 263 static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info) 264 { 265 struct dentry *cpu_dir; 266 char buffer[TMP_SIZE]; 267 void *rc; 268 269 snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_get_info_type(), 270 cpu_info)); 271 cpu_dir = hypfs_mkdir(cpus_dir, buffer); 272 if (IS_ERR(cpu_dir)) 273 return PTR_ERR(cpu_dir); 274 rc = hypfs_create_u64(cpu_dir, "mgmtime", 275 phys_cpu__mgm_time(diag204_get_info_type(), cpu_info)); 276 if (IS_ERR(rc)) 277 return PTR_ERR(rc); 278 diag224_idx2name(phys_cpu__ctidx(diag204_get_info_type(), cpu_info), buffer); 279 rc = hypfs_create_str(cpu_dir, "type", buffer); 280 return PTR_ERR_OR_ZERO(rc); 281 } 282 283 static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr) 284 { 285 int i; 286 void *cpu_info; 287 struct dentry *cpus_dir; 288 289 cpus_dir = hypfs_mkdir(parent_dir, "cpus"); 290 if (IS_ERR(cpus_dir)) 291 return cpus_dir; 292 cpu_info = phys_hdr + phys_hdr__size(diag204_get_info_type()); 293 for (i = 0; i < phys_hdr__cpus(diag204_get_info_type(), phys_hdr); i++) { 294 int rc; 295 296 rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info); 297 if (rc) 298 return ERR_PTR(rc); 299 cpu_info += phys_cpu__size(diag204_get_info_type()); 300 } 301 return cpu_info; 302 } 303 304 int hypfs_diag_create_files(struct dentry *root) 305 { 306 struct dentry *systems_dir, *hyp_dir; 307 void *time_hdr, *part_hdr; 308 void *buffer, *ptr; 309 int i, rc, pages; 310 311 buffer = diag204_get_buffer(diag204_get_info_type(), &pages); 312 if (IS_ERR(buffer)) 313 return PTR_ERR(buffer); 314 rc = diag204_store(buffer, pages); 315 if (rc) 316 return rc; 317 318 systems_dir = hypfs_mkdir(root, "systems"); 319 if (IS_ERR(systems_dir)) { 320 rc = PTR_ERR(systems_dir); 321 goto err_out; 322 } 323 time_hdr = (struct x_info_blk_hdr *)buffer; 324 part_hdr = time_hdr + info_blk_hdr__size(diag204_get_info_type()); 325 for (i = 0; i < info_blk_hdr__npar(diag204_get_info_type(), time_hdr); i++) { 326 part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr); 327 if (IS_ERR(part_hdr)) { 328 rc = PTR_ERR(part_hdr); 329 goto err_out; 330 } 331 } 332 if (info_blk_hdr__flags(diag204_get_info_type(), time_hdr) & 333 DIAG204_LPAR_PHYS_FLG) { 334 ptr = hypfs_create_phys_files(root, part_hdr); 335 if (IS_ERR(ptr)) { 336 rc = PTR_ERR(ptr); 337 goto err_out; 338 } 339 } 340 hyp_dir = hypfs_mkdir(root, "hyp"); 341 if (IS_ERR(hyp_dir)) { 342 rc = PTR_ERR(hyp_dir); 343 goto err_out; 344 } 345 ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor"); 346 if (IS_ERR(ptr)) { 347 rc = PTR_ERR(ptr); 348 goto err_out; 349 } 350 rc = 0; 351 352 err_out: 353 return rc; 354 } 355 356 /* Diagnose 224 functions */ 357 358 static int diag224_idx2name(int index, char *name) 359 { 360 memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN), 361 DIAG204_CPU_NAME_LEN); 362 name[DIAG204_CPU_NAME_LEN] = 0; 363 strim(name); 364 return 0; 365 } 366 367 static int diag224_get_name_table(void) 368 { 369 /* memory must be below 2GB */ 370 diag224_cpu_names = (char *)__get_free_page(GFP_KERNEL | GFP_DMA); 371 if (!diag224_cpu_names) 372 return -ENOMEM; 373 if (diag224(diag224_cpu_names)) { 374 free_page((unsigned long)diag224_cpu_names); 375 return -EOPNOTSUPP; 376 } 377 EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16); 378 return 0; 379 } 380 381 static void diag224_delete_name_table(void) 382 { 383 free_page((unsigned long)diag224_cpu_names); 384 } 385 386 int __init __hypfs_diag_fs_init(void) 387 { 388 if (machine_is_lpar()) 389 return diag224_get_name_table(); 390 return 0; 391 } 392 393 void __hypfs_diag_fs_exit(void) 394 { 395 diag224_delete_name_table(); 396 } 397