xref: /openbmc/linux/arch/s390/hypfs/hypfs_diag.c (revision e65f30e0cb29694c4241bd9c96ea9413938fcec5)
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