124bbb1faSMichael Holzheu /* 224bbb1faSMichael Holzheu * Hypervisor filesystem for Linux on s390. Diag 204 and 224 324bbb1faSMichael Holzheu * implementation. 424bbb1faSMichael Holzheu * 5f55495baSMichael Holzheu * Copyright IBM Corp. 2006, 2008 624bbb1faSMichael Holzheu * Author(s): Michael Holzheu <holzheu@de.ibm.com> 724bbb1faSMichael Holzheu */ 824bbb1faSMichael Holzheu 9f55495baSMichael Holzheu #define KMSG_COMPONENT "hypfs" 10f55495baSMichael Holzheu #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11f55495baSMichael Holzheu 1224bbb1faSMichael Holzheu #include <linux/types.h> 1324bbb1faSMichael Holzheu #include <linux/errno.h> 1433b26d79SHeiko Carstens #include <linux/slab.h> 1524bbb1faSMichael Holzheu #include <linux/string.h> 1624bbb1faSMichael Holzheu #include <linux/vmalloc.h> 1757b28f66SMichael Holzheu #include <linux/mm.h> 181ec2772eSMartin Schwidefsky #include <asm/diag.h> 1924bbb1faSMichael Holzheu #include <asm/ebcdic.h> 2024bbb1faSMichael Holzheu #include "hypfs.h" 2124bbb1faSMichael Holzheu 2224bbb1faSMichael Holzheu #define TMP_SIZE 64 /* size of temporary buffers */ 2324bbb1faSMichael Holzheu 2457b28f66SMichael Holzheu #define DBFS_D204_HDR_VERSION 0 2557b28f66SMichael Holzheu 2624bbb1faSMichael Holzheu static char *diag224_cpu_names; /* diag 224 name table */ 2724bbb1faSMichael Holzheu static enum diag204_sc diag204_store_sc; /* used subcode for store */ 2824bbb1faSMichael Holzheu static enum diag204_format diag204_info_type; /* used diag 204 data format */ 2924bbb1faSMichael Holzheu 3024bbb1faSMichael Holzheu static void *diag204_buf; /* 4K aligned buffer for diag204 data */ 3124bbb1faSMichael Holzheu static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */ 3224bbb1faSMichael Holzheu static int diag204_buf_pages; /* number of pages for diag204 data */ 3324bbb1faSMichael Holzheu 3457b28f66SMichael Holzheu static struct dentry *dbfs_d204_file; 3557b28f66SMichael Holzheu 3624bbb1faSMichael Holzheu /* 37*e65f30e0SJanosch Frank * DIAG 204 member access functions. 3824bbb1faSMichael Holzheu * 3924bbb1faSMichael Holzheu * Since we have two different diag 204 data formats for old and new s390 4024bbb1faSMichael Holzheu * machines, we do not access the structs directly, but use getter functions for 4124bbb1faSMichael Holzheu * each struct member instead. This should make the code more readable. 4224bbb1faSMichael Holzheu */ 4324bbb1faSMichael Holzheu 4424bbb1faSMichael Holzheu /* Time information block */ 4524bbb1faSMichael Holzheu 4624bbb1faSMichael Holzheu static inline int info_blk_hdr__size(enum diag204_format type) 4724bbb1faSMichael Holzheu { 48*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 49*e65f30e0SJanosch Frank return sizeof(struct diag204_info_blk_hdr); 50*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 51*e65f30e0SJanosch Frank return sizeof(struct diag204_x_info_blk_hdr); 5224bbb1faSMichael Holzheu } 5324bbb1faSMichael Holzheu 5424bbb1faSMichael Holzheu static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr) 5524bbb1faSMichael Holzheu { 56*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 57*e65f30e0SJanosch Frank return ((struct diag204_info_blk_hdr *)hdr)->npar; 58*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 59*e65f30e0SJanosch Frank return ((struct diag204_x_info_blk_hdr *)hdr)->npar; 6024bbb1faSMichael Holzheu } 6124bbb1faSMichael Holzheu 6224bbb1faSMichael Holzheu static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr) 6324bbb1faSMichael Holzheu { 64*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 65*e65f30e0SJanosch Frank return ((struct diag204_info_blk_hdr *)hdr)->flags; 66*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 67*e65f30e0SJanosch Frank return ((struct diag204_x_info_blk_hdr *)hdr)->flags; 6824bbb1faSMichael Holzheu } 6924bbb1faSMichael Holzheu 7024bbb1faSMichael Holzheu static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr) 7124bbb1faSMichael Holzheu { 72*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 73*e65f30e0SJanosch Frank return ((struct diag204_info_blk_hdr *)hdr)->phys_cpus; 74*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 75*e65f30e0SJanosch Frank return ((struct diag204_x_info_blk_hdr *)hdr)->phys_cpus; 7624bbb1faSMichael Holzheu } 7724bbb1faSMichael Holzheu 7824bbb1faSMichael Holzheu /* Partition header */ 7924bbb1faSMichael Holzheu 8024bbb1faSMichael Holzheu static inline int part_hdr__size(enum diag204_format type) 8124bbb1faSMichael Holzheu { 82*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 83*e65f30e0SJanosch Frank return sizeof(struct diag204_part_hdr); 84*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 85*e65f30e0SJanosch Frank return sizeof(struct diag204_x_part_hdr); 8624bbb1faSMichael Holzheu } 8724bbb1faSMichael Holzheu 8824bbb1faSMichael Holzheu static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr) 8924bbb1faSMichael Holzheu { 90*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 91*e65f30e0SJanosch Frank return ((struct diag204_part_hdr *)hdr)->cpus; 92*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 93*e65f30e0SJanosch Frank return ((struct diag204_x_part_hdr *)hdr)->rcpus; 9424bbb1faSMichael Holzheu } 9524bbb1faSMichael Holzheu 9624bbb1faSMichael Holzheu static inline void part_hdr__part_name(enum diag204_format type, void *hdr, 9724bbb1faSMichael Holzheu char *name) 9824bbb1faSMichael Holzheu { 99*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 100*e65f30e0SJanosch Frank memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name, 101*e65f30e0SJanosch Frank DIAG204_LPAR_NAME_LEN); 102*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 103*e65f30e0SJanosch Frank memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name, 104*e65f30e0SJanosch Frank DIAG204_LPAR_NAME_LEN); 105*e65f30e0SJanosch Frank EBCASC(name, DIAG204_LPAR_NAME_LEN); 106*e65f30e0SJanosch Frank name[DIAG204_LPAR_NAME_LEN] = 0; 1071d802e24SHeiko Carstens strim(name); 10824bbb1faSMichael Holzheu } 10924bbb1faSMichael Holzheu 11024bbb1faSMichael Holzheu /* CPU info block */ 11124bbb1faSMichael Holzheu 11224bbb1faSMichael Holzheu static inline int cpu_info__size(enum diag204_format type) 11324bbb1faSMichael Holzheu { 114*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 115*e65f30e0SJanosch Frank return sizeof(struct diag204_cpu_info); 116*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 117*e65f30e0SJanosch Frank return sizeof(struct diag204_x_cpu_info); 11824bbb1faSMichael Holzheu } 11924bbb1faSMichael Holzheu 12024bbb1faSMichael Holzheu static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr) 12124bbb1faSMichael Holzheu { 122*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 123*e65f30e0SJanosch Frank return ((struct diag204_cpu_info *)hdr)->ctidx; 124*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 125*e65f30e0SJanosch Frank return ((struct diag204_x_cpu_info *)hdr)->ctidx; 12624bbb1faSMichael Holzheu } 12724bbb1faSMichael Holzheu 12824bbb1faSMichael Holzheu static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr) 12924bbb1faSMichael Holzheu { 130*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 131*e65f30e0SJanosch Frank return ((struct diag204_cpu_info *)hdr)->cpu_addr; 132*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 133*e65f30e0SJanosch Frank return ((struct diag204_x_cpu_info *)hdr)->cpu_addr; 13424bbb1faSMichael Holzheu } 13524bbb1faSMichael Holzheu 13624bbb1faSMichael Holzheu static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr) 13724bbb1faSMichael Holzheu { 138*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 139*e65f30e0SJanosch Frank return ((struct diag204_cpu_info *)hdr)->acc_time; 140*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 141*e65f30e0SJanosch Frank return ((struct diag204_x_cpu_info *)hdr)->acc_time; 14224bbb1faSMichael Holzheu } 14324bbb1faSMichael Holzheu 14424bbb1faSMichael Holzheu static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr) 14524bbb1faSMichael Holzheu { 146*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 147*e65f30e0SJanosch Frank return ((struct diag204_cpu_info *)hdr)->lp_time; 148*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 149*e65f30e0SJanosch Frank return ((struct diag204_x_cpu_info *)hdr)->lp_time; 15024bbb1faSMichael Holzheu } 15124bbb1faSMichael Holzheu 15224bbb1faSMichael Holzheu static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr) 15324bbb1faSMichael Holzheu { 154*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 15524bbb1faSMichael Holzheu return 0; /* online_time not available in simple info */ 156*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 157*e65f30e0SJanosch Frank return ((struct diag204_x_cpu_info *)hdr)->online_time; 15824bbb1faSMichael Holzheu } 15924bbb1faSMichael Holzheu 16024bbb1faSMichael Holzheu /* Physical header */ 16124bbb1faSMichael Holzheu 16224bbb1faSMichael Holzheu static inline int phys_hdr__size(enum diag204_format type) 16324bbb1faSMichael Holzheu { 164*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 165*e65f30e0SJanosch Frank return sizeof(struct diag204_phys_hdr); 166*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 167*e65f30e0SJanosch Frank return sizeof(struct diag204_x_phys_hdr); 16824bbb1faSMichael Holzheu } 16924bbb1faSMichael Holzheu 17024bbb1faSMichael Holzheu static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr) 17124bbb1faSMichael Holzheu { 172*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 173*e65f30e0SJanosch Frank return ((struct diag204_phys_hdr *)hdr)->cpus; 174*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 175*e65f30e0SJanosch Frank return ((struct diag204_x_phys_hdr *)hdr)->cpus; 17624bbb1faSMichael Holzheu } 17724bbb1faSMichael Holzheu 17824bbb1faSMichael Holzheu /* Physical CPU info block */ 17924bbb1faSMichael Holzheu 18024bbb1faSMichael Holzheu static inline int phys_cpu__size(enum diag204_format type) 18124bbb1faSMichael Holzheu { 182*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 183*e65f30e0SJanosch Frank return sizeof(struct diag204_phys_cpu); 184*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 185*e65f30e0SJanosch Frank return sizeof(struct diag204_x_phys_cpu); 18624bbb1faSMichael Holzheu } 18724bbb1faSMichael Holzheu 18824bbb1faSMichael Holzheu static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr) 18924bbb1faSMichael Holzheu { 190*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 191*e65f30e0SJanosch Frank return ((struct diag204_phys_cpu *)hdr)->cpu_addr; 192*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 193*e65f30e0SJanosch Frank return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr; 19424bbb1faSMichael Holzheu } 19524bbb1faSMichael Holzheu 19624bbb1faSMichael Holzheu static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr) 19724bbb1faSMichael Holzheu { 198*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 199*e65f30e0SJanosch Frank return ((struct diag204_phys_cpu *)hdr)->mgm_time; 200*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 201*e65f30e0SJanosch Frank return ((struct diag204_x_phys_cpu *)hdr)->mgm_time; 20224bbb1faSMichael Holzheu } 20324bbb1faSMichael Holzheu 20424bbb1faSMichael Holzheu static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr) 20524bbb1faSMichael Holzheu { 206*e65f30e0SJanosch Frank if (type == DIAG204_INFO_SIMPLE) 207*e65f30e0SJanosch Frank return ((struct diag204_phys_cpu *)hdr)->ctidx; 208*e65f30e0SJanosch Frank else /* DIAG204_INFO_EXT */ 209*e65f30e0SJanosch Frank return ((struct diag204_x_phys_cpu *)hdr)->ctidx; 21024bbb1faSMichael Holzheu } 21124bbb1faSMichael Holzheu 21224bbb1faSMichael Holzheu /* Diagnose 204 functions */ 21324bbb1faSMichael Holzheu /* 21424bbb1faSMichael Holzheu * For the old diag subcode 4 with simple data format we have to use real 21524bbb1faSMichael Holzheu * memory. If we use subcode 6 or 7 with extended data format, we can (and 21624bbb1faSMichael Holzheu * should) use vmalloc, since we need a lot of memory in that case. Currently 21724bbb1faSMichael Holzheu * up to 93 pages! 21824bbb1faSMichael Holzheu */ 21924bbb1faSMichael Holzheu 22024bbb1faSMichael Holzheu static void diag204_free_buffer(void) 22124bbb1faSMichael Holzheu { 22224bbb1faSMichael Holzheu if (!diag204_buf) 22324bbb1faSMichael Holzheu return; 22424bbb1faSMichael Holzheu if (diag204_buf_vmalloc) { 22524bbb1faSMichael Holzheu vfree(diag204_buf_vmalloc); 22624bbb1faSMichael Holzheu diag204_buf_vmalloc = NULL; 22724bbb1faSMichael Holzheu } else { 22824bbb1faSMichael Holzheu free_pages((unsigned long) diag204_buf, 0); 22924bbb1faSMichael Holzheu } 23024bbb1faSMichael Holzheu diag204_buf = NULL; 23124bbb1faSMichael Holzheu } 23224bbb1faSMichael Holzheu 23357b28f66SMichael Holzheu static void *page_align_ptr(void *ptr) 23457b28f66SMichael Holzheu { 23557b28f66SMichael Holzheu return (void *) PAGE_ALIGN((unsigned long) ptr); 23657b28f66SMichael Holzheu } 23757b28f66SMichael Holzheu 23824bbb1faSMichael Holzheu static void *diag204_alloc_vbuf(int pages) 23924bbb1faSMichael Holzheu { 24024bbb1faSMichael Holzheu /* The buffer has to be page aligned! */ 24124bbb1faSMichael Holzheu diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1)); 24224bbb1faSMichael Holzheu if (!diag204_buf_vmalloc) 24324bbb1faSMichael Holzheu return ERR_PTR(-ENOMEM); 24457b28f66SMichael Holzheu diag204_buf = page_align_ptr(diag204_buf_vmalloc); 24524bbb1faSMichael Holzheu diag204_buf_pages = pages; 24624bbb1faSMichael Holzheu return diag204_buf; 24724bbb1faSMichael Holzheu } 24824bbb1faSMichael Holzheu 24924bbb1faSMichael Holzheu static void *diag204_alloc_rbuf(void) 25024bbb1faSMichael Holzheu { 25124bbb1faSMichael Holzheu diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0); 25286b22470SChristian Borntraeger if (!diag204_buf) 25324bbb1faSMichael Holzheu return ERR_PTR(-ENOMEM); 25424bbb1faSMichael Holzheu diag204_buf_pages = 1; 25524bbb1faSMichael Holzheu return diag204_buf; 25624bbb1faSMichael Holzheu } 25724bbb1faSMichael Holzheu 25824bbb1faSMichael Holzheu static void *diag204_get_buffer(enum diag204_format fmt, int *pages) 25924bbb1faSMichael Holzheu { 26024bbb1faSMichael Holzheu if (diag204_buf) { 26124bbb1faSMichael Holzheu *pages = diag204_buf_pages; 26224bbb1faSMichael Holzheu return diag204_buf; 26324bbb1faSMichael Holzheu } 264*e65f30e0SJanosch Frank if (fmt == DIAG204_INFO_SIMPLE) { 26524bbb1faSMichael Holzheu *pages = 1; 26624bbb1faSMichael Holzheu return diag204_alloc_rbuf(); 267*e65f30e0SJanosch Frank } else {/* DIAG204_INFO_EXT */ 268*e65f30e0SJanosch Frank *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 269*e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_EXT, 0, NULL); 27024bbb1faSMichael Holzheu if (*pages <= 0) 27124bbb1faSMichael Holzheu return ERR_PTR(-ENOSYS); 27224bbb1faSMichael Holzheu else 27324bbb1faSMichael Holzheu return diag204_alloc_vbuf(*pages); 27424bbb1faSMichael Holzheu } 27524bbb1faSMichael Holzheu } 27624bbb1faSMichael Holzheu 27724bbb1faSMichael Holzheu /* 27824bbb1faSMichael Holzheu * diag204_probe() has to find out, which type of diagnose 204 implementation 27924bbb1faSMichael Holzheu * we have on our machine. Currently there are three possible scanarios: 28024bbb1faSMichael Holzheu * - subcode 4 + simple data format (only one page) 28124bbb1faSMichael Holzheu * - subcode 4-6 + extended data format 28224bbb1faSMichael Holzheu * - subcode 4-7 + extended data format 28324bbb1faSMichael Holzheu * 28424bbb1faSMichael Holzheu * Subcode 5 is used to retrieve the size of the data, provided by subcodes 28524bbb1faSMichael Holzheu * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 28624bbb1faSMichael Holzheu * to subcode 6 it provides also information about secondary cpus. 28724bbb1faSMichael Holzheu * In order to get as much information as possible, we first try 28824bbb1faSMichael Holzheu * subcode 7, then 6 and if both fail, we use subcode 4. 28924bbb1faSMichael Holzheu */ 29024bbb1faSMichael Holzheu 29124bbb1faSMichael Holzheu static int diag204_probe(void) 29224bbb1faSMichael Holzheu { 29324bbb1faSMichael Holzheu void *buf; 29424bbb1faSMichael Holzheu int pages, rc; 29524bbb1faSMichael Holzheu 296*e65f30e0SJanosch Frank buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 29724bbb1faSMichael Holzheu if (!IS_ERR(buf)) { 298*e65f30e0SJanosch Frank if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 299*e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 300*e65f30e0SJanosch Frank diag204_store_sc = DIAG204_SUBC_STIB7; 301*e65f30e0SJanosch Frank diag204_info_type = DIAG204_INFO_EXT; 30224bbb1faSMichael Holzheu goto out; 30324bbb1faSMichael Holzheu } 304*e65f30e0SJanosch Frank if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 305*e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 306*e65f30e0SJanosch Frank diag204_store_sc = DIAG204_SUBC_STIB6; 307*e65f30e0SJanosch Frank diag204_info_type = DIAG204_INFO_EXT; 30824bbb1faSMichael Holzheu goto out; 30924bbb1faSMichael Holzheu } 31024bbb1faSMichael Holzheu diag204_free_buffer(); 31124bbb1faSMichael Holzheu } 31224bbb1faSMichael Holzheu 31324bbb1faSMichael Holzheu /* subcodes 6 and 7 failed, now try subcode 4 */ 31424bbb1faSMichael Holzheu 315*e65f30e0SJanosch Frank buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 31624bbb1faSMichael Holzheu if (IS_ERR(buf)) { 31724bbb1faSMichael Holzheu rc = PTR_ERR(buf); 31824bbb1faSMichael Holzheu goto fail_alloc; 31924bbb1faSMichael Holzheu } 320*e65f30e0SJanosch Frank if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 321*e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 322*e65f30e0SJanosch Frank diag204_store_sc = DIAG204_SUBC_STIB4; 323*e65f30e0SJanosch Frank diag204_info_type = DIAG204_INFO_SIMPLE; 32424bbb1faSMichael Holzheu goto out; 32524bbb1faSMichael Holzheu } else { 32624bbb1faSMichael Holzheu rc = -ENOSYS; 32724bbb1faSMichael Holzheu goto fail_store; 32824bbb1faSMichael Holzheu } 32924bbb1faSMichael Holzheu out: 33024bbb1faSMichael Holzheu rc = 0; 33124bbb1faSMichael Holzheu fail_store: 33224bbb1faSMichael Holzheu diag204_free_buffer(); 33324bbb1faSMichael Holzheu fail_alloc: 33424bbb1faSMichael Holzheu return rc; 33524bbb1faSMichael Holzheu } 33624bbb1faSMichael Holzheu 33757b28f66SMichael Holzheu static int diag204_do_store(void *buf, int pages) 33857b28f66SMichael Holzheu { 33957b28f66SMichael Holzheu int rc; 34057b28f66SMichael Holzheu 34157b28f66SMichael Holzheu rc = diag204((unsigned long) diag204_store_sc | 34257b28f66SMichael Holzheu (unsigned long) diag204_info_type, pages, buf); 34357b28f66SMichael Holzheu return rc < 0 ? -ENOSYS : 0; 34457b28f66SMichael Holzheu } 34557b28f66SMichael Holzheu 34624bbb1faSMichael Holzheu static void *diag204_store(void) 34724bbb1faSMichael Holzheu { 34824bbb1faSMichael Holzheu void *buf; 34957b28f66SMichael Holzheu int pages, rc; 35024bbb1faSMichael Holzheu 35124bbb1faSMichael Holzheu buf = diag204_get_buffer(diag204_info_type, &pages); 35224bbb1faSMichael Holzheu if (IS_ERR(buf)) 35324bbb1faSMichael Holzheu goto out; 35457b28f66SMichael Holzheu rc = diag204_do_store(buf, pages); 35557b28f66SMichael Holzheu if (rc) 35657b28f66SMichael Holzheu return ERR_PTR(rc); 35724bbb1faSMichael Holzheu out: 35824bbb1faSMichael Holzheu return buf; 35924bbb1faSMichael Holzheu } 36024bbb1faSMichael Holzheu 36124bbb1faSMichael Holzheu /* Diagnose 224 functions */ 36224bbb1faSMichael Holzheu 363c41d4e3eSMichael Holzheu static int diag224(void *ptr) 36424bbb1faSMichael Holzheu { 365b8e660b8SHeiko Carstens int rc = -EOPNOTSUPP; 366c41d4e3eSMichael Holzheu 3671ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X224); 368c41d4e3eSMichael Holzheu asm volatile( 369c41d4e3eSMichael Holzheu " diag %1,%2,0x224\n" 370c41d4e3eSMichael Holzheu "0: lhi %0,0x0\n" 371c41d4e3eSMichael Holzheu "1:\n" 372c41d4e3eSMichael Holzheu EX_TABLE(0b,1b) 373c41d4e3eSMichael Holzheu : "+d" (rc) :"d" (0), "d" (ptr) : "memory"); 374c41d4e3eSMichael Holzheu return rc; 37524bbb1faSMichael Holzheu } 37624bbb1faSMichael Holzheu 37724bbb1faSMichael Holzheu static int diag224_get_name_table(void) 37824bbb1faSMichael Holzheu { 37924bbb1faSMichael Holzheu /* memory must be below 2GB */ 38024bbb1faSMichael Holzheu diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); 38124bbb1faSMichael Holzheu if (!diag224_cpu_names) 38224bbb1faSMichael Holzheu return -ENOMEM; 383c41d4e3eSMichael Holzheu if (diag224(diag224_cpu_names)) { 384c41d4e3eSMichael Holzheu kfree(diag224_cpu_names); 385b8e660b8SHeiko Carstens return -EOPNOTSUPP; 386c41d4e3eSMichael Holzheu } 38724bbb1faSMichael Holzheu EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16); 38824bbb1faSMichael Holzheu return 0; 38924bbb1faSMichael Holzheu } 39024bbb1faSMichael Holzheu 39124bbb1faSMichael Holzheu static void diag224_delete_name_table(void) 39224bbb1faSMichael Holzheu { 39324bbb1faSMichael Holzheu kfree(diag224_cpu_names); 39424bbb1faSMichael Holzheu } 39524bbb1faSMichael Holzheu 39624bbb1faSMichael Holzheu static int diag224_idx2name(int index, char *name) 39724bbb1faSMichael Holzheu { 398*e65f30e0SJanosch Frank memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN), 399*e65f30e0SJanosch Frank DIAG204_CPU_NAME_LEN); 400*e65f30e0SJanosch Frank name[DIAG204_CPU_NAME_LEN] = 0; 4011d802e24SHeiko Carstens strim(name); 40224bbb1faSMichael Holzheu return 0; 40324bbb1faSMichael Holzheu } 40424bbb1faSMichael Holzheu 40557b28f66SMichael Holzheu struct dbfs_d204_hdr { 40657b28f66SMichael Holzheu u64 len; /* Length of d204 buffer without header */ 40757b28f66SMichael Holzheu u16 version; /* Version of header */ 40857b28f66SMichael Holzheu u8 sc; /* Used subcode */ 40957b28f66SMichael Holzheu char reserved[53]; 41057b28f66SMichael Holzheu } __attribute__ ((packed)); 41157b28f66SMichael Holzheu 41257b28f66SMichael Holzheu struct dbfs_d204 { 41357b28f66SMichael Holzheu struct dbfs_d204_hdr hdr; /* 64 byte header */ 41457b28f66SMichael Holzheu char buf[]; /* d204 buffer */ 41557b28f66SMichael Holzheu } __attribute__ ((packed)); 41657b28f66SMichael Holzheu 4172fcb3686SMichael Holzheu static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 41857b28f66SMichael Holzheu { 41957b28f66SMichael Holzheu struct dbfs_d204 *d204; 42057b28f66SMichael Holzheu int rc, buf_size; 4212fcb3686SMichael Holzheu void *base; 42257b28f66SMichael Holzheu 42357b28f66SMichael Holzheu buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 424dc8a5c99SJoe Perches base = vzalloc(buf_size); 4252fcb3686SMichael Holzheu if (!base) 4262fcb3686SMichael Holzheu return -ENOMEM; 4272fcb3686SMichael Holzheu d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); 4282fcb3686SMichael Holzheu rc = diag204_do_store(d204->buf, diag204_buf_pages); 4292fcb3686SMichael Holzheu if (rc) { 4302fcb3686SMichael Holzheu vfree(base); 4312fcb3686SMichael Holzheu return rc; 43257b28f66SMichael Holzheu } 43357b28f66SMichael Holzheu d204->hdr.version = DBFS_D204_HDR_VERSION; 43457b28f66SMichael Holzheu d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 43557b28f66SMichael Holzheu d204->hdr.sc = diag204_store_sc; 4362fcb3686SMichael Holzheu *data = d204; 4372fcb3686SMichael Holzheu *data_free_ptr = base; 4382fcb3686SMichael Holzheu *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 43957b28f66SMichael Holzheu return 0; 44057b28f66SMichael Holzheu } 44157b28f66SMichael Holzheu 4422fcb3686SMichael Holzheu static struct hypfs_dbfs_file dbfs_file_d204 = { 4432fcb3686SMichael Holzheu .name = "diag_204", 4442fcb3686SMichael Holzheu .data_create = dbfs_d204_create, 4452fcb3686SMichael Holzheu .data_free = vfree, 44657b28f66SMichael Holzheu }; 44757b28f66SMichael Holzheu 44824bbb1faSMichael Holzheu __init int hypfs_diag_init(void) 44924bbb1faSMichael Holzheu { 45024bbb1faSMichael Holzheu int rc; 45124bbb1faSMichael Holzheu 45224bbb1faSMichael Holzheu if (diag204_probe()) { 453f55495baSMichael Holzheu pr_err("The hardware system does not support hypfs\n"); 45424bbb1faSMichael Holzheu return -ENODATA; 45524bbb1faSMichael Holzheu } 456*e65f30e0SJanosch Frank if (diag204_info_type == DIAG204_INFO_EXT) { 4572fcb3686SMichael Holzheu rc = hypfs_dbfs_create_file(&dbfs_file_d204); 45857b28f66SMichael Holzheu if (rc) 45924bbb1faSMichael Holzheu return rc; 46024bbb1faSMichael Holzheu } 4613c8ebca0SMichael Holzheu if (MACHINE_IS_LPAR) { 4623c8ebca0SMichael Holzheu rc = diag224_get_name_table(); 4633c8ebca0SMichael Holzheu if (rc) { 4643c8ebca0SMichael Holzheu pr_err("The hardware system does not provide all " 4653c8ebca0SMichael Holzheu "functions required by hypfs\n"); 4663c8ebca0SMichael Holzheu debugfs_remove(dbfs_d204_file); 4673c8ebca0SMichael Holzheu return rc; 4683c8ebca0SMichael Holzheu } 4693c8ebca0SMichael Holzheu } 4703c8ebca0SMichael Holzheu return 0; 4713c8ebca0SMichael Holzheu } 47224bbb1faSMichael Holzheu 4731375fc1fSHeiko Carstens void hypfs_diag_exit(void) 47424bbb1faSMichael Holzheu { 47557b28f66SMichael Holzheu debugfs_remove(dbfs_d204_file); 47624bbb1faSMichael Holzheu diag224_delete_name_table(); 47724bbb1faSMichael Holzheu diag204_free_buffer(); 4782fcb3686SMichael Holzheu hypfs_dbfs_remove_file(&dbfs_file_d204); 47924bbb1faSMichael Holzheu } 48024bbb1faSMichael Holzheu 48124bbb1faSMichael Holzheu /* 48224bbb1faSMichael Holzheu * Functions to create the directory structure 48324bbb1faSMichael Holzheu * ******************************************* 48424bbb1faSMichael Holzheu */ 48524bbb1faSMichael Holzheu 486e334cf4fSAl Viro static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info) 48724bbb1faSMichael Holzheu { 48824bbb1faSMichael Holzheu struct dentry *cpu_dir; 48924bbb1faSMichael Holzheu char buffer[TMP_SIZE]; 49024bbb1faSMichael Holzheu void *rc; 49124bbb1faSMichael Holzheu 49224bbb1faSMichael Holzheu snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type, 49324bbb1faSMichael Holzheu cpu_info)); 494a118cfdfSAl Viro cpu_dir = hypfs_mkdir(cpus_dir, buffer); 495a118cfdfSAl Viro rc = hypfs_create_u64(cpu_dir, "mgmtime", 49624bbb1faSMichael Holzheu cpu_info__acc_time(diag204_info_type, cpu_info) - 49724bbb1faSMichael Holzheu cpu_info__lp_time(diag204_info_type, cpu_info)); 49824bbb1faSMichael Holzheu if (IS_ERR(rc)) 49924bbb1faSMichael Holzheu return PTR_ERR(rc); 500a118cfdfSAl Viro rc = hypfs_create_u64(cpu_dir, "cputime", 50124bbb1faSMichael Holzheu cpu_info__lp_time(diag204_info_type, cpu_info)); 50224bbb1faSMichael Holzheu if (IS_ERR(rc)) 50324bbb1faSMichael Holzheu return PTR_ERR(rc); 504*e65f30e0SJanosch Frank if (diag204_info_type == DIAG204_INFO_EXT) { 505a118cfdfSAl Viro rc = hypfs_create_u64(cpu_dir, "onlinetime", 50624bbb1faSMichael Holzheu cpu_info__online_time(diag204_info_type, 50724bbb1faSMichael Holzheu cpu_info)); 50824bbb1faSMichael Holzheu if (IS_ERR(rc)) 50924bbb1faSMichael Holzheu return PTR_ERR(rc); 51024bbb1faSMichael Holzheu } 51124bbb1faSMichael Holzheu diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer); 512a118cfdfSAl Viro rc = hypfs_create_str(cpu_dir, "type", buffer); 5135eba9bb8SThomas Meyer return PTR_RET(rc); 51424bbb1faSMichael Holzheu } 51524bbb1faSMichael Holzheu 516e334cf4fSAl Viro static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr) 51724bbb1faSMichael Holzheu { 51824bbb1faSMichael Holzheu struct dentry *cpus_dir; 51924bbb1faSMichael Holzheu struct dentry *lpar_dir; 520*e65f30e0SJanosch Frank char lpar_name[DIAG204_LPAR_NAME_LEN + 1]; 52124bbb1faSMichael Holzheu void *cpu_info; 52224bbb1faSMichael Holzheu int i; 52324bbb1faSMichael Holzheu 52424bbb1faSMichael Holzheu part_hdr__part_name(diag204_info_type, part_hdr, lpar_name); 525*e65f30e0SJanosch Frank lpar_name[DIAG204_LPAR_NAME_LEN] = 0; 526a118cfdfSAl Viro lpar_dir = hypfs_mkdir(systems_dir, lpar_name); 52724bbb1faSMichael Holzheu if (IS_ERR(lpar_dir)) 52824bbb1faSMichael Holzheu return lpar_dir; 529a118cfdfSAl Viro cpus_dir = hypfs_mkdir(lpar_dir, "cpus"); 53024bbb1faSMichael Holzheu if (IS_ERR(cpus_dir)) 53124bbb1faSMichael Holzheu return cpus_dir; 53224bbb1faSMichael Holzheu cpu_info = part_hdr + part_hdr__size(diag204_info_type); 53324bbb1faSMichael Holzheu for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) { 53424bbb1faSMichael Holzheu int rc; 535e334cf4fSAl Viro rc = hypfs_create_cpu_files(cpus_dir, cpu_info); 53624bbb1faSMichael Holzheu if (rc) 53724bbb1faSMichael Holzheu return ERR_PTR(rc); 53824bbb1faSMichael Holzheu cpu_info += cpu_info__size(diag204_info_type); 53924bbb1faSMichael Holzheu } 54024bbb1faSMichael Holzheu return cpu_info; 54124bbb1faSMichael Holzheu } 54224bbb1faSMichael Holzheu 543e334cf4fSAl Viro static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info) 54424bbb1faSMichael Holzheu { 54524bbb1faSMichael Holzheu struct dentry *cpu_dir; 54624bbb1faSMichael Holzheu char buffer[TMP_SIZE]; 54724bbb1faSMichael Holzheu void *rc; 54824bbb1faSMichael Holzheu 54924bbb1faSMichael Holzheu snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type, 55024bbb1faSMichael Holzheu cpu_info)); 551a118cfdfSAl Viro cpu_dir = hypfs_mkdir(cpus_dir, buffer); 55224bbb1faSMichael Holzheu if (IS_ERR(cpu_dir)) 55324bbb1faSMichael Holzheu return PTR_ERR(cpu_dir); 554a118cfdfSAl Viro rc = hypfs_create_u64(cpu_dir, "mgmtime", 55524bbb1faSMichael Holzheu phys_cpu__mgm_time(diag204_info_type, cpu_info)); 55624bbb1faSMichael Holzheu if (IS_ERR(rc)) 55724bbb1faSMichael Holzheu return PTR_ERR(rc); 55824bbb1faSMichael Holzheu diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer); 559a118cfdfSAl Viro rc = hypfs_create_str(cpu_dir, "type", buffer); 5605eba9bb8SThomas Meyer return PTR_RET(rc); 56124bbb1faSMichael Holzheu } 56224bbb1faSMichael Holzheu 563e334cf4fSAl Viro static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr) 56424bbb1faSMichael Holzheu { 56524bbb1faSMichael Holzheu int i; 56624bbb1faSMichael Holzheu void *cpu_info; 56724bbb1faSMichael Holzheu struct dentry *cpus_dir; 56824bbb1faSMichael Holzheu 569a118cfdfSAl Viro cpus_dir = hypfs_mkdir(parent_dir, "cpus"); 57024bbb1faSMichael Holzheu if (IS_ERR(cpus_dir)) 57124bbb1faSMichael Holzheu return cpus_dir; 57224bbb1faSMichael Holzheu cpu_info = phys_hdr + phys_hdr__size(diag204_info_type); 57324bbb1faSMichael Holzheu for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) { 57424bbb1faSMichael Holzheu int rc; 575e334cf4fSAl Viro rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info); 57624bbb1faSMichael Holzheu if (rc) 57724bbb1faSMichael Holzheu return ERR_PTR(rc); 57824bbb1faSMichael Holzheu cpu_info += phys_cpu__size(diag204_info_type); 57924bbb1faSMichael Holzheu } 58024bbb1faSMichael Holzheu return cpu_info; 58124bbb1faSMichael Holzheu } 58224bbb1faSMichael Holzheu 583e334cf4fSAl Viro int hypfs_diag_create_files(struct dentry *root) 58424bbb1faSMichael Holzheu { 58524bbb1faSMichael Holzheu struct dentry *systems_dir, *hyp_dir; 58624bbb1faSMichael Holzheu void *time_hdr, *part_hdr; 58724bbb1faSMichael Holzheu int i, rc; 58824bbb1faSMichael Holzheu void *buffer, *ptr; 58924bbb1faSMichael Holzheu 59024bbb1faSMichael Holzheu buffer = diag204_store(); 59124bbb1faSMichael Holzheu if (IS_ERR(buffer)) 59224bbb1faSMichael Holzheu return PTR_ERR(buffer); 59324bbb1faSMichael Holzheu 594a118cfdfSAl Viro systems_dir = hypfs_mkdir(root, "systems"); 59524bbb1faSMichael Holzheu if (IS_ERR(systems_dir)) { 59624bbb1faSMichael Holzheu rc = PTR_ERR(systems_dir); 59724bbb1faSMichael Holzheu goto err_out; 59824bbb1faSMichael Holzheu } 59924bbb1faSMichael Holzheu time_hdr = (struct x_info_blk_hdr *)buffer; 60024bbb1faSMichael Holzheu part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type); 60124bbb1faSMichael Holzheu for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) { 602e334cf4fSAl Viro part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr); 60324bbb1faSMichael Holzheu if (IS_ERR(part_hdr)) { 60424bbb1faSMichael Holzheu rc = PTR_ERR(part_hdr); 60524bbb1faSMichael Holzheu goto err_out; 60624bbb1faSMichael Holzheu } 60724bbb1faSMichael Holzheu } 608*e65f30e0SJanosch Frank if (info_blk_hdr__flags(diag204_info_type, time_hdr) & 609*e65f30e0SJanosch Frank DIAG204_LPAR_PHYS_FLG) { 610e334cf4fSAl Viro ptr = hypfs_create_phys_files(root, part_hdr); 61124bbb1faSMichael Holzheu if (IS_ERR(ptr)) { 61224bbb1faSMichael Holzheu rc = PTR_ERR(ptr); 61324bbb1faSMichael Holzheu goto err_out; 61424bbb1faSMichael Holzheu } 61524bbb1faSMichael Holzheu } 616a118cfdfSAl Viro hyp_dir = hypfs_mkdir(root, "hyp"); 61724bbb1faSMichael Holzheu if (IS_ERR(hyp_dir)) { 61824bbb1faSMichael Holzheu rc = PTR_ERR(hyp_dir); 61924bbb1faSMichael Holzheu goto err_out; 62024bbb1faSMichael Holzheu } 621a118cfdfSAl Viro ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor"); 62224bbb1faSMichael Holzheu if (IS_ERR(ptr)) { 62324bbb1faSMichael Holzheu rc = PTR_ERR(ptr); 62424bbb1faSMichael Holzheu goto err_out; 62524bbb1faSMichael Holzheu } 62624bbb1faSMichael Holzheu rc = 0; 62724bbb1faSMichael Holzheu 62824bbb1faSMichael Holzheu err_out: 62924bbb1faSMichael Holzheu return rc; 63024bbb1faSMichael Holzheu } 631