xref: /openbmc/linux/arch/s390/hypfs/hypfs_diag.c (revision 57b28f66)
124bbb1faSMichael Holzheu /*
2f19bfb2cSMichael Holzheu  *  arch/s390/hypfs/hypfs_diag.c
324bbb1faSMichael Holzheu  *    Hypervisor filesystem for Linux on s390. Diag 204 and 224
424bbb1faSMichael Holzheu  *    implementation.
524bbb1faSMichael Holzheu  *
6f55495baSMichael Holzheu  *    Copyright IBM Corp. 2006, 2008
724bbb1faSMichael Holzheu  *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
824bbb1faSMichael Holzheu  */
924bbb1faSMichael Holzheu 
10f55495baSMichael Holzheu #define KMSG_COMPONENT "hypfs"
11f55495baSMichael Holzheu #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12f55495baSMichael Holzheu 
1324bbb1faSMichael Holzheu #include <linux/types.h>
1424bbb1faSMichael Holzheu #include <linux/errno.h>
1533b26d79SHeiko Carstens #include <linux/slab.h>
1624bbb1faSMichael Holzheu #include <linux/string.h>
1724bbb1faSMichael Holzheu #include <linux/vmalloc.h>
1857b28f66SMichael Holzheu #include <linux/mm.h>
1924bbb1faSMichael Holzheu #include <asm/ebcdic.h>
2024bbb1faSMichael Holzheu #include "hypfs.h"
2124bbb1faSMichael Holzheu 
2224bbb1faSMichael Holzheu #define LPAR_NAME_LEN 8		/* lpar name len in diag 204 data */
2324bbb1faSMichael Holzheu #define CPU_NAME_LEN 16		/* type name len of cpus in diag224 name table */
2424bbb1faSMichael Holzheu #define TMP_SIZE 64		/* size of temporary buffers */
2524bbb1faSMichael Holzheu 
2657b28f66SMichael Holzheu #define DBFS_D204_HDR_VERSION	0
2757b28f66SMichael Holzheu 
2824bbb1faSMichael Holzheu /* diag 204 subcodes */
2924bbb1faSMichael Holzheu enum diag204_sc {
3024bbb1faSMichael Holzheu 	SUBC_STIB4 = 4,
3124bbb1faSMichael Holzheu 	SUBC_RSI = 5,
3224bbb1faSMichael Holzheu 	SUBC_STIB6 = 6,
3324bbb1faSMichael Holzheu 	SUBC_STIB7 = 7
3424bbb1faSMichael Holzheu };
3524bbb1faSMichael Holzheu 
3624bbb1faSMichael Holzheu /* The two available diag 204 data formats */
3724bbb1faSMichael Holzheu enum diag204_format {
3824bbb1faSMichael Holzheu 	INFO_SIMPLE = 0,
3924bbb1faSMichael Holzheu 	INFO_EXT = 0x00010000
4024bbb1faSMichael Holzheu };
4124bbb1faSMichael Holzheu 
4224bbb1faSMichael Holzheu /* bit is set in flags, when physical cpu info is included in diag 204 data */
4324bbb1faSMichael Holzheu #define LPAR_PHYS_FLG  0x80
4424bbb1faSMichael Holzheu 
4524bbb1faSMichael Holzheu static char *diag224_cpu_names;			/* diag 224 name table */
4624bbb1faSMichael Holzheu static enum diag204_sc diag204_store_sc;	/* used subcode for store */
4724bbb1faSMichael Holzheu static enum diag204_format diag204_info_type;	/* used diag 204 data format */
4824bbb1faSMichael Holzheu 
4924bbb1faSMichael Holzheu static void *diag204_buf;		/* 4K aligned buffer for diag204 data */
5024bbb1faSMichael Holzheu static void *diag204_buf_vmalloc;	/* vmalloc pointer for diag204 data */
5124bbb1faSMichael Holzheu static int diag204_buf_pages;		/* number of pages for diag204 data */
5224bbb1faSMichael Holzheu 
5357b28f66SMichael Holzheu static struct dentry *dbfs_d204_file;
5457b28f66SMichael Holzheu 
5524bbb1faSMichael Holzheu /*
5624bbb1faSMichael Holzheu  * DIAG 204 data structures and member access functions.
5724bbb1faSMichael Holzheu  *
5824bbb1faSMichael Holzheu  * Since we have two different diag 204 data formats for old and new s390
5924bbb1faSMichael Holzheu  * machines, we do not access the structs directly, but use getter functions for
6024bbb1faSMichael Holzheu  * each struct member instead. This should make the code more readable.
6124bbb1faSMichael Holzheu  */
6224bbb1faSMichael Holzheu 
6324bbb1faSMichael Holzheu /* Time information block */
6424bbb1faSMichael Holzheu 
6524bbb1faSMichael Holzheu struct info_blk_hdr {
6624bbb1faSMichael Holzheu 	__u8  npar;
6724bbb1faSMichael Holzheu 	__u8  flags;
6824bbb1faSMichael Holzheu 	__u16 tslice;
6924bbb1faSMichael Holzheu 	__u16 phys_cpus;
7024bbb1faSMichael Holzheu 	__u16 this_part;
7124bbb1faSMichael Holzheu 	__u64 curtod;
7224bbb1faSMichael Holzheu } __attribute__ ((packed));
7324bbb1faSMichael Holzheu 
7424bbb1faSMichael Holzheu struct x_info_blk_hdr {
7524bbb1faSMichael Holzheu 	__u8  npar;
7624bbb1faSMichael Holzheu 	__u8  flags;
7724bbb1faSMichael Holzheu 	__u16 tslice;
7824bbb1faSMichael Holzheu 	__u16 phys_cpus;
7924bbb1faSMichael Holzheu 	__u16 this_part;
8024bbb1faSMichael Holzheu 	__u64 curtod1;
8124bbb1faSMichael Holzheu 	__u64 curtod2;
8224bbb1faSMichael Holzheu 	char reserved[40];
8324bbb1faSMichael Holzheu } __attribute__ ((packed));
8424bbb1faSMichael Holzheu 
8524bbb1faSMichael Holzheu static inline int info_blk_hdr__size(enum diag204_format type)
8624bbb1faSMichael Holzheu {
8724bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
8824bbb1faSMichael Holzheu 		return sizeof(struct info_blk_hdr);
8924bbb1faSMichael Holzheu 	else /* INFO_EXT */
9024bbb1faSMichael Holzheu 		return sizeof(struct x_info_blk_hdr);
9124bbb1faSMichael Holzheu }
9224bbb1faSMichael Holzheu 
9324bbb1faSMichael Holzheu static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
9424bbb1faSMichael Holzheu {
9524bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
9624bbb1faSMichael Holzheu 		return ((struct info_blk_hdr *)hdr)->npar;
9724bbb1faSMichael Holzheu 	else /* INFO_EXT */
9824bbb1faSMichael Holzheu 		return ((struct x_info_blk_hdr *)hdr)->npar;
9924bbb1faSMichael Holzheu }
10024bbb1faSMichael Holzheu 
10124bbb1faSMichael Holzheu static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
10224bbb1faSMichael Holzheu {
10324bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
10424bbb1faSMichael Holzheu 		return ((struct info_blk_hdr *)hdr)->flags;
10524bbb1faSMichael Holzheu 	else /* INFO_EXT */
10624bbb1faSMichael Holzheu 		return ((struct x_info_blk_hdr *)hdr)->flags;
10724bbb1faSMichael Holzheu }
10824bbb1faSMichael Holzheu 
10924bbb1faSMichael Holzheu static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
11024bbb1faSMichael Holzheu {
11124bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
11224bbb1faSMichael Holzheu 		return ((struct info_blk_hdr *)hdr)->phys_cpus;
11324bbb1faSMichael Holzheu 	else /* INFO_EXT */
11424bbb1faSMichael Holzheu 		return ((struct x_info_blk_hdr *)hdr)->phys_cpus;
11524bbb1faSMichael Holzheu }
11624bbb1faSMichael Holzheu 
11724bbb1faSMichael Holzheu /* Partition header */
11824bbb1faSMichael Holzheu 
11924bbb1faSMichael Holzheu struct part_hdr {
12024bbb1faSMichael Holzheu 	__u8 pn;
12124bbb1faSMichael Holzheu 	__u8 cpus;
12224bbb1faSMichael Holzheu 	char reserved[6];
12324bbb1faSMichael Holzheu 	char part_name[LPAR_NAME_LEN];
12424bbb1faSMichael Holzheu } __attribute__ ((packed));
12524bbb1faSMichael Holzheu 
12624bbb1faSMichael Holzheu struct x_part_hdr {
12724bbb1faSMichael Holzheu 	__u8  pn;
12824bbb1faSMichael Holzheu 	__u8  cpus;
12924bbb1faSMichael Holzheu 	__u8  rcpus;
13024bbb1faSMichael Holzheu 	__u8  pflag;
13124bbb1faSMichael Holzheu 	__u32 mlu;
13224bbb1faSMichael Holzheu 	char  part_name[LPAR_NAME_LEN];
13324bbb1faSMichael Holzheu 	char  lpc_name[8];
13424bbb1faSMichael Holzheu 	char  os_name[8];
13524bbb1faSMichael Holzheu 	__u64 online_cs;
13624bbb1faSMichael Holzheu 	__u64 online_es;
13724bbb1faSMichael Holzheu 	__u8  upid;
13824bbb1faSMichael Holzheu 	char  reserved1[3];
13924bbb1faSMichael Holzheu 	__u32 group_mlu;
14024bbb1faSMichael Holzheu 	char  group_name[8];
14124bbb1faSMichael Holzheu 	char  reserved2[32];
14224bbb1faSMichael Holzheu } __attribute__ ((packed));
14324bbb1faSMichael Holzheu 
14424bbb1faSMichael Holzheu static inline int part_hdr__size(enum diag204_format type)
14524bbb1faSMichael Holzheu {
14624bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
14724bbb1faSMichael Holzheu 		return sizeof(struct part_hdr);
14824bbb1faSMichael Holzheu 	else /* INFO_EXT */
14924bbb1faSMichael Holzheu 		return sizeof(struct x_part_hdr);
15024bbb1faSMichael Holzheu }
15124bbb1faSMichael Holzheu 
15224bbb1faSMichael Holzheu static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
15324bbb1faSMichael Holzheu {
15424bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
15524bbb1faSMichael Holzheu 		return ((struct part_hdr *)hdr)->cpus;
15624bbb1faSMichael Holzheu 	else /* INFO_EXT */
15724bbb1faSMichael Holzheu 		return ((struct x_part_hdr *)hdr)->rcpus;
15824bbb1faSMichael Holzheu }
15924bbb1faSMichael Holzheu 
16024bbb1faSMichael Holzheu static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
16124bbb1faSMichael Holzheu 				       char *name)
16224bbb1faSMichael Holzheu {
16324bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
16424bbb1faSMichael Holzheu 		memcpy(name, ((struct part_hdr *)hdr)->part_name,
16524bbb1faSMichael Holzheu 		       LPAR_NAME_LEN);
16624bbb1faSMichael Holzheu 	else /* INFO_EXT */
16724bbb1faSMichael Holzheu 		memcpy(name, ((struct x_part_hdr *)hdr)->part_name,
16824bbb1faSMichael Holzheu 		       LPAR_NAME_LEN);
16924bbb1faSMichael Holzheu 	EBCASC(name, LPAR_NAME_LEN);
17024bbb1faSMichael Holzheu 	name[LPAR_NAME_LEN] = 0;
1711d802e24SHeiko Carstens 	strim(name);
17224bbb1faSMichael Holzheu }
17324bbb1faSMichael Holzheu 
17424bbb1faSMichael Holzheu struct cpu_info {
17524bbb1faSMichael Holzheu 	__u16 cpu_addr;
17624bbb1faSMichael Holzheu 	char  reserved1[2];
17724bbb1faSMichael Holzheu 	__u8  ctidx;
17824bbb1faSMichael Holzheu 	__u8  cflag;
17924bbb1faSMichael Holzheu 	__u16 weight;
18024bbb1faSMichael Holzheu 	__u64 acc_time;
18124bbb1faSMichael Holzheu 	__u64 lp_time;
18224bbb1faSMichael Holzheu } __attribute__ ((packed));
18324bbb1faSMichael Holzheu 
18424bbb1faSMichael Holzheu struct x_cpu_info {
18524bbb1faSMichael Holzheu 	__u16 cpu_addr;
18624bbb1faSMichael Holzheu 	char  reserved1[2];
18724bbb1faSMichael Holzheu 	__u8  ctidx;
18824bbb1faSMichael Holzheu 	__u8  cflag;
18924bbb1faSMichael Holzheu 	__u16 weight;
19024bbb1faSMichael Holzheu 	__u64 acc_time;
19124bbb1faSMichael Holzheu 	__u64 lp_time;
19224bbb1faSMichael Holzheu 	__u16 min_weight;
19324bbb1faSMichael Holzheu 	__u16 cur_weight;
19424bbb1faSMichael Holzheu 	__u16 max_weight;
19524bbb1faSMichael Holzheu 	char  reseved2[2];
19624bbb1faSMichael Holzheu 	__u64 online_time;
19724bbb1faSMichael Holzheu 	__u64 wait_time;
19824bbb1faSMichael Holzheu 	__u32 pma_weight;
19924bbb1faSMichael Holzheu 	__u32 polar_weight;
20024bbb1faSMichael Holzheu 	char  reserved3[40];
20124bbb1faSMichael Holzheu } __attribute__ ((packed));
20224bbb1faSMichael Holzheu 
20324bbb1faSMichael Holzheu /* CPU info block */
20424bbb1faSMichael Holzheu 
20524bbb1faSMichael Holzheu static inline int cpu_info__size(enum diag204_format type)
20624bbb1faSMichael Holzheu {
20724bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
20824bbb1faSMichael Holzheu 		return sizeof(struct cpu_info);
20924bbb1faSMichael Holzheu 	else /* INFO_EXT */
21024bbb1faSMichael Holzheu 		return sizeof(struct x_cpu_info);
21124bbb1faSMichael Holzheu }
21224bbb1faSMichael Holzheu 
21324bbb1faSMichael Holzheu static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
21424bbb1faSMichael Holzheu {
21524bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
21624bbb1faSMichael Holzheu 		return ((struct cpu_info *)hdr)->ctidx;
21724bbb1faSMichael Holzheu 	else /* INFO_EXT */
21824bbb1faSMichael Holzheu 		return ((struct x_cpu_info *)hdr)->ctidx;
21924bbb1faSMichael Holzheu }
22024bbb1faSMichael Holzheu 
22124bbb1faSMichael Holzheu static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
22224bbb1faSMichael Holzheu {
22324bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
22424bbb1faSMichael Holzheu 		return ((struct cpu_info *)hdr)->cpu_addr;
22524bbb1faSMichael Holzheu 	else /* INFO_EXT */
22624bbb1faSMichael Holzheu 		return ((struct x_cpu_info *)hdr)->cpu_addr;
22724bbb1faSMichael Holzheu }
22824bbb1faSMichael Holzheu 
22924bbb1faSMichael Holzheu static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
23024bbb1faSMichael Holzheu {
23124bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
23224bbb1faSMichael Holzheu 		return ((struct cpu_info *)hdr)->acc_time;
23324bbb1faSMichael Holzheu 	else /* INFO_EXT */
23424bbb1faSMichael Holzheu 		return ((struct x_cpu_info *)hdr)->acc_time;
23524bbb1faSMichael Holzheu }
23624bbb1faSMichael Holzheu 
23724bbb1faSMichael Holzheu static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
23824bbb1faSMichael Holzheu {
23924bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
24024bbb1faSMichael Holzheu 		return ((struct cpu_info *)hdr)->lp_time;
24124bbb1faSMichael Holzheu 	else /* INFO_EXT */
24224bbb1faSMichael Holzheu 		return ((struct x_cpu_info *)hdr)->lp_time;
24324bbb1faSMichael Holzheu }
24424bbb1faSMichael Holzheu 
24524bbb1faSMichael Holzheu static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
24624bbb1faSMichael Holzheu {
24724bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
24824bbb1faSMichael Holzheu 		return 0;	/* online_time not available in simple info */
24924bbb1faSMichael Holzheu 	else /* INFO_EXT */
25024bbb1faSMichael Holzheu 		return ((struct x_cpu_info *)hdr)->online_time;
25124bbb1faSMichael Holzheu }
25224bbb1faSMichael Holzheu 
25324bbb1faSMichael Holzheu /* Physical header */
25424bbb1faSMichael Holzheu 
25524bbb1faSMichael Holzheu struct phys_hdr {
25624bbb1faSMichael Holzheu 	char reserved1[1];
25724bbb1faSMichael Holzheu 	__u8 cpus;
25824bbb1faSMichael Holzheu 	char reserved2[6];
25924bbb1faSMichael Holzheu 	char mgm_name[8];
26024bbb1faSMichael Holzheu } __attribute__ ((packed));
26124bbb1faSMichael Holzheu 
26224bbb1faSMichael Holzheu struct x_phys_hdr {
26324bbb1faSMichael Holzheu 	char reserved1[1];
26424bbb1faSMichael Holzheu 	__u8 cpus;
26524bbb1faSMichael Holzheu 	char reserved2[6];
26624bbb1faSMichael Holzheu 	char mgm_name[8];
26724bbb1faSMichael Holzheu 	char reserved3[80];
26824bbb1faSMichael Holzheu } __attribute__ ((packed));
26924bbb1faSMichael Holzheu 
27024bbb1faSMichael Holzheu static inline int phys_hdr__size(enum diag204_format type)
27124bbb1faSMichael Holzheu {
27224bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
27324bbb1faSMichael Holzheu 		return sizeof(struct phys_hdr);
27424bbb1faSMichael Holzheu 	else /* INFO_EXT */
27524bbb1faSMichael Holzheu 		return sizeof(struct x_phys_hdr);
27624bbb1faSMichael Holzheu }
27724bbb1faSMichael Holzheu 
27824bbb1faSMichael Holzheu static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
27924bbb1faSMichael Holzheu {
28024bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
28124bbb1faSMichael Holzheu 		return ((struct phys_hdr *)hdr)->cpus;
28224bbb1faSMichael Holzheu 	else /* INFO_EXT */
28324bbb1faSMichael Holzheu 		return ((struct x_phys_hdr *)hdr)->cpus;
28424bbb1faSMichael Holzheu }
28524bbb1faSMichael Holzheu 
28624bbb1faSMichael Holzheu /* Physical CPU info block */
28724bbb1faSMichael Holzheu 
28824bbb1faSMichael Holzheu struct phys_cpu {
28924bbb1faSMichael Holzheu 	__u16 cpu_addr;
29024bbb1faSMichael Holzheu 	char  reserved1[2];
29124bbb1faSMichael Holzheu 	__u8  ctidx;
29224bbb1faSMichael Holzheu 	char  reserved2[3];
29324bbb1faSMichael Holzheu 	__u64 mgm_time;
29424bbb1faSMichael Holzheu 	char  reserved3[8];
29524bbb1faSMichael Holzheu } __attribute__ ((packed));
29624bbb1faSMichael Holzheu 
29724bbb1faSMichael Holzheu struct x_phys_cpu {
29824bbb1faSMichael Holzheu 	__u16 cpu_addr;
29924bbb1faSMichael Holzheu 	char  reserved1[2];
30024bbb1faSMichael Holzheu 	__u8  ctidx;
30124bbb1faSMichael Holzheu 	char  reserved2[3];
30224bbb1faSMichael Holzheu 	__u64 mgm_time;
30324bbb1faSMichael Holzheu 	char  reserved3[80];
30424bbb1faSMichael Holzheu } __attribute__ ((packed));
30524bbb1faSMichael Holzheu 
30624bbb1faSMichael Holzheu static inline int phys_cpu__size(enum diag204_format type)
30724bbb1faSMichael Holzheu {
30824bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
30924bbb1faSMichael Holzheu 		return sizeof(struct phys_cpu);
31024bbb1faSMichael Holzheu 	else /* INFO_EXT */
31124bbb1faSMichael Holzheu 		return sizeof(struct x_phys_cpu);
31224bbb1faSMichael Holzheu }
31324bbb1faSMichael Holzheu 
31424bbb1faSMichael Holzheu static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
31524bbb1faSMichael Holzheu {
31624bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
31724bbb1faSMichael Holzheu 		return ((struct phys_cpu *)hdr)->cpu_addr;
31824bbb1faSMichael Holzheu 	else /* INFO_EXT */
31924bbb1faSMichael Holzheu 		return ((struct x_phys_cpu *)hdr)->cpu_addr;
32024bbb1faSMichael Holzheu }
32124bbb1faSMichael Holzheu 
32224bbb1faSMichael Holzheu static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
32324bbb1faSMichael Holzheu {
32424bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
32524bbb1faSMichael Holzheu 		return ((struct phys_cpu *)hdr)->mgm_time;
32624bbb1faSMichael Holzheu 	else /* INFO_EXT */
32724bbb1faSMichael Holzheu 		return ((struct x_phys_cpu *)hdr)->mgm_time;
32824bbb1faSMichael Holzheu }
32924bbb1faSMichael Holzheu 
33024bbb1faSMichael Holzheu static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
33124bbb1faSMichael Holzheu {
33224bbb1faSMichael Holzheu 	if (type == INFO_SIMPLE)
33324bbb1faSMichael Holzheu 		return ((struct phys_cpu *)hdr)->ctidx;
33424bbb1faSMichael Holzheu 	else /* INFO_EXT */
33524bbb1faSMichael Holzheu 		return ((struct x_phys_cpu *)hdr)->ctidx;
33624bbb1faSMichael Holzheu }
33724bbb1faSMichael Holzheu 
33824bbb1faSMichael Holzheu /* Diagnose 204 functions */
33924bbb1faSMichael Holzheu 
34024bbb1faSMichael Holzheu static int diag204(unsigned long subcode, unsigned long size, void *addr)
34124bbb1faSMichael Holzheu {
34224bbb1faSMichael Holzheu 	register unsigned long _subcode asm("0") = subcode;
34324bbb1faSMichael Holzheu 	register unsigned long _size asm("1") = size;
34424bbb1faSMichael Holzheu 
34594c12cc7SMartin Schwidefsky 	asm volatile(
34694c12cc7SMartin Schwidefsky 		"	diag	%2,%0,0x204\n"
34794c12cc7SMartin Schwidefsky 		"0:\n"
34894c12cc7SMartin Schwidefsky 		EX_TABLE(0b,0b)
34994c12cc7SMartin Schwidefsky 		: "+d" (_subcode), "+d" (_size) : "d" (addr) : "memory");
35024bbb1faSMichael Holzheu 	if (_subcode)
35124bbb1faSMichael Holzheu 		return -1;
35224bbb1faSMichael Holzheu 	return _size;
35324bbb1faSMichael Holzheu }
35424bbb1faSMichael Holzheu 
35524bbb1faSMichael Holzheu /*
35624bbb1faSMichael Holzheu  * For the old diag subcode 4 with simple data format we have to use real
35724bbb1faSMichael Holzheu  * memory. If we use subcode 6 or 7 with extended data format, we can (and
35824bbb1faSMichael Holzheu  * should) use vmalloc, since we need a lot of memory in that case. Currently
35924bbb1faSMichael Holzheu  * up to 93 pages!
36024bbb1faSMichael Holzheu  */
36124bbb1faSMichael Holzheu 
36224bbb1faSMichael Holzheu static void diag204_free_buffer(void)
36324bbb1faSMichael Holzheu {
36424bbb1faSMichael Holzheu 	if (!diag204_buf)
36524bbb1faSMichael Holzheu 		return;
36624bbb1faSMichael Holzheu 	if (diag204_buf_vmalloc) {
36724bbb1faSMichael Holzheu 		vfree(diag204_buf_vmalloc);
36824bbb1faSMichael Holzheu 		diag204_buf_vmalloc = NULL;
36924bbb1faSMichael Holzheu 	} else {
37024bbb1faSMichael Holzheu 		free_pages((unsigned long) diag204_buf, 0);
37124bbb1faSMichael Holzheu 	}
37224bbb1faSMichael Holzheu 	diag204_buf = NULL;
37324bbb1faSMichael Holzheu }
37424bbb1faSMichael Holzheu 
37557b28f66SMichael Holzheu static void *page_align_ptr(void *ptr)
37657b28f66SMichael Holzheu {
37757b28f66SMichael Holzheu 	return (void *) PAGE_ALIGN((unsigned long) ptr);
37857b28f66SMichael Holzheu }
37957b28f66SMichael Holzheu 
38024bbb1faSMichael Holzheu static void *diag204_alloc_vbuf(int pages)
38124bbb1faSMichael Holzheu {
38224bbb1faSMichael Holzheu 	/* The buffer has to be page aligned! */
38324bbb1faSMichael Holzheu 	diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
38424bbb1faSMichael Holzheu 	if (!diag204_buf_vmalloc)
38524bbb1faSMichael Holzheu 		return ERR_PTR(-ENOMEM);
38657b28f66SMichael Holzheu 	diag204_buf = page_align_ptr(diag204_buf_vmalloc);
38724bbb1faSMichael Holzheu 	diag204_buf_pages = pages;
38824bbb1faSMichael Holzheu 	return diag204_buf;
38924bbb1faSMichael Holzheu }
39024bbb1faSMichael Holzheu 
39124bbb1faSMichael Holzheu static void *diag204_alloc_rbuf(void)
39224bbb1faSMichael Holzheu {
39324bbb1faSMichael Holzheu 	diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0);
39486b22470SChristian Borntraeger 	if (!diag204_buf)
39524bbb1faSMichael Holzheu 		return ERR_PTR(-ENOMEM);
39624bbb1faSMichael Holzheu 	diag204_buf_pages = 1;
39724bbb1faSMichael Holzheu 	return diag204_buf;
39824bbb1faSMichael Holzheu }
39924bbb1faSMichael Holzheu 
40024bbb1faSMichael Holzheu static void *diag204_get_buffer(enum diag204_format fmt, int *pages)
40124bbb1faSMichael Holzheu {
40224bbb1faSMichael Holzheu 	if (diag204_buf) {
40324bbb1faSMichael Holzheu 		*pages = diag204_buf_pages;
40424bbb1faSMichael Holzheu 		return diag204_buf;
40524bbb1faSMichael Holzheu 	}
40624bbb1faSMichael Holzheu 	if (fmt == INFO_SIMPLE) {
40724bbb1faSMichael Holzheu 		*pages = 1;
40824bbb1faSMichael Holzheu 		return diag204_alloc_rbuf();
40924bbb1faSMichael Holzheu 	} else {/* INFO_EXT */
41023c100d9SMichael Holzheu 		*pages = diag204((unsigned long)SUBC_RSI |
41123c100d9SMichael Holzheu 				 (unsigned long)INFO_EXT, 0, NULL);
41224bbb1faSMichael Holzheu 		if (*pages <= 0)
41324bbb1faSMichael Holzheu 			return ERR_PTR(-ENOSYS);
41424bbb1faSMichael Holzheu 		else
41524bbb1faSMichael Holzheu 			return diag204_alloc_vbuf(*pages);
41624bbb1faSMichael Holzheu 	}
41724bbb1faSMichael Holzheu }
41824bbb1faSMichael Holzheu 
41924bbb1faSMichael Holzheu /*
42024bbb1faSMichael Holzheu  * diag204_probe() has to find out, which type of diagnose 204 implementation
42124bbb1faSMichael Holzheu  * we have on our machine. Currently there are three possible scanarios:
42224bbb1faSMichael Holzheu  *   - subcode 4   + simple data format (only one page)
42324bbb1faSMichael Holzheu  *   - subcode 4-6 + extended data format
42424bbb1faSMichael Holzheu  *   - subcode 4-7 + extended data format
42524bbb1faSMichael Holzheu  *
42624bbb1faSMichael Holzheu  * Subcode 5 is used to retrieve the size of the data, provided by subcodes
42724bbb1faSMichael Holzheu  * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition
42824bbb1faSMichael Holzheu  * to subcode 6 it provides also information about secondary cpus.
42924bbb1faSMichael Holzheu  * In order to get as much information as possible, we first try
43024bbb1faSMichael Holzheu  * subcode 7, then 6 and if both fail, we use subcode 4.
43124bbb1faSMichael Holzheu  */
43224bbb1faSMichael Holzheu 
43324bbb1faSMichael Holzheu static int diag204_probe(void)
43424bbb1faSMichael Holzheu {
43524bbb1faSMichael Holzheu 	void *buf;
43624bbb1faSMichael Holzheu 	int pages, rc;
43724bbb1faSMichael Holzheu 
43824bbb1faSMichael Holzheu 	buf = diag204_get_buffer(INFO_EXT, &pages);
43924bbb1faSMichael Holzheu 	if (!IS_ERR(buf)) {
440331c982dSMichael Holzheu 		if (diag204((unsigned long)SUBC_STIB7 |
441331c982dSMichael Holzheu 			    (unsigned long)INFO_EXT, pages, buf) >= 0) {
44224bbb1faSMichael Holzheu 			diag204_store_sc = SUBC_STIB7;
44324bbb1faSMichael Holzheu 			diag204_info_type = INFO_EXT;
44424bbb1faSMichael Holzheu 			goto out;
44524bbb1faSMichael Holzheu 		}
446331c982dSMichael Holzheu 		if (diag204((unsigned long)SUBC_STIB6 |
447331c982dSMichael Holzheu 			    (unsigned long)INFO_EXT, pages, buf) >= 0) {
4487874b1b6SMichael Holzheu 			diag204_store_sc = SUBC_STIB6;
44924bbb1faSMichael Holzheu 			diag204_info_type = INFO_EXT;
45024bbb1faSMichael Holzheu 			goto out;
45124bbb1faSMichael Holzheu 		}
45224bbb1faSMichael Holzheu 		diag204_free_buffer();
45324bbb1faSMichael Holzheu 	}
45424bbb1faSMichael Holzheu 
45524bbb1faSMichael Holzheu 	/* subcodes 6 and 7 failed, now try subcode 4 */
45624bbb1faSMichael Holzheu 
45724bbb1faSMichael Holzheu 	buf = diag204_get_buffer(INFO_SIMPLE, &pages);
45824bbb1faSMichael Holzheu 	if (IS_ERR(buf)) {
45924bbb1faSMichael Holzheu 		rc = PTR_ERR(buf);
46024bbb1faSMichael Holzheu 		goto fail_alloc;
46124bbb1faSMichael Holzheu 	}
462331c982dSMichael Holzheu 	if (diag204((unsigned long)SUBC_STIB4 |
463331c982dSMichael Holzheu 		    (unsigned long)INFO_SIMPLE, pages, buf) >= 0) {
46424bbb1faSMichael Holzheu 		diag204_store_sc = SUBC_STIB4;
46524bbb1faSMichael Holzheu 		diag204_info_type = INFO_SIMPLE;
46624bbb1faSMichael Holzheu 		goto out;
46724bbb1faSMichael Holzheu 	} else {
46824bbb1faSMichael Holzheu 		rc = -ENOSYS;
46924bbb1faSMichael Holzheu 		goto fail_store;
47024bbb1faSMichael Holzheu 	}
47124bbb1faSMichael Holzheu out:
47224bbb1faSMichael Holzheu 	rc = 0;
47324bbb1faSMichael Holzheu fail_store:
47424bbb1faSMichael Holzheu 	diag204_free_buffer();
47524bbb1faSMichael Holzheu fail_alloc:
47624bbb1faSMichael Holzheu 	return rc;
47724bbb1faSMichael Holzheu }
47824bbb1faSMichael Holzheu 
47957b28f66SMichael Holzheu static int diag204_do_store(void *buf, int pages)
48057b28f66SMichael Holzheu {
48157b28f66SMichael Holzheu 	int rc;
48257b28f66SMichael Holzheu 
48357b28f66SMichael Holzheu 	rc = diag204((unsigned long) diag204_store_sc |
48457b28f66SMichael Holzheu 		     (unsigned long) diag204_info_type, pages, buf);
48557b28f66SMichael Holzheu 	return rc < 0 ? -ENOSYS : 0;
48657b28f66SMichael Holzheu }
48757b28f66SMichael Holzheu 
48824bbb1faSMichael Holzheu static void *diag204_store(void)
48924bbb1faSMichael Holzheu {
49024bbb1faSMichael Holzheu 	void *buf;
49157b28f66SMichael Holzheu 	int pages, rc;
49224bbb1faSMichael Holzheu 
49324bbb1faSMichael Holzheu 	buf = diag204_get_buffer(diag204_info_type, &pages);
49424bbb1faSMichael Holzheu 	if (IS_ERR(buf))
49524bbb1faSMichael Holzheu 		goto out;
49657b28f66SMichael Holzheu 	rc = diag204_do_store(buf, pages);
49757b28f66SMichael Holzheu 	if (rc)
49857b28f66SMichael Holzheu 		return ERR_PTR(rc);
49924bbb1faSMichael Holzheu out:
50024bbb1faSMichael Holzheu 	return buf;
50124bbb1faSMichael Holzheu }
50224bbb1faSMichael Holzheu 
50324bbb1faSMichael Holzheu /* Diagnose 224 functions */
50424bbb1faSMichael Holzheu 
505c41d4e3eSMichael Holzheu static int diag224(void *ptr)
50624bbb1faSMichael Holzheu {
507b8e660b8SHeiko Carstens 	int rc = -EOPNOTSUPP;
508c41d4e3eSMichael Holzheu 
509c41d4e3eSMichael Holzheu 	asm volatile(
510c41d4e3eSMichael Holzheu 		"	diag	%1,%2,0x224\n"
511c41d4e3eSMichael Holzheu 		"0:	lhi	%0,0x0\n"
512c41d4e3eSMichael Holzheu 		"1:\n"
513c41d4e3eSMichael Holzheu 		EX_TABLE(0b,1b)
514c41d4e3eSMichael Holzheu 		: "+d" (rc) :"d" (0), "d" (ptr) : "memory");
515c41d4e3eSMichael Holzheu 	return rc;
51624bbb1faSMichael Holzheu }
51724bbb1faSMichael Holzheu 
51824bbb1faSMichael Holzheu static int diag224_get_name_table(void)
51924bbb1faSMichael Holzheu {
52024bbb1faSMichael Holzheu 	/* memory must be below 2GB */
52124bbb1faSMichael Holzheu 	diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
52224bbb1faSMichael Holzheu 	if (!diag224_cpu_names)
52324bbb1faSMichael Holzheu 		return -ENOMEM;
524c41d4e3eSMichael Holzheu 	if (diag224(diag224_cpu_names)) {
525c41d4e3eSMichael Holzheu 		kfree(diag224_cpu_names);
526b8e660b8SHeiko Carstens 		return -EOPNOTSUPP;
527c41d4e3eSMichael Holzheu 	}
52824bbb1faSMichael Holzheu 	EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
52924bbb1faSMichael Holzheu 	return 0;
53024bbb1faSMichael Holzheu }
53124bbb1faSMichael Holzheu 
53224bbb1faSMichael Holzheu static void diag224_delete_name_table(void)
53324bbb1faSMichael Holzheu {
53424bbb1faSMichael Holzheu 	kfree(diag224_cpu_names);
53524bbb1faSMichael Holzheu }
53624bbb1faSMichael Holzheu 
53724bbb1faSMichael Holzheu static int diag224_idx2name(int index, char *name)
53824bbb1faSMichael Holzheu {
53924bbb1faSMichael Holzheu 	memcpy(name, diag224_cpu_names + ((index + 1) * CPU_NAME_LEN),
54024bbb1faSMichael Holzheu 		CPU_NAME_LEN);
54124bbb1faSMichael Holzheu 	name[CPU_NAME_LEN] = 0;
5421d802e24SHeiko Carstens 	strim(name);
54324bbb1faSMichael Holzheu 	return 0;
54424bbb1faSMichael Holzheu }
54524bbb1faSMichael Holzheu 
54657b28f66SMichael Holzheu struct dbfs_d204_hdr {
54757b28f66SMichael Holzheu 	u64	len;		/* Length of d204 buffer without header */
54857b28f66SMichael Holzheu 	u16	version;	/* Version of header */
54957b28f66SMichael Holzheu 	u8	sc;		/* Used subcode */
55057b28f66SMichael Holzheu 	char	reserved[53];
55157b28f66SMichael Holzheu } __attribute__ ((packed));
55257b28f66SMichael Holzheu 
55357b28f66SMichael Holzheu struct dbfs_d204 {
55457b28f66SMichael Holzheu 	struct dbfs_d204_hdr	hdr;	/* 64 byte header */
55557b28f66SMichael Holzheu 	char			buf[];	/* d204 buffer */
55657b28f66SMichael Holzheu } __attribute__ ((packed));
55757b28f66SMichael Holzheu 
55857b28f66SMichael Holzheu struct dbfs_d204_private {
55957b28f66SMichael Holzheu 	struct dbfs_d204	*d204;	/* Aligned d204 data with header */
56057b28f66SMichael Holzheu 	void			*base;	/* Base pointer (needed for vfree) */
56157b28f66SMichael Holzheu };
56257b28f66SMichael Holzheu 
56357b28f66SMichael Holzheu static int dbfs_d204_open(struct inode *inode, struct file *file)
56457b28f66SMichael Holzheu {
56557b28f66SMichael Holzheu 	struct dbfs_d204_private *data;
56657b28f66SMichael Holzheu 	struct dbfs_d204 *d204;
56757b28f66SMichael Holzheu 	int rc, buf_size;
56857b28f66SMichael Holzheu 
56957b28f66SMichael Holzheu 	data = kzalloc(sizeof(*data), GFP_KERNEL);
57057b28f66SMichael Holzheu 	if (!data)
57157b28f66SMichael Holzheu 		return -ENOMEM;
57257b28f66SMichael Holzheu 	buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr);
57357b28f66SMichael Holzheu 	data->base = vmalloc(buf_size);
57457b28f66SMichael Holzheu 	if (!data->base) {
57557b28f66SMichael Holzheu 		rc = -ENOMEM;
57657b28f66SMichael Holzheu 		goto fail_kfree_data;
57757b28f66SMichael Holzheu 	}
57857b28f66SMichael Holzheu 	memset(data->base, 0, buf_size);
57957b28f66SMichael Holzheu 	d204 = page_align_ptr(data->base + sizeof(d204->hdr))
58057b28f66SMichael Holzheu 		- sizeof(d204->hdr);
58157b28f66SMichael Holzheu 	rc = diag204_do_store(&d204->buf, diag204_buf_pages);
58257b28f66SMichael Holzheu 	if (rc)
58357b28f66SMichael Holzheu 		goto fail_vfree_base;
58457b28f66SMichael Holzheu 	d204->hdr.version = DBFS_D204_HDR_VERSION;
58557b28f66SMichael Holzheu 	d204->hdr.len = PAGE_SIZE * diag204_buf_pages;
58657b28f66SMichael Holzheu 	d204->hdr.sc = diag204_store_sc;
58757b28f66SMichael Holzheu 	data->d204 = d204;
58857b28f66SMichael Holzheu 	file->private_data = data;
58957b28f66SMichael Holzheu 	return nonseekable_open(inode, file);
59057b28f66SMichael Holzheu 
59157b28f66SMichael Holzheu fail_vfree_base:
59257b28f66SMichael Holzheu 	vfree(data->base);
59357b28f66SMichael Holzheu fail_kfree_data:
59457b28f66SMichael Holzheu 	kfree(data);
59557b28f66SMichael Holzheu 	return rc;
59657b28f66SMichael Holzheu }
59757b28f66SMichael Holzheu 
59857b28f66SMichael Holzheu static int dbfs_d204_release(struct inode *inode, struct file *file)
59957b28f66SMichael Holzheu {
60057b28f66SMichael Holzheu 	struct dbfs_d204_private *data = file->private_data;
60157b28f66SMichael Holzheu 
60257b28f66SMichael Holzheu 	vfree(data->base);
60357b28f66SMichael Holzheu 	kfree(data);
60457b28f66SMichael Holzheu 	return 0;
60557b28f66SMichael Holzheu }
60657b28f66SMichael Holzheu 
60757b28f66SMichael Holzheu static ssize_t dbfs_d204_read(struct file *file, char __user *buf,
60857b28f66SMichael Holzheu 			      size_t size, loff_t *ppos)
60957b28f66SMichael Holzheu {
61057b28f66SMichael Holzheu 	struct dbfs_d204_private *data = file->private_data;
61157b28f66SMichael Holzheu 
61257b28f66SMichael Holzheu 	return simple_read_from_buffer(buf, size, ppos, data->d204,
61357b28f66SMichael Holzheu 				       data->d204->hdr.len +
61457b28f66SMichael Holzheu 				       sizeof(data->d204->hdr));
61557b28f66SMichael Holzheu }
61657b28f66SMichael Holzheu 
61757b28f66SMichael Holzheu static const struct file_operations dbfs_d204_ops = {
61857b28f66SMichael Holzheu 	.open		= dbfs_d204_open,
61957b28f66SMichael Holzheu 	.read		= dbfs_d204_read,
62057b28f66SMichael Holzheu 	.release	= dbfs_d204_release,
62157b28f66SMichael Holzheu };
62257b28f66SMichael Holzheu 
62357b28f66SMichael Holzheu static int hypfs_dbfs_init(void)
62457b28f66SMichael Holzheu {
62557b28f66SMichael Holzheu 	dbfs_d204_file = debugfs_create_file("diag_204", 0400, hypfs_dbfs_dir,
62657b28f66SMichael Holzheu 					     NULL, &dbfs_d204_ops);
62757b28f66SMichael Holzheu 	if (IS_ERR(dbfs_d204_file))
62857b28f66SMichael Holzheu 		return PTR_ERR(dbfs_d204_file);
62957b28f66SMichael Holzheu 	return 0;
63057b28f66SMichael Holzheu }
63157b28f66SMichael Holzheu 
63224bbb1faSMichael Holzheu __init int hypfs_diag_init(void)
63324bbb1faSMichael Holzheu {
63424bbb1faSMichael Holzheu 	int rc;
63524bbb1faSMichael Holzheu 
63624bbb1faSMichael Holzheu 	if (diag204_probe()) {
637f55495baSMichael Holzheu 		pr_err("The hardware system does not support hypfs\n");
63824bbb1faSMichael Holzheu 		return -ENODATA;
63924bbb1faSMichael Holzheu 	}
64024bbb1faSMichael Holzheu 	rc = diag224_get_name_table();
64124bbb1faSMichael Holzheu 	if (rc) {
64286b22470SChristian Borntraeger 		diag204_free_buffer();
643f55495baSMichael Holzheu 		pr_err("The hardware system does not provide all "
644f55495baSMichael Holzheu 		       "functions required by hypfs\n");
64524bbb1faSMichael Holzheu 	}
64657b28f66SMichael Holzheu 	if (diag204_info_type == INFO_EXT) {
64757b28f66SMichael Holzheu 		rc = hypfs_dbfs_init();
64857b28f66SMichael Holzheu 		if (rc)
64957b28f66SMichael Holzheu 			diag204_free_buffer();
65057b28f66SMichael Holzheu 	}
65124bbb1faSMichael Holzheu 	return rc;
65224bbb1faSMichael Holzheu }
65324bbb1faSMichael Holzheu 
6541375fc1fSHeiko Carstens void hypfs_diag_exit(void)
65524bbb1faSMichael Holzheu {
65657b28f66SMichael Holzheu 	debugfs_remove(dbfs_d204_file);
65724bbb1faSMichael Holzheu 	diag224_delete_name_table();
65824bbb1faSMichael Holzheu 	diag204_free_buffer();
65924bbb1faSMichael Holzheu }
66024bbb1faSMichael Holzheu 
66124bbb1faSMichael Holzheu /*
66224bbb1faSMichael Holzheu  * Functions to create the directory structure
66324bbb1faSMichael Holzheu  * *******************************************
66424bbb1faSMichael Holzheu  */
66524bbb1faSMichael Holzheu 
66624bbb1faSMichael Holzheu static int hypfs_create_cpu_files(struct super_block *sb,
66724bbb1faSMichael Holzheu 				  struct dentry *cpus_dir, void *cpu_info)
66824bbb1faSMichael Holzheu {
66924bbb1faSMichael Holzheu 	struct dentry *cpu_dir;
67024bbb1faSMichael Holzheu 	char buffer[TMP_SIZE];
67124bbb1faSMichael Holzheu 	void *rc;
67224bbb1faSMichael Holzheu 
67324bbb1faSMichael Holzheu 	snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type,
67424bbb1faSMichael Holzheu 							    cpu_info));
67524bbb1faSMichael Holzheu 	cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
67624bbb1faSMichael Holzheu 	rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
67724bbb1faSMichael Holzheu 			      cpu_info__acc_time(diag204_info_type, cpu_info) -
67824bbb1faSMichael Holzheu 			      cpu_info__lp_time(diag204_info_type, cpu_info));
67924bbb1faSMichael Holzheu 	if (IS_ERR(rc))
68024bbb1faSMichael Holzheu 		return PTR_ERR(rc);
68124bbb1faSMichael Holzheu 	rc = hypfs_create_u64(sb, cpu_dir, "cputime",
68224bbb1faSMichael Holzheu 			      cpu_info__lp_time(diag204_info_type, cpu_info));
68324bbb1faSMichael Holzheu 	if (IS_ERR(rc))
68424bbb1faSMichael Holzheu 		return PTR_ERR(rc);
68524bbb1faSMichael Holzheu 	if (diag204_info_type == INFO_EXT) {
68624bbb1faSMichael Holzheu 		rc = hypfs_create_u64(sb, cpu_dir, "onlinetime",
68724bbb1faSMichael Holzheu 				      cpu_info__online_time(diag204_info_type,
68824bbb1faSMichael Holzheu 							    cpu_info));
68924bbb1faSMichael Holzheu 		if (IS_ERR(rc))
69024bbb1faSMichael Holzheu 			return PTR_ERR(rc);
69124bbb1faSMichael Holzheu 	}
69224bbb1faSMichael Holzheu 	diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer);
69324bbb1faSMichael Holzheu 	rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
69424bbb1faSMichael Holzheu 	if (IS_ERR(rc))
69524bbb1faSMichael Holzheu 		return PTR_ERR(rc);
69624bbb1faSMichael Holzheu 	return 0;
69724bbb1faSMichael Holzheu }
69824bbb1faSMichael Holzheu 
69924bbb1faSMichael Holzheu static void *hypfs_create_lpar_files(struct super_block *sb,
70024bbb1faSMichael Holzheu 				     struct dentry *systems_dir, void *part_hdr)
70124bbb1faSMichael Holzheu {
70224bbb1faSMichael Holzheu 	struct dentry *cpus_dir;
70324bbb1faSMichael Holzheu 	struct dentry *lpar_dir;
70424bbb1faSMichael Holzheu 	char lpar_name[LPAR_NAME_LEN + 1];
70524bbb1faSMichael Holzheu 	void *cpu_info;
70624bbb1faSMichael Holzheu 	int i;
70724bbb1faSMichael Holzheu 
70824bbb1faSMichael Holzheu 	part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
70924bbb1faSMichael Holzheu 	lpar_name[LPAR_NAME_LEN] = 0;
71024bbb1faSMichael Holzheu 	lpar_dir = hypfs_mkdir(sb, systems_dir, lpar_name);
71124bbb1faSMichael Holzheu 	if (IS_ERR(lpar_dir))
71224bbb1faSMichael Holzheu 		return lpar_dir;
71324bbb1faSMichael Holzheu 	cpus_dir = hypfs_mkdir(sb, lpar_dir, "cpus");
71424bbb1faSMichael Holzheu 	if (IS_ERR(cpus_dir))
71524bbb1faSMichael Holzheu 		return cpus_dir;
71624bbb1faSMichael Holzheu 	cpu_info = part_hdr + part_hdr__size(diag204_info_type);
71724bbb1faSMichael Holzheu 	for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) {
71824bbb1faSMichael Holzheu 		int rc;
71924bbb1faSMichael Holzheu 		rc = hypfs_create_cpu_files(sb, cpus_dir, cpu_info);
72024bbb1faSMichael Holzheu 		if (rc)
72124bbb1faSMichael Holzheu 			return ERR_PTR(rc);
72224bbb1faSMichael Holzheu 		cpu_info += cpu_info__size(diag204_info_type);
72324bbb1faSMichael Holzheu 	}
72424bbb1faSMichael Holzheu 	return cpu_info;
72524bbb1faSMichael Holzheu }
72624bbb1faSMichael Holzheu 
72724bbb1faSMichael Holzheu static int hypfs_create_phys_cpu_files(struct super_block *sb,
72824bbb1faSMichael Holzheu 				       struct dentry *cpus_dir, void *cpu_info)
72924bbb1faSMichael Holzheu {
73024bbb1faSMichael Holzheu 	struct dentry *cpu_dir;
73124bbb1faSMichael Holzheu 	char buffer[TMP_SIZE];
73224bbb1faSMichael Holzheu 	void *rc;
73324bbb1faSMichael Holzheu 
73424bbb1faSMichael Holzheu 	snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type,
73524bbb1faSMichael Holzheu 							    cpu_info));
73624bbb1faSMichael Holzheu 	cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
73724bbb1faSMichael Holzheu 	if (IS_ERR(cpu_dir))
73824bbb1faSMichael Holzheu 		return PTR_ERR(cpu_dir);
73924bbb1faSMichael Holzheu 	rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
74024bbb1faSMichael Holzheu 			      phys_cpu__mgm_time(diag204_info_type, cpu_info));
74124bbb1faSMichael Holzheu 	if (IS_ERR(rc))
74224bbb1faSMichael Holzheu 		return PTR_ERR(rc);
74324bbb1faSMichael Holzheu 	diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer);
74424bbb1faSMichael Holzheu 	rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
74524bbb1faSMichael Holzheu 	if (IS_ERR(rc))
74624bbb1faSMichael Holzheu 		return PTR_ERR(rc);
74724bbb1faSMichael Holzheu 	return 0;
74824bbb1faSMichael Holzheu }
74924bbb1faSMichael Holzheu 
75024bbb1faSMichael Holzheu static void *hypfs_create_phys_files(struct super_block *sb,
75124bbb1faSMichael Holzheu 				     struct dentry *parent_dir, void *phys_hdr)
75224bbb1faSMichael Holzheu {
75324bbb1faSMichael Holzheu 	int i;
75424bbb1faSMichael Holzheu 	void *cpu_info;
75524bbb1faSMichael Holzheu 	struct dentry *cpus_dir;
75624bbb1faSMichael Holzheu 
75724bbb1faSMichael Holzheu 	cpus_dir = hypfs_mkdir(sb, parent_dir, "cpus");
75824bbb1faSMichael Holzheu 	if (IS_ERR(cpus_dir))
75924bbb1faSMichael Holzheu 		return cpus_dir;
76024bbb1faSMichael Holzheu 	cpu_info = phys_hdr + phys_hdr__size(diag204_info_type);
76124bbb1faSMichael Holzheu 	for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) {
76224bbb1faSMichael Holzheu 		int rc;
76324bbb1faSMichael Holzheu 		rc = hypfs_create_phys_cpu_files(sb, cpus_dir, cpu_info);
76424bbb1faSMichael Holzheu 		if (rc)
76524bbb1faSMichael Holzheu 			return ERR_PTR(rc);
76624bbb1faSMichael Holzheu 		cpu_info += phys_cpu__size(diag204_info_type);
76724bbb1faSMichael Holzheu 	}
76824bbb1faSMichael Holzheu 	return cpu_info;
76924bbb1faSMichael Holzheu }
77024bbb1faSMichael Holzheu 
77124bbb1faSMichael Holzheu int hypfs_diag_create_files(struct super_block *sb, struct dentry *root)
77224bbb1faSMichael Holzheu {
77324bbb1faSMichael Holzheu 	struct dentry *systems_dir, *hyp_dir;
77424bbb1faSMichael Holzheu 	void *time_hdr, *part_hdr;
77524bbb1faSMichael Holzheu 	int i, rc;
77624bbb1faSMichael Holzheu 	void *buffer, *ptr;
77724bbb1faSMichael Holzheu 
77824bbb1faSMichael Holzheu 	buffer = diag204_store();
77924bbb1faSMichael Holzheu 	if (IS_ERR(buffer))
78024bbb1faSMichael Holzheu 		return PTR_ERR(buffer);
78124bbb1faSMichael Holzheu 
78224bbb1faSMichael Holzheu 	systems_dir = hypfs_mkdir(sb, root, "systems");
78324bbb1faSMichael Holzheu 	if (IS_ERR(systems_dir)) {
78424bbb1faSMichael Holzheu 		rc = PTR_ERR(systems_dir);
78524bbb1faSMichael Holzheu 		goto err_out;
78624bbb1faSMichael Holzheu 	}
78724bbb1faSMichael Holzheu 	time_hdr = (struct x_info_blk_hdr *)buffer;
78824bbb1faSMichael Holzheu 	part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type);
78924bbb1faSMichael Holzheu 	for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) {
79024bbb1faSMichael Holzheu 		part_hdr = hypfs_create_lpar_files(sb, systems_dir, part_hdr);
79124bbb1faSMichael Holzheu 		if (IS_ERR(part_hdr)) {
79224bbb1faSMichael Holzheu 			rc = PTR_ERR(part_hdr);
79324bbb1faSMichael Holzheu 			goto err_out;
79424bbb1faSMichael Holzheu 		}
79524bbb1faSMichael Holzheu 	}
79624bbb1faSMichael Holzheu 	if (info_blk_hdr__flags(diag204_info_type, time_hdr) & LPAR_PHYS_FLG) {
79724bbb1faSMichael Holzheu 		ptr = hypfs_create_phys_files(sb, root, part_hdr);
79824bbb1faSMichael Holzheu 		if (IS_ERR(ptr)) {
79924bbb1faSMichael Holzheu 			rc = PTR_ERR(ptr);
80024bbb1faSMichael Holzheu 			goto err_out;
80124bbb1faSMichael Holzheu 		}
80224bbb1faSMichael Holzheu 	}
80324bbb1faSMichael Holzheu 	hyp_dir = hypfs_mkdir(sb, root, "hyp");
80424bbb1faSMichael Holzheu 	if (IS_ERR(hyp_dir)) {
80524bbb1faSMichael Holzheu 		rc = PTR_ERR(hyp_dir);
80624bbb1faSMichael Holzheu 		goto err_out;
80724bbb1faSMichael Holzheu 	}
80824bbb1faSMichael Holzheu 	ptr = hypfs_create_str(sb, hyp_dir, "type", "LPAR Hypervisor");
80924bbb1faSMichael Holzheu 	if (IS_ERR(ptr)) {
81024bbb1faSMichael Holzheu 		rc = PTR_ERR(ptr);
81124bbb1faSMichael Holzheu 		goto err_out;
81224bbb1faSMichael Holzheu 	}
81324bbb1faSMichael Holzheu 	rc = 0;
81424bbb1faSMichael Holzheu 
81524bbb1faSMichael Holzheu err_out:
81624bbb1faSMichael Holzheu 	return rc;
81724bbb1faSMichael Holzheu }
818