0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define KMSG_COMPONENT "hypfs"
0011 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0012
0013 #include <linux/types.h>
0014 #include <linux/errno.h>
0015 #include <linux/slab.h>
0016 #include <linux/string.h>
0017 #include <linux/vmalloc.h>
0018 #include <linux/mm.h>
0019 #include <asm/diag.h>
0020 #include <asm/ebcdic.h>
0021 #include "hypfs.h"
0022
0023 #define TMP_SIZE 64
0024
0025 #define DBFS_D204_HDR_VERSION 0
0026
0027 static char *diag224_cpu_names;
0028 static enum diag204_sc diag204_store_sc;
0029 static enum diag204_format diag204_info_type;
0030
0031 static void *diag204_buf;
0032 static void *diag204_buf_vmalloc;
0033 static int diag204_buf_pages;
0034
0035 static struct dentry *dbfs_d204_file;
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 static inline int info_blk_hdr__size(enum diag204_format type)
0048 {
0049 if (type == DIAG204_INFO_SIMPLE)
0050 return sizeof(struct diag204_info_blk_hdr);
0051 else
0052 return sizeof(struct diag204_x_info_blk_hdr);
0053 }
0054
0055 static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
0056 {
0057 if (type == DIAG204_INFO_SIMPLE)
0058 return ((struct diag204_info_blk_hdr *)hdr)->npar;
0059 else
0060 return ((struct diag204_x_info_blk_hdr *)hdr)->npar;
0061 }
0062
0063 static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
0064 {
0065 if (type == DIAG204_INFO_SIMPLE)
0066 return ((struct diag204_info_blk_hdr *)hdr)->flags;
0067 else
0068 return ((struct diag204_x_info_blk_hdr *)hdr)->flags;
0069 }
0070
0071 static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
0072 {
0073 if (type == DIAG204_INFO_SIMPLE)
0074 return ((struct diag204_info_blk_hdr *)hdr)->phys_cpus;
0075 else
0076 return ((struct diag204_x_info_blk_hdr *)hdr)->phys_cpus;
0077 }
0078
0079
0080
0081 static inline int part_hdr__size(enum diag204_format type)
0082 {
0083 if (type == DIAG204_INFO_SIMPLE)
0084 return sizeof(struct diag204_part_hdr);
0085 else
0086 return sizeof(struct diag204_x_part_hdr);
0087 }
0088
0089 static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
0090 {
0091 if (type == DIAG204_INFO_SIMPLE)
0092 return ((struct diag204_part_hdr *)hdr)->cpus;
0093 else
0094 return ((struct diag204_x_part_hdr *)hdr)->rcpus;
0095 }
0096
0097 static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
0098 char *name)
0099 {
0100 if (type == DIAG204_INFO_SIMPLE)
0101 memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name,
0102 DIAG204_LPAR_NAME_LEN);
0103 else
0104 memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name,
0105 DIAG204_LPAR_NAME_LEN);
0106 EBCASC(name, DIAG204_LPAR_NAME_LEN);
0107 name[DIAG204_LPAR_NAME_LEN] = 0;
0108 strim(name);
0109 }
0110
0111
0112
0113 static inline int cpu_info__size(enum diag204_format type)
0114 {
0115 if (type == DIAG204_INFO_SIMPLE)
0116 return sizeof(struct diag204_cpu_info);
0117 else
0118 return sizeof(struct diag204_x_cpu_info);
0119 }
0120
0121 static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
0122 {
0123 if (type == DIAG204_INFO_SIMPLE)
0124 return ((struct diag204_cpu_info *)hdr)->ctidx;
0125 else
0126 return ((struct diag204_x_cpu_info *)hdr)->ctidx;
0127 }
0128
0129 static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
0130 {
0131 if (type == DIAG204_INFO_SIMPLE)
0132 return ((struct diag204_cpu_info *)hdr)->cpu_addr;
0133 else
0134 return ((struct diag204_x_cpu_info *)hdr)->cpu_addr;
0135 }
0136
0137 static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
0138 {
0139 if (type == DIAG204_INFO_SIMPLE)
0140 return ((struct diag204_cpu_info *)hdr)->acc_time;
0141 else
0142 return ((struct diag204_x_cpu_info *)hdr)->acc_time;
0143 }
0144
0145 static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
0146 {
0147 if (type == DIAG204_INFO_SIMPLE)
0148 return ((struct diag204_cpu_info *)hdr)->lp_time;
0149 else
0150 return ((struct diag204_x_cpu_info *)hdr)->lp_time;
0151 }
0152
0153 static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
0154 {
0155 if (type == DIAG204_INFO_SIMPLE)
0156 return 0;
0157 else
0158 return ((struct diag204_x_cpu_info *)hdr)->online_time;
0159 }
0160
0161
0162
0163 static inline int phys_hdr__size(enum diag204_format type)
0164 {
0165 if (type == DIAG204_INFO_SIMPLE)
0166 return sizeof(struct diag204_phys_hdr);
0167 else
0168 return sizeof(struct diag204_x_phys_hdr);
0169 }
0170
0171 static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
0172 {
0173 if (type == DIAG204_INFO_SIMPLE)
0174 return ((struct diag204_phys_hdr *)hdr)->cpus;
0175 else
0176 return ((struct diag204_x_phys_hdr *)hdr)->cpus;
0177 }
0178
0179
0180
0181 static inline int phys_cpu__size(enum diag204_format type)
0182 {
0183 if (type == DIAG204_INFO_SIMPLE)
0184 return sizeof(struct diag204_phys_cpu);
0185 else
0186 return sizeof(struct diag204_x_phys_cpu);
0187 }
0188
0189 static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
0190 {
0191 if (type == DIAG204_INFO_SIMPLE)
0192 return ((struct diag204_phys_cpu *)hdr)->cpu_addr;
0193 else
0194 return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr;
0195 }
0196
0197 static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
0198 {
0199 if (type == DIAG204_INFO_SIMPLE)
0200 return ((struct diag204_phys_cpu *)hdr)->mgm_time;
0201 else
0202 return ((struct diag204_x_phys_cpu *)hdr)->mgm_time;
0203 }
0204
0205 static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
0206 {
0207 if (type == DIAG204_INFO_SIMPLE)
0208 return ((struct diag204_phys_cpu *)hdr)->ctidx;
0209 else
0210 return ((struct diag204_x_phys_cpu *)hdr)->ctidx;
0211 }
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221 static void diag204_free_buffer(void)
0222 {
0223 if (!diag204_buf)
0224 return;
0225 if (diag204_buf_vmalloc) {
0226 vfree(diag204_buf_vmalloc);
0227 diag204_buf_vmalloc = NULL;
0228 } else {
0229 free_pages((unsigned long) diag204_buf, 0);
0230 }
0231 diag204_buf = NULL;
0232 }
0233
0234 static void *page_align_ptr(void *ptr)
0235 {
0236 return (void *) PAGE_ALIGN((unsigned long) ptr);
0237 }
0238
0239 static void *diag204_alloc_vbuf(int pages)
0240 {
0241
0242 diag204_buf_vmalloc = vmalloc(array_size(PAGE_SIZE, (pages + 1)));
0243 if (!diag204_buf_vmalloc)
0244 return ERR_PTR(-ENOMEM);
0245 diag204_buf = page_align_ptr(diag204_buf_vmalloc);
0246 diag204_buf_pages = pages;
0247 return diag204_buf;
0248 }
0249
0250 static void *diag204_alloc_rbuf(void)
0251 {
0252 diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0);
0253 if (!diag204_buf)
0254 return ERR_PTR(-ENOMEM);
0255 diag204_buf_pages = 1;
0256 return diag204_buf;
0257 }
0258
0259 static void *diag204_get_buffer(enum diag204_format fmt, int *pages)
0260 {
0261 if (diag204_buf) {
0262 *pages = diag204_buf_pages;
0263 return diag204_buf;
0264 }
0265 if (fmt == DIAG204_INFO_SIMPLE) {
0266 *pages = 1;
0267 return diag204_alloc_rbuf();
0268 } else {
0269 *pages = diag204((unsigned long)DIAG204_SUBC_RSI |
0270 (unsigned long)DIAG204_INFO_EXT, 0, NULL);
0271 if (*pages <= 0)
0272 return ERR_PTR(-ENOSYS);
0273 else
0274 return diag204_alloc_vbuf(*pages);
0275 }
0276 }
0277
0278
0279
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292 static int diag204_probe(void)
0293 {
0294 void *buf;
0295 int pages, rc;
0296
0297 buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages);
0298 if (!IS_ERR(buf)) {
0299 if (diag204((unsigned long)DIAG204_SUBC_STIB7 |
0300 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) {
0301 diag204_store_sc = DIAG204_SUBC_STIB7;
0302 diag204_info_type = DIAG204_INFO_EXT;
0303 goto out;
0304 }
0305 if (diag204((unsigned long)DIAG204_SUBC_STIB6 |
0306 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) {
0307 diag204_store_sc = DIAG204_SUBC_STIB6;
0308 diag204_info_type = DIAG204_INFO_EXT;
0309 goto out;
0310 }
0311 diag204_free_buffer();
0312 }
0313
0314
0315
0316 buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages);
0317 if (IS_ERR(buf)) {
0318 rc = PTR_ERR(buf);
0319 goto fail_alloc;
0320 }
0321 if (diag204((unsigned long)DIAG204_SUBC_STIB4 |
0322 (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) {
0323 diag204_store_sc = DIAG204_SUBC_STIB4;
0324 diag204_info_type = DIAG204_INFO_SIMPLE;
0325 goto out;
0326 } else {
0327 rc = -ENOSYS;
0328 goto fail_store;
0329 }
0330 out:
0331 rc = 0;
0332 fail_store:
0333 diag204_free_buffer();
0334 fail_alloc:
0335 return rc;
0336 }
0337
0338 static int diag204_do_store(void *buf, int pages)
0339 {
0340 int rc;
0341
0342 rc = diag204((unsigned long) diag204_store_sc |
0343 (unsigned long) diag204_info_type, pages, buf);
0344 return rc < 0 ? -ENOSYS : 0;
0345 }
0346
0347 static void *diag204_store(void)
0348 {
0349 void *buf;
0350 int pages, rc;
0351
0352 buf = diag204_get_buffer(diag204_info_type, &pages);
0353 if (IS_ERR(buf))
0354 goto out;
0355 rc = diag204_do_store(buf, pages);
0356 if (rc)
0357 return ERR_PTR(rc);
0358 out:
0359 return buf;
0360 }
0361
0362
0363
0364 static int diag224_get_name_table(void)
0365 {
0366
0367 diag224_cpu_names = (char *) __get_free_page(GFP_KERNEL | GFP_DMA);
0368 if (!diag224_cpu_names)
0369 return -ENOMEM;
0370 if (diag224(diag224_cpu_names)) {
0371 free_page((unsigned long) diag224_cpu_names);
0372 return -EOPNOTSUPP;
0373 }
0374 EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
0375 return 0;
0376 }
0377
0378 static void diag224_delete_name_table(void)
0379 {
0380 free_page((unsigned long) diag224_cpu_names);
0381 }
0382
0383 static int diag224_idx2name(int index, char *name)
0384 {
0385 memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN),
0386 DIAG204_CPU_NAME_LEN);
0387 name[DIAG204_CPU_NAME_LEN] = 0;
0388 strim(name);
0389 return 0;
0390 }
0391
0392 struct dbfs_d204_hdr {
0393 u64 len;
0394 u16 version;
0395 u8 sc;
0396 char reserved[53];
0397 } __attribute__ ((packed));
0398
0399 struct dbfs_d204 {
0400 struct dbfs_d204_hdr hdr;
0401 char buf[];
0402 } __attribute__ ((packed));
0403
0404 static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size)
0405 {
0406 struct dbfs_d204 *d204;
0407 int rc, buf_size;
0408 void *base;
0409
0410 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr);
0411 base = vzalloc(buf_size);
0412 if (!base)
0413 return -ENOMEM;
0414 d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr);
0415 rc = diag204_do_store(d204->buf, diag204_buf_pages);
0416 if (rc) {
0417 vfree(base);
0418 return rc;
0419 }
0420 d204->hdr.version = DBFS_D204_HDR_VERSION;
0421 d204->hdr.len = PAGE_SIZE * diag204_buf_pages;
0422 d204->hdr.sc = diag204_store_sc;
0423 *data = d204;
0424 *data_free_ptr = base;
0425 *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr);
0426 return 0;
0427 }
0428
0429 static struct hypfs_dbfs_file dbfs_file_d204 = {
0430 .name = "diag_204",
0431 .data_create = dbfs_d204_create,
0432 .data_free = vfree,
0433 };
0434
0435 __init int hypfs_diag_init(void)
0436 {
0437 int rc;
0438
0439 if (diag204_probe()) {
0440 pr_info("The hardware system does not support hypfs\n");
0441 return -ENODATA;
0442 }
0443
0444 if (diag204_info_type == DIAG204_INFO_EXT)
0445 hypfs_dbfs_create_file(&dbfs_file_d204);
0446
0447 if (MACHINE_IS_LPAR) {
0448 rc = diag224_get_name_table();
0449 if (rc) {
0450 pr_err("The hardware system does not provide all "
0451 "functions required by hypfs\n");
0452 debugfs_remove(dbfs_d204_file);
0453 return rc;
0454 }
0455 }
0456 return 0;
0457 }
0458
0459 void hypfs_diag_exit(void)
0460 {
0461 debugfs_remove(dbfs_d204_file);
0462 diag224_delete_name_table();
0463 diag204_free_buffer();
0464 hypfs_dbfs_remove_file(&dbfs_file_d204);
0465 }
0466
0467
0468
0469
0470
0471
0472 static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
0473 {
0474 struct dentry *cpu_dir;
0475 char buffer[TMP_SIZE];
0476 void *rc;
0477
0478 snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type,
0479 cpu_info));
0480 cpu_dir = hypfs_mkdir(cpus_dir, buffer);
0481 rc = hypfs_create_u64(cpu_dir, "mgmtime",
0482 cpu_info__acc_time(diag204_info_type, cpu_info) -
0483 cpu_info__lp_time(diag204_info_type, cpu_info));
0484 if (IS_ERR(rc))
0485 return PTR_ERR(rc);
0486 rc = hypfs_create_u64(cpu_dir, "cputime",
0487 cpu_info__lp_time(diag204_info_type, cpu_info));
0488 if (IS_ERR(rc))
0489 return PTR_ERR(rc);
0490 if (diag204_info_type == DIAG204_INFO_EXT) {
0491 rc = hypfs_create_u64(cpu_dir, "onlinetime",
0492 cpu_info__online_time(diag204_info_type,
0493 cpu_info));
0494 if (IS_ERR(rc))
0495 return PTR_ERR(rc);
0496 }
0497 diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer);
0498 rc = hypfs_create_str(cpu_dir, "type", buffer);
0499 return PTR_ERR_OR_ZERO(rc);
0500 }
0501
0502 static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr)
0503 {
0504 struct dentry *cpus_dir;
0505 struct dentry *lpar_dir;
0506 char lpar_name[DIAG204_LPAR_NAME_LEN + 1];
0507 void *cpu_info;
0508 int i;
0509
0510 part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
0511 lpar_name[DIAG204_LPAR_NAME_LEN] = 0;
0512 lpar_dir = hypfs_mkdir(systems_dir, lpar_name);
0513 if (IS_ERR(lpar_dir))
0514 return lpar_dir;
0515 cpus_dir = hypfs_mkdir(lpar_dir, "cpus");
0516 if (IS_ERR(cpus_dir))
0517 return cpus_dir;
0518 cpu_info = part_hdr + part_hdr__size(diag204_info_type);
0519 for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) {
0520 int rc;
0521 rc = hypfs_create_cpu_files(cpus_dir, cpu_info);
0522 if (rc)
0523 return ERR_PTR(rc);
0524 cpu_info += cpu_info__size(diag204_info_type);
0525 }
0526 return cpu_info;
0527 }
0528
0529 static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
0530 {
0531 struct dentry *cpu_dir;
0532 char buffer[TMP_SIZE];
0533 void *rc;
0534
0535 snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type,
0536 cpu_info));
0537 cpu_dir = hypfs_mkdir(cpus_dir, buffer);
0538 if (IS_ERR(cpu_dir))
0539 return PTR_ERR(cpu_dir);
0540 rc = hypfs_create_u64(cpu_dir, "mgmtime",
0541 phys_cpu__mgm_time(diag204_info_type, cpu_info));
0542 if (IS_ERR(rc))
0543 return PTR_ERR(rc);
0544 diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer);
0545 rc = hypfs_create_str(cpu_dir, "type", buffer);
0546 return PTR_ERR_OR_ZERO(rc);
0547 }
0548
0549 static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr)
0550 {
0551 int i;
0552 void *cpu_info;
0553 struct dentry *cpus_dir;
0554
0555 cpus_dir = hypfs_mkdir(parent_dir, "cpus");
0556 if (IS_ERR(cpus_dir))
0557 return cpus_dir;
0558 cpu_info = phys_hdr + phys_hdr__size(diag204_info_type);
0559 for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) {
0560 int rc;
0561 rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info);
0562 if (rc)
0563 return ERR_PTR(rc);
0564 cpu_info += phys_cpu__size(diag204_info_type);
0565 }
0566 return cpu_info;
0567 }
0568
0569 int hypfs_diag_create_files(struct dentry *root)
0570 {
0571 struct dentry *systems_dir, *hyp_dir;
0572 void *time_hdr, *part_hdr;
0573 int i, rc;
0574 void *buffer, *ptr;
0575
0576 buffer = diag204_store();
0577 if (IS_ERR(buffer))
0578 return PTR_ERR(buffer);
0579
0580 systems_dir = hypfs_mkdir(root, "systems");
0581 if (IS_ERR(systems_dir)) {
0582 rc = PTR_ERR(systems_dir);
0583 goto err_out;
0584 }
0585 time_hdr = (struct x_info_blk_hdr *)buffer;
0586 part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type);
0587 for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) {
0588 part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr);
0589 if (IS_ERR(part_hdr)) {
0590 rc = PTR_ERR(part_hdr);
0591 goto err_out;
0592 }
0593 }
0594 if (info_blk_hdr__flags(diag204_info_type, time_hdr) &
0595 DIAG204_LPAR_PHYS_FLG) {
0596 ptr = hypfs_create_phys_files(root, part_hdr);
0597 if (IS_ERR(ptr)) {
0598 rc = PTR_ERR(ptr);
0599 goto err_out;
0600 }
0601 }
0602 hyp_dir = hypfs_mkdir(root, "hyp");
0603 if (IS_ERR(hyp_dir)) {
0604 rc = PTR_ERR(hyp_dir);
0605 goto err_out;
0606 }
0607 ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
0608 if (IS_ERR(ptr)) {
0609 rc = PTR_ERR(ptr);
0610 goto err_out;
0611 }
0612 rc = 0;
0613
0614 err_out:
0615 return rc;
0616 }