xref: /linux/arch/s390/hypfs/hypfs_diag_fs.c (revision e48e99b6edf41c69c5528aa7ffb2daf3c59ee105)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *    Hypervisor filesystem for Linux on s390. Diag 204 and 224
4  *    implementation.
5  *
6  *    Copyright IBM Corp. 2006, 2008
7  *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
8  */
9 
10 #define KMSG_COMPONENT "hypfs"
11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12 
13 #include <linux/types.h>
14 #include <linux/errno.h>
15 #include <linux/slab.h>
16 #include <linux/string.h>
17 #include <linux/vmalloc.h>
18 #include <linux/mm.h>
19 #include <asm/machine.h>
20 #include <asm/diag.h>
21 #include <asm/ebcdic.h>
22 #include "hypfs_diag.h"
23 #include "hypfs.h"
24 
25 #define TMP_SIZE 64		/* size of temporary buffers */
26 
27 static char *diag224_cpu_names;			/* diag 224 name table */
28 static int diag224_idx2name(int index, char *name);
29 
30 /*
31  * DIAG 204 member access functions.
32  *
33  * Since we have two different diag 204 data formats for old and new s390
34  * machines, we do not access the structs directly, but use getter functions for
35  * each struct member instead. This should make the code more readable.
36  */
37 
38 /* Time information block */
39 
info_blk_hdr__size(enum diag204_format type)40 static inline int info_blk_hdr__size(enum diag204_format type)
41 {
42 	if (type == DIAG204_INFO_SIMPLE)
43 		return sizeof(struct diag204_info_blk_hdr);
44 	else /* DIAG204_INFO_EXT */
45 		return sizeof(struct diag204_x_info_blk_hdr);
46 }
47 
info_blk_hdr__npar(enum diag204_format type,void * hdr)48 static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
49 {
50 	if (type == DIAG204_INFO_SIMPLE)
51 		return ((struct diag204_info_blk_hdr *)hdr)->npar;
52 	else /* DIAG204_INFO_EXT */
53 		return ((struct diag204_x_info_blk_hdr *)hdr)->npar;
54 }
55 
info_blk_hdr__flags(enum diag204_format type,void * hdr)56 static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
57 {
58 	if (type == DIAG204_INFO_SIMPLE)
59 		return ((struct diag204_info_blk_hdr *)hdr)->flags;
60 	else /* DIAG204_INFO_EXT */
61 		return ((struct diag204_x_info_blk_hdr *)hdr)->flags;
62 }
63 
64 /* Partition header */
65 
part_hdr__size(enum diag204_format type)66 static inline int part_hdr__size(enum diag204_format type)
67 {
68 	if (type == DIAG204_INFO_SIMPLE)
69 		return sizeof(struct diag204_part_hdr);
70 	else /* DIAG204_INFO_EXT */
71 		return sizeof(struct diag204_x_part_hdr);
72 }
73 
part_hdr__rcpus(enum diag204_format type,void * hdr)74 static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
75 {
76 	if (type == DIAG204_INFO_SIMPLE)
77 		return ((struct diag204_part_hdr *)hdr)->cpus;
78 	else /* DIAG204_INFO_EXT */
79 		return ((struct diag204_x_part_hdr *)hdr)->rcpus;
80 }
81 
part_hdr__part_name(enum diag204_format type,void * hdr,char * name)82 static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
83 				       char *name)
84 {
85 	if (type == DIAG204_INFO_SIMPLE)
86 		memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name,
87 		       DIAG204_LPAR_NAME_LEN);
88 	else /* DIAG204_INFO_EXT */
89 		memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name,
90 		       DIAG204_LPAR_NAME_LEN);
91 	EBCASC(name, DIAG204_LPAR_NAME_LEN);
92 	name[DIAG204_LPAR_NAME_LEN] = 0;
93 	strim(name);
94 }
95 
96 /* CPU info block */
97 
cpu_info__size(enum diag204_format type)98 static inline int cpu_info__size(enum diag204_format type)
99 {
100 	if (type == DIAG204_INFO_SIMPLE)
101 		return sizeof(struct diag204_cpu_info);
102 	else /* DIAG204_INFO_EXT */
103 		return sizeof(struct diag204_x_cpu_info);
104 }
105 
cpu_info__ctidx(enum diag204_format type,void * hdr)106 static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
107 {
108 	if (type == DIAG204_INFO_SIMPLE)
109 		return ((struct diag204_cpu_info *)hdr)->ctidx;
110 	else /* DIAG204_INFO_EXT */
111 		return ((struct diag204_x_cpu_info *)hdr)->ctidx;
112 }
113 
cpu_info__cpu_addr(enum diag204_format type,void * hdr)114 static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
115 {
116 	if (type == DIAG204_INFO_SIMPLE)
117 		return ((struct diag204_cpu_info *)hdr)->cpu_addr;
118 	else /* DIAG204_INFO_EXT */
119 		return ((struct diag204_x_cpu_info *)hdr)->cpu_addr;
120 }
121 
cpu_info__acc_time(enum diag204_format type,void * hdr)122 static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
123 {
124 	if (type == DIAG204_INFO_SIMPLE)
125 		return ((struct diag204_cpu_info *)hdr)->acc_time;
126 	else /* DIAG204_INFO_EXT */
127 		return ((struct diag204_x_cpu_info *)hdr)->acc_time;
128 }
129 
cpu_info__lp_time(enum diag204_format type,void * hdr)130 static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
131 {
132 	if (type == DIAG204_INFO_SIMPLE)
133 		return ((struct diag204_cpu_info *)hdr)->lp_time;
134 	else /* DIAG204_INFO_EXT */
135 		return ((struct diag204_x_cpu_info *)hdr)->lp_time;
136 }
137 
cpu_info__online_time(enum diag204_format type,void * hdr)138 static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
139 {
140 	if (type == DIAG204_INFO_SIMPLE)
141 		return 0;	/* online_time not available in simple info */
142 	else /* DIAG204_INFO_EXT */
143 		return ((struct diag204_x_cpu_info *)hdr)->online_time;
144 }
145 
146 /* Physical header */
147 
phys_hdr__size(enum diag204_format type)148 static inline int phys_hdr__size(enum diag204_format type)
149 {
150 	if (type == DIAG204_INFO_SIMPLE)
151 		return sizeof(struct diag204_phys_hdr);
152 	else /* DIAG204_INFO_EXT */
153 		return sizeof(struct diag204_x_phys_hdr);
154 }
155 
phys_hdr__cpus(enum diag204_format type,void * hdr)156 static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
157 {
158 	if (type == DIAG204_INFO_SIMPLE)
159 		return ((struct diag204_phys_hdr *)hdr)->cpus;
160 	else /* DIAG204_INFO_EXT */
161 		return ((struct diag204_x_phys_hdr *)hdr)->cpus;
162 }
163 
164 /* Physical CPU info block */
165 
phys_cpu__size(enum diag204_format type)166 static inline int phys_cpu__size(enum diag204_format type)
167 {
168 	if (type == DIAG204_INFO_SIMPLE)
169 		return sizeof(struct diag204_phys_cpu);
170 	else /* DIAG204_INFO_EXT */
171 		return sizeof(struct diag204_x_phys_cpu);
172 }
173 
phys_cpu__cpu_addr(enum diag204_format type,void * hdr)174 static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
175 {
176 	if (type == DIAG204_INFO_SIMPLE)
177 		return ((struct diag204_phys_cpu *)hdr)->cpu_addr;
178 	else /* DIAG204_INFO_EXT */
179 		return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr;
180 }
181 
phys_cpu__mgm_time(enum diag204_format type,void * hdr)182 static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
183 {
184 	if (type == DIAG204_INFO_SIMPLE)
185 		return ((struct diag204_phys_cpu *)hdr)->mgm_time;
186 	else /* DIAG204_INFO_EXT */
187 		return ((struct diag204_x_phys_cpu *)hdr)->mgm_time;
188 }
189 
phys_cpu__ctidx(enum diag204_format type,void * hdr)190 static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
191 {
192 	if (type == DIAG204_INFO_SIMPLE)
193 		return ((struct diag204_phys_cpu *)hdr)->ctidx;
194 	else /* DIAG204_INFO_EXT */
195 		return ((struct diag204_x_phys_cpu *)hdr)->ctidx;
196 }
197 
198 /*
199  * Functions to create the directory structure
200  * *******************************************
201  */
202 
hypfs_create_cpu_files(struct dentry * cpus_dir,void * cpu_info)203 static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
204 {
205 	struct dentry *cpu_dir;
206 	char buffer[TMP_SIZE];
207 	void *rc;
208 
209 	snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_get_info_type(),
210 							    cpu_info));
211 	cpu_dir = hypfs_mkdir(cpus_dir, buffer);
212 	if (IS_ERR(cpu_dir))
213 		return PTR_ERR(cpu_dir);
214 	rc = hypfs_create_u64(cpu_dir, "mgmtime",
215 			      cpu_info__acc_time(diag204_get_info_type(), cpu_info) -
216 			      cpu_info__lp_time(diag204_get_info_type(), cpu_info));
217 	if (IS_ERR(rc))
218 		return PTR_ERR(rc);
219 	rc = hypfs_create_u64(cpu_dir, "cputime",
220 			      cpu_info__lp_time(diag204_get_info_type(), cpu_info));
221 	if (IS_ERR(rc))
222 		return PTR_ERR(rc);
223 	if (diag204_get_info_type() == DIAG204_INFO_EXT) {
224 		rc = hypfs_create_u64(cpu_dir, "onlinetime",
225 				      cpu_info__online_time(diag204_get_info_type(),
226 							    cpu_info));
227 		if (IS_ERR(rc))
228 			return PTR_ERR(rc);
229 	}
230 	diag224_idx2name(cpu_info__ctidx(diag204_get_info_type(), cpu_info), buffer);
231 	rc = hypfs_create_str(cpu_dir, "type", buffer);
232 	return PTR_ERR_OR_ZERO(rc);
233 }
234 
hypfs_create_lpar_files(struct dentry * systems_dir,void * part_hdr)235 static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr)
236 {
237 	struct dentry *cpus_dir;
238 	struct dentry *lpar_dir;
239 	char lpar_name[DIAG204_LPAR_NAME_LEN + 1];
240 	void *cpu_info;
241 	int i;
242 
243 	part_hdr__part_name(diag204_get_info_type(), part_hdr, lpar_name);
244 	lpar_name[DIAG204_LPAR_NAME_LEN] = 0;
245 	lpar_dir = hypfs_mkdir(systems_dir, lpar_name);
246 	if (IS_ERR(lpar_dir))
247 		return lpar_dir;
248 	cpus_dir = hypfs_mkdir(lpar_dir, "cpus");
249 	if (IS_ERR(cpus_dir))
250 		return cpus_dir;
251 	cpu_info = part_hdr + part_hdr__size(diag204_get_info_type());
252 	for (i = 0; i < part_hdr__rcpus(diag204_get_info_type(), part_hdr); i++) {
253 		int rc;
254 
255 		rc = hypfs_create_cpu_files(cpus_dir, cpu_info);
256 		if (rc)
257 			return ERR_PTR(rc);
258 		cpu_info += cpu_info__size(diag204_get_info_type());
259 	}
260 	return cpu_info;
261 }
262 
hypfs_create_phys_cpu_files(struct dentry * cpus_dir,void * cpu_info)263 static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
264 {
265 	struct dentry *cpu_dir;
266 	char buffer[TMP_SIZE];
267 	void *rc;
268 
269 	snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_get_info_type(),
270 							    cpu_info));
271 	cpu_dir = hypfs_mkdir(cpus_dir, buffer);
272 	if (IS_ERR(cpu_dir))
273 		return PTR_ERR(cpu_dir);
274 	rc = hypfs_create_u64(cpu_dir, "mgmtime",
275 			      phys_cpu__mgm_time(diag204_get_info_type(), cpu_info));
276 	if (IS_ERR(rc))
277 		return PTR_ERR(rc);
278 	diag224_idx2name(phys_cpu__ctidx(diag204_get_info_type(), cpu_info), buffer);
279 	rc = hypfs_create_str(cpu_dir, "type", buffer);
280 	return PTR_ERR_OR_ZERO(rc);
281 }
282 
hypfs_create_phys_files(struct dentry * parent_dir,void * phys_hdr)283 static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr)
284 {
285 	int i;
286 	void *cpu_info;
287 	struct dentry *cpus_dir;
288 
289 	cpus_dir = hypfs_mkdir(parent_dir, "cpus");
290 	if (IS_ERR(cpus_dir))
291 		return cpus_dir;
292 	cpu_info = phys_hdr + phys_hdr__size(diag204_get_info_type());
293 	for (i = 0; i < phys_hdr__cpus(diag204_get_info_type(), phys_hdr); i++) {
294 		int rc;
295 
296 		rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info);
297 		if (rc)
298 			return ERR_PTR(rc);
299 		cpu_info += phys_cpu__size(diag204_get_info_type());
300 	}
301 	return cpu_info;
302 }
303 
hypfs_diag_create_files(struct dentry * root)304 int hypfs_diag_create_files(struct dentry *root)
305 {
306 	struct dentry *systems_dir, *hyp_dir;
307 	void *time_hdr, *part_hdr;
308 	void *buffer, *ptr;
309 	int i, rc, pages;
310 
311 	buffer = diag204_get_buffer(diag204_get_info_type(), &pages);
312 	if (IS_ERR(buffer))
313 		return PTR_ERR(buffer);
314 	rc = diag204_store(buffer, pages);
315 	if (rc)
316 		return rc;
317 
318 	systems_dir = hypfs_mkdir(root, "systems");
319 	if (IS_ERR(systems_dir)) {
320 		rc = PTR_ERR(systems_dir);
321 		goto err_out;
322 	}
323 	time_hdr = (struct x_info_blk_hdr *)buffer;
324 	part_hdr = time_hdr + info_blk_hdr__size(diag204_get_info_type());
325 	for (i = 0; i < info_blk_hdr__npar(diag204_get_info_type(), time_hdr); i++) {
326 		part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr);
327 		if (IS_ERR(part_hdr)) {
328 			rc = PTR_ERR(part_hdr);
329 			goto err_out;
330 		}
331 	}
332 	if (info_blk_hdr__flags(diag204_get_info_type(), time_hdr) &
333 	    DIAG204_LPAR_PHYS_FLG) {
334 		ptr = hypfs_create_phys_files(root, part_hdr);
335 		if (IS_ERR(ptr)) {
336 			rc = PTR_ERR(ptr);
337 			goto err_out;
338 		}
339 	}
340 	hyp_dir = hypfs_mkdir(root, "hyp");
341 	if (IS_ERR(hyp_dir)) {
342 		rc = PTR_ERR(hyp_dir);
343 		goto err_out;
344 	}
345 	ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
346 	if (IS_ERR(ptr)) {
347 		rc = PTR_ERR(ptr);
348 		goto err_out;
349 	}
350 	rc = 0;
351 
352 err_out:
353 	return rc;
354 }
355 
356 /* Diagnose 224 functions */
357 
diag224_idx2name(int index,char * name)358 static int diag224_idx2name(int index, char *name)
359 {
360 	memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN),
361 	       DIAG204_CPU_NAME_LEN);
362 	name[DIAG204_CPU_NAME_LEN] = 0;
363 	strim(name);
364 	return 0;
365 }
366 
diag224_get_name_table(void)367 static int diag224_get_name_table(void)
368 {
369 	/* memory must be below 2GB */
370 	diag224_cpu_names = (char *)__get_free_page(GFP_KERNEL | GFP_DMA);
371 	if (!diag224_cpu_names)
372 		return -ENOMEM;
373 	if (diag224(diag224_cpu_names)) {
374 		free_page((unsigned long)diag224_cpu_names);
375 		return -EOPNOTSUPP;
376 	}
377 	EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
378 	return 0;
379 }
380 
diag224_delete_name_table(void)381 static void diag224_delete_name_table(void)
382 {
383 	free_page((unsigned long)diag224_cpu_names);
384 }
385 
__hypfs_diag_fs_init(void)386 int __init __hypfs_diag_fs_init(void)
387 {
388 	if (machine_is_lpar())
389 		return diag224_get_name_table();
390 	return 0;
391 }
392 
__hypfs_diag_fs_exit(void)393 void __hypfs_diag_fs_exit(void)
394 {
395 	diag224_delete_name_table();
396 }
397