1 /* 2 * Hypervisor filesystem for Linux on s390. Diag 204 and 224 3 * implementation. 4 * 5 * Copyright IBM Corp. 2006, 2008 6 * Author(s): Michael Holzheu <holzheu@de.ibm.com> 7 */ 8 9 #define KMSG_COMPONENT "hypfs" 10 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11 12 #include <linux/types.h> 13 #include <linux/errno.h> 14 #include <linux/slab.h> 15 #include <linux/string.h> 16 #include <linux/vmalloc.h> 17 #include <linux/mm.h> 18 #include <asm/diag.h> 19 #include <asm/ebcdic.h> 20 #include "hypfs.h" 21 22 #define TMP_SIZE 64 /* size of temporary buffers */ 23 24 #define DBFS_D204_HDR_VERSION 0 25 26 static char *diag224_cpu_names; /* diag 224 name table */ 27 static enum diag204_sc diag204_store_sc; /* used subcode for store */ 28 static enum diag204_format diag204_info_type; /* used diag 204 data format */ 29 30 static void *diag204_buf; /* 4K aligned buffer for diag204 data */ 31 static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */ 32 static int diag204_buf_pages; /* number of pages for diag204 data */ 33 34 static struct dentry *dbfs_d204_file; 35 36 /* 37 * DIAG 204 member access functions. 38 * 39 * Since we have two different diag 204 data formats for old and new s390 40 * machines, we do not access the structs directly, but use getter functions for 41 * each struct member instead. This should make the code more readable. 42 */ 43 44 /* Time information block */ 45 46 static inline int info_blk_hdr__size(enum diag204_format type) 47 { 48 if (type == DIAG204_INFO_SIMPLE) 49 return sizeof(struct diag204_info_blk_hdr); 50 else /* DIAG204_INFO_EXT */ 51 return sizeof(struct diag204_x_info_blk_hdr); 52 } 53 54 static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr) 55 { 56 if (type == DIAG204_INFO_SIMPLE) 57 return ((struct diag204_info_blk_hdr *)hdr)->npar; 58 else /* DIAG204_INFO_EXT */ 59 return ((struct diag204_x_info_blk_hdr *)hdr)->npar; 60 } 61 62 static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr) 63 { 64 if (type == DIAG204_INFO_SIMPLE) 65 return ((struct diag204_info_blk_hdr *)hdr)->flags; 66 else /* DIAG204_INFO_EXT */ 67 return ((struct diag204_x_info_blk_hdr *)hdr)->flags; 68 } 69 70 static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr) 71 { 72 if (type == DIAG204_INFO_SIMPLE) 73 return ((struct diag204_info_blk_hdr *)hdr)->phys_cpus; 74 else /* DIAG204_INFO_EXT */ 75 return ((struct diag204_x_info_blk_hdr *)hdr)->phys_cpus; 76 } 77 78 /* Partition header */ 79 80 static inline int part_hdr__size(enum diag204_format type) 81 { 82 if (type == DIAG204_INFO_SIMPLE) 83 return sizeof(struct diag204_part_hdr); 84 else /* DIAG204_INFO_EXT */ 85 return sizeof(struct diag204_x_part_hdr); 86 } 87 88 static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr) 89 { 90 if (type == DIAG204_INFO_SIMPLE) 91 return ((struct diag204_part_hdr *)hdr)->cpus; 92 else /* DIAG204_INFO_EXT */ 93 return ((struct diag204_x_part_hdr *)hdr)->rcpus; 94 } 95 96 static inline void part_hdr__part_name(enum diag204_format type, void *hdr, 97 char *name) 98 { 99 if (type == DIAG204_INFO_SIMPLE) 100 memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name, 101 DIAG204_LPAR_NAME_LEN); 102 else /* DIAG204_INFO_EXT */ 103 memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name, 104 DIAG204_LPAR_NAME_LEN); 105 EBCASC(name, DIAG204_LPAR_NAME_LEN); 106 name[DIAG204_LPAR_NAME_LEN] = 0; 107 strim(name); 108 } 109 110 /* CPU info block */ 111 112 static inline int cpu_info__size(enum diag204_format type) 113 { 114 if (type == DIAG204_INFO_SIMPLE) 115 return sizeof(struct diag204_cpu_info); 116 else /* DIAG204_INFO_EXT */ 117 return sizeof(struct diag204_x_cpu_info); 118 } 119 120 static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr) 121 { 122 if (type == DIAG204_INFO_SIMPLE) 123 return ((struct diag204_cpu_info *)hdr)->ctidx; 124 else /* DIAG204_INFO_EXT */ 125 return ((struct diag204_x_cpu_info *)hdr)->ctidx; 126 } 127 128 static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr) 129 { 130 if (type == DIAG204_INFO_SIMPLE) 131 return ((struct diag204_cpu_info *)hdr)->cpu_addr; 132 else /* DIAG204_INFO_EXT */ 133 return ((struct diag204_x_cpu_info *)hdr)->cpu_addr; 134 } 135 136 static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr) 137 { 138 if (type == DIAG204_INFO_SIMPLE) 139 return ((struct diag204_cpu_info *)hdr)->acc_time; 140 else /* DIAG204_INFO_EXT */ 141 return ((struct diag204_x_cpu_info *)hdr)->acc_time; 142 } 143 144 static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr) 145 { 146 if (type == DIAG204_INFO_SIMPLE) 147 return ((struct diag204_cpu_info *)hdr)->lp_time; 148 else /* DIAG204_INFO_EXT */ 149 return ((struct diag204_x_cpu_info *)hdr)->lp_time; 150 } 151 152 static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr) 153 { 154 if (type == DIAG204_INFO_SIMPLE) 155 return 0; /* online_time not available in simple info */ 156 else /* DIAG204_INFO_EXT */ 157 return ((struct diag204_x_cpu_info *)hdr)->online_time; 158 } 159 160 /* Physical header */ 161 162 static inline int phys_hdr__size(enum diag204_format type) 163 { 164 if (type == DIAG204_INFO_SIMPLE) 165 return sizeof(struct diag204_phys_hdr); 166 else /* DIAG204_INFO_EXT */ 167 return sizeof(struct diag204_x_phys_hdr); 168 } 169 170 static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr) 171 { 172 if (type == DIAG204_INFO_SIMPLE) 173 return ((struct diag204_phys_hdr *)hdr)->cpus; 174 else /* DIAG204_INFO_EXT */ 175 return ((struct diag204_x_phys_hdr *)hdr)->cpus; 176 } 177 178 /* Physical CPU info block */ 179 180 static inline int phys_cpu__size(enum diag204_format type) 181 { 182 if (type == DIAG204_INFO_SIMPLE) 183 return sizeof(struct diag204_phys_cpu); 184 else /* DIAG204_INFO_EXT */ 185 return sizeof(struct diag204_x_phys_cpu); 186 } 187 188 static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr) 189 { 190 if (type == DIAG204_INFO_SIMPLE) 191 return ((struct diag204_phys_cpu *)hdr)->cpu_addr; 192 else /* DIAG204_INFO_EXT */ 193 return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr; 194 } 195 196 static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr) 197 { 198 if (type == DIAG204_INFO_SIMPLE) 199 return ((struct diag204_phys_cpu *)hdr)->mgm_time; 200 else /* DIAG204_INFO_EXT */ 201 return ((struct diag204_x_phys_cpu *)hdr)->mgm_time; 202 } 203 204 static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr) 205 { 206 if (type == DIAG204_INFO_SIMPLE) 207 return ((struct diag204_phys_cpu *)hdr)->ctidx; 208 else /* DIAG204_INFO_EXT */ 209 return ((struct diag204_x_phys_cpu *)hdr)->ctidx; 210 } 211 212 /* Diagnose 204 functions */ 213 /* 214 * For the old diag subcode 4 with simple data format we have to use real 215 * memory. If we use subcode 6 or 7 with extended data format, we can (and 216 * should) use vmalloc, since we need a lot of memory in that case. Currently 217 * up to 93 pages! 218 */ 219 220 static void diag204_free_buffer(void) 221 { 222 if (!diag204_buf) 223 return; 224 if (diag204_buf_vmalloc) { 225 vfree(diag204_buf_vmalloc); 226 diag204_buf_vmalloc = NULL; 227 } else { 228 free_pages((unsigned long) diag204_buf, 0); 229 } 230 diag204_buf = NULL; 231 } 232 233 static void *page_align_ptr(void *ptr) 234 { 235 return (void *) PAGE_ALIGN((unsigned long) ptr); 236 } 237 238 static void *diag204_alloc_vbuf(int pages) 239 { 240 /* The buffer has to be page aligned! */ 241 diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1)); 242 if (!diag204_buf_vmalloc) 243 return ERR_PTR(-ENOMEM); 244 diag204_buf = page_align_ptr(diag204_buf_vmalloc); 245 diag204_buf_pages = pages; 246 return diag204_buf; 247 } 248 249 static void *diag204_alloc_rbuf(void) 250 { 251 diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0); 252 if (!diag204_buf) 253 return ERR_PTR(-ENOMEM); 254 diag204_buf_pages = 1; 255 return diag204_buf; 256 } 257 258 static void *diag204_get_buffer(enum diag204_format fmt, int *pages) 259 { 260 if (diag204_buf) { 261 *pages = diag204_buf_pages; 262 return diag204_buf; 263 } 264 if (fmt == DIAG204_INFO_SIMPLE) { 265 *pages = 1; 266 return diag204_alloc_rbuf(); 267 } else {/* DIAG204_INFO_EXT */ 268 *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 269 (unsigned long)DIAG204_INFO_EXT, 0, NULL); 270 if (*pages <= 0) 271 return ERR_PTR(-ENOSYS); 272 else 273 return diag204_alloc_vbuf(*pages); 274 } 275 } 276 277 /* 278 * diag204_probe() has to find out, which type of diagnose 204 implementation 279 * we have on our machine. Currently there are three possible scanarios: 280 * - subcode 4 + simple data format (only one page) 281 * - subcode 4-6 + extended data format 282 * - subcode 4-7 + extended data format 283 * 284 * Subcode 5 is used to retrieve the size of the data, provided by subcodes 285 * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 286 * to subcode 6 it provides also information about secondary cpus. 287 * In order to get as much information as possible, we first try 288 * subcode 7, then 6 and if both fail, we use subcode 4. 289 */ 290 291 static int diag204_probe(void) 292 { 293 void *buf; 294 int pages, rc; 295 296 buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 297 if (!IS_ERR(buf)) { 298 if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 299 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 300 diag204_store_sc = DIAG204_SUBC_STIB7; 301 diag204_info_type = DIAG204_INFO_EXT; 302 goto out; 303 } 304 if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 305 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 306 diag204_store_sc = DIAG204_SUBC_STIB6; 307 diag204_info_type = DIAG204_INFO_EXT; 308 goto out; 309 } 310 diag204_free_buffer(); 311 } 312 313 /* subcodes 6 and 7 failed, now try subcode 4 */ 314 315 buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 316 if (IS_ERR(buf)) { 317 rc = PTR_ERR(buf); 318 goto fail_alloc; 319 } 320 if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 321 (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 322 diag204_store_sc = DIAG204_SUBC_STIB4; 323 diag204_info_type = DIAG204_INFO_SIMPLE; 324 goto out; 325 } else { 326 rc = -ENOSYS; 327 goto fail_store; 328 } 329 out: 330 rc = 0; 331 fail_store: 332 diag204_free_buffer(); 333 fail_alloc: 334 return rc; 335 } 336 337 static int diag204_do_store(void *buf, int pages) 338 { 339 int rc; 340 341 rc = diag204((unsigned long) diag204_store_sc | 342 (unsigned long) diag204_info_type, pages, buf); 343 return rc < 0 ? -ENOSYS : 0; 344 } 345 346 static void *diag204_store(void) 347 { 348 void *buf; 349 int pages, rc; 350 351 buf = diag204_get_buffer(diag204_info_type, &pages); 352 if (IS_ERR(buf)) 353 goto out; 354 rc = diag204_do_store(buf, pages); 355 if (rc) 356 return ERR_PTR(rc); 357 out: 358 return buf; 359 } 360 361 /* Diagnose 224 functions */ 362 363 static int diag224_get_name_table(void) 364 { 365 /* memory must be below 2GB */ 366 diag224_cpu_names = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); 367 if (!diag224_cpu_names) 368 return -ENOMEM; 369 if (diag224(diag224_cpu_names)) { 370 free_page((unsigned long) diag224_cpu_names); 371 return -EOPNOTSUPP; 372 } 373 EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16); 374 return 0; 375 } 376 377 static void diag224_delete_name_table(void) 378 { 379 free_page((unsigned long) diag224_cpu_names); 380 } 381 382 static int diag224_idx2name(int index, char *name) 383 { 384 memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN), 385 DIAG204_CPU_NAME_LEN); 386 name[DIAG204_CPU_NAME_LEN] = 0; 387 strim(name); 388 return 0; 389 } 390 391 struct dbfs_d204_hdr { 392 u64 len; /* Length of d204 buffer without header */ 393 u16 version; /* Version of header */ 394 u8 sc; /* Used subcode */ 395 char reserved[53]; 396 } __attribute__ ((packed)); 397 398 struct dbfs_d204 { 399 struct dbfs_d204_hdr hdr; /* 64 byte header */ 400 char buf[]; /* d204 buffer */ 401 } __attribute__ ((packed)); 402 403 static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 404 { 405 struct dbfs_d204 *d204; 406 int rc, buf_size; 407 void *base; 408 409 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 410 base = vzalloc(buf_size); 411 if (!base) 412 return -ENOMEM; 413 d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); 414 rc = diag204_do_store(d204->buf, diag204_buf_pages); 415 if (rc) { 416 vfree(base); 417 return rc; 418 } 419 d204->hdr.version = DBFS_D204_HDR_VERSION; 420 d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 421 d204->hdr.sc = diag204_store_sc; 422 *data = d204; 423 *data_free_ptr = base; 424 *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 425 return 0; 426 } 427 428 static struct hypfs_dbfs_file dbfs_file_d204 = { 429 .name = "diag_204", 430 .data_create = dbfs_d204_create, 431 .data_free = vfree, 432 }; 433 434 __init int hypfs_diag_init(void) 435 { 436 int rc; 437 438 if (diag204_probe()) { 439 pr_err("The hardware system does not support hypfs\n"); 440 return -ENODATA; 441 } 442 if (diag204_info_type == DIAG204_INFO_EXT) { 443 rc = hypfs_dbfs_create_file(&dbfs_file_d204); 444 if (rc) 445 return rc; 446 } 447 if (MACHINE_IS_LPAR) { 448 rc = diag224_get_name_table(); 449 if (rc) { 450 pr_err("The hardware system does not provide all " 451 "functions required by hypfs\n"); 452 debugfs_remove(dbfs_d204_file); 453 return rc; 454 } 455 } 456 return 0; 457 } 458 459 void hypfs_diag_exit(void) 460 { 461 debugfs_remove(dbfs_d204_file); 462 diag224_delete_name_table(); 463 diag204_free_buffer(); 464 hypfs_dbfs_remove_file(&dbfs_file_d204); 465 } 466 467 /* 468 * Functions to create the directory structure 469 * ******************************************* 470 */ 471 472 static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info) 473 { 474 struct dentry *cpu_dir; 475 char buffer[TMP_SIZE]; 476 void *rc; 477 478 snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type, 479 cpu_info)); 480 cpu_dir = hypfs_mkdir(cpus_dir, buffer); 481 rc = hypfs_create_u64(cpu_dir, "mgmtime", 482 cpu_info__acc_time(diag204_info_type, cpu_info) - 483 cpu_info__lp_time(diag204_info_type, cpu_info)); 484 if (IS_ERR(rc)) 485 return PTR_ERR(rc); 486 rc = hypfs_create_u64(cpu_dir, "cputime", 487 cpu_info__lp_time(diag204_info_type, cpu_info)); 488 if (IS_ERR(rc)) 489 return PTR_ERR(rc); 490 if (diag204_info_type == DIAG204_INFO_EXT) { 491 rc = hypfs_create_u64(cpu_dir, "onlinetime", 492 cpu_info__online_time(diag204_info_type, 493 cpu_info)); 494 if (IS_ERR(rc)) 495 return PTR_ERR(rc); 496 } 497 diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer); 498 rc = hypfs_create_str(cpu_dir, "type", buffer); 499 return PTR_RET(rc); 500 } 501 502 static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr) 503 { 504 struct dentry *cpus_dir; 505 struct dentry *lpar_dir; 506 char lpar_name[DIAG204_LPAR_NAME_LEN + 1]; 507 void *cpu_info; 508 int i; 509 510 part_hdr__part_name(diag204_info_type, part_hdr, lpar_name); 511 lpar_name[DIAG204_LPAR_NAME_LEN] = 0; 512 lpar_dir = hypfs_mkdir(systems_dir, lpar_name); 513 if (IS_ERR(lpar_dir)) 514 return lpar_dir; 515 cpus_dir = hypfs_mkdir(lpar_dir, "cpus"); 516 if (IS_ERR(cpus_dir)) 517 return cpus_dir; 518 cpu_info = part_hdr + part_hdr__size(diag204_info_type); 519 for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) { 520 int rc; 521 rc = hypfs_create_cpu_files(cpus_dir, cpu_info); 522 if (rc) 523 return ERR_PTR(rc); 524 cpu_info += cpu_info__size(diag204_info_type); 525 } 526 return cpu_info; 527 } 528 529 static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info) 530 { 531 struct dentry *cpu_dir; 532 char buffer[TMP_SIZE]; 533 void *rc; 534 535 snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type, 536 cpu_info)); 537 cpu_dir = hypfs_mkdir(cpus_dir, buffer); 538 if (IS_ERR(cpu_dir)) 539 return PTR_ERR(cpu_dir); 540 rc = hypfs_create_u64(cpu_dir, "mgmtime", 541 phys_cpu__mgm_time(diag204_info_type, cpu_info)); 542 if (IS_ERR(rc)) 543 return PTR_ERR(rc); 544 diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer); 545 rc = hypfs_create_str(cpu_dir, "type", buffer); 546 return PTR_RET(rc); 547 } 548 549 static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr) 550 { 551 int i; 552 void *cpu_info; 553 struct dentry *cpus_dir; 554 555 cpus_dir = hypfs_mkdir(parent_dir, "cpus"); 556 if (IS_ERR(cpus_dir)) 557 return cpus_dir; 558 cpu_info = phys_hdr + phys_hdr__size(diag204_info_type); 559 for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) { 560 int rc; 561 rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info); 562 if (rc) 563 return ERR_PTR(rc); 564 cpu_info += phys_cpu__size(diag204_info_type); 565 } 566 return cpu_info; 567 } 568 569 int hypfs_diag_create_files(struct dentry *root) 570 { 571 struct dentry *systems_dir, *hyp_dir; 572 void *time_hdr, *part_hdr; 573 int i, rc; 574 void *buffer, *ptr; 575 576 buffer = diag204_store(); 577 if (IS_ERR(buffer)) 578 return PTR_ERR(buffer); 579 580 systems_dir = hypfs_mkdir(root, "systems"); 581 if (IS_ERR(systems_dir)) { 582 rc = PTR_ERR(systems_dir); 583 goto err_out; 584 } 585 time_hdr = (struct x_info_blk_hdr *)buffer; 586 part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type); 587 for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) { 588 part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr); 589 if (IS_ERR(part_hdr)) { 590 rc = PTR_ERR(part_hdr); 591 goto err_out; 592 } 593 } 594 if (info_blk_hdr__flags(diag204_info_type, time_hdr) & 595 DIAG204_LPAR_PHYS_FLG) { 596 ptr = hypfs_create_phys_files(root, part_hdr); 597 if (IS_ERR(ptr)) { 598 rc = PTR_ERR(ptr); 599 goto err_out; 600 } 601 } 602 hyp_dir = hypfs_mkdir(root, "hyp"); 603 if (IS_ERR(hyp_dir)) { 604 rc = PTR_ERR(hyp_dir); 605 goto err_out; 606 } 607 ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor"); 608 if (IS_ERR(ptr)) { 609 rc = PTR_ERR(ptr); 610 goto err_out; 611 } 612 rc = 0; 613 614 err_out: 615 return rc; 616 } 617