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