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