1/*
2 *  arch/s390/hypfs/hypfs_diag.c
3 *    Hypervisor filesystem for Linux on s390. Diag 204 and 224
4 *    implementation.
5 *
6 *    Copyright (C) IBM Corp. 2006
7 *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
8 */
9
10#include <linux/types.h>
11#include <linux/errno.h>
12#include <linux/string.h>
13#include <linux/vmalloc.h>
14#include <asm/ebcdic.h>
15#include "hypfs.h"
16
17#define LPAR_NAME_LEN 8		/* lpar name len in diag 204 data */
18#define CPU_NAME_LEN 16		/* type name len of cpus in diag224 name table */
19#define TMP_SIZE 64		/* size of temporary buffers */
20
21/* diag 204 subcodes */
22enum diag204_sc {
23	SUBC_STIB4 = 4,
24	SUBC_RSI = 5,
25	SUBC_STIB6 = 6,
26	SUBC_STIB7 = 7
27};
28
29/* The two available diag 204 data formats */
30enum diag204_format {
31	INFO_SIMPLE = 0,
32	INFO_EXT = 0x00010000
33};
34
35/* bit is set in flags, when physical cpu info is included in diag 204 data */
36#define LPAR_PHYS_FLG  0x80
37
38static char *diag224_cpu_names;			/* diag 224 name table */
39static enum diag204_sc diag204_store_sc;	/* used subcode for store */
40static enum diag204_format diag204_info_type;	/* used diag 204 data format */
41
42static void *diag204_buf;		/* 4K aligned buffer for diag204 data */
43static void *diag204_buf_vmalloc;	/* vmalloc pointer for diag204 data */
44static int diag204_buf_pages;		/* number of pages for diag204 data */
45
46/*
47 * DIAG 204 data structures and member access functions.
48 *
49 * Since we have two different diag 204 data formats for old and new s390
50 * machines, we do not access the structs directly, but use getter functions for
51 * each struct member instead. This should make the code more readable.
52 */
53
54/* Time information block */
55
56struct info_blk_hdr {
57	__u8  npar;
58	__u8  flags;
59	__u16 tslice;
60	__u16 phys_cpus;
61	__u16 this_part;
62	__u64 curtod;
63} __attribute__ ((packed));
64
65struct x_info_blk_hdr {
66	__u8  npar;
67	__u8  flags;
68	__u16 tslice;
69	__u16 phys_cpus;
70	__u16 this_part;
71	__u64 curtod1;
72	__u64 curtod2;
73	char reserved[40];
74} __attribute__ ((packed));
75
76static inline int info_blk_hdr__size(enum diag204_format type)
77{
78	if (type == INFO_SIMPLE)
79		return sizeof(struct info_blk_hdr);
80	else /* INFO_EXT */
81		return sizeof(struct x_info_blk_hdr);
82}
83
84static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
85{
86	if (type == INFO_SIMPLE)
87		return ((struct info_blk_hdr *)hdr)->npar;
88	else /* INFO_EXT */
89		return ((struct x_info_blk_hdr *)hdr)->npar;
90}
91
92static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
93{
94	if (type == INFO_SIMPLE)
95		return ((struct info_blk_hdr *)hdr)->flags;
96	else /* INFO_EXT */
97		return ((struct x_info_blk_hdr *)hdr)->flags;
98}
99
100static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
101{
102	if (type == INFO_SIMPLE)
103		return ((struct info_blk_hdr *)hdr)->phys_cpus;
104	else /* INFO_EXT */
105		return ((struct x_info_blk_hdr *)hdr)->phys_cpus;
106}
107
108/* Partition header */
109
110struct part_hdr {
111	__u8 pn;
112	__u8 cpus;
113	char reserved[6];
114	char part_name[LPAR_NAME_LEN];
115} __attribute__ ((packed));
116
117struct x_part_hdr {
118	__u8  pn;
119	__u8  cpus;
120	__u8  rcpus;
121	__u8  pflag;
122	__u32 mlu;
123	char  part_name[LPAR_NAME_LEN];
124	char  lpc_name[8];
125	char  os_name[8];
126	__u64 online_cs;
127	__u64 online_es;
128	__u8  upid;
129	char  reserved1[3];
130	__u32 group_mlu;
131	char  group_name[8];
132	char  reserved2[32];
133} __attribute__ ((packed));
134
135static inline int part_hdr__size(enum diag204_format type)
136{
137	if (type == INFO_SIMPLE)
138		return sizeof(struct part_hdr);
139	else /* INFO_EXT */
140		return sizeof(struct x_part_hdr);
141}
142
143static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
144{
145	if (type == INFO_SIMPLE)
146		return ((struct part_hdr *)hdr)->cpus;
147	else /* INFO_EXT */
148		return ((struct x_part_hdr *)hdr)->rcpus;
149}
150
151static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
152				       char *name)
153{
154	if (type == INFO_SIMPLE)
155		memcpy(name, ((struct part_hdr *)hdr)->part_name,
156		       LPAR_NAME_LEN);
157	else /* INFO_EXT */
158		memcpy(name, ((struct x_part_hdr *)hdr)->part_name,
159		       LPAR_NAME_LEN);
160	EBCASC(name, LPAR_NAME_LEN);
161	name[LPAR_NAME_LEN] = 0;
162	strstrip(name);
163}
164
165struct cpu_info {
166	__u16 cpu_addr;
167	char  reserved1[2];
168	__u8  ctidx;
169	__u8  cflag;
170	__u16 weight;
171	__u64 acc_time;
172	__u64 lp_time;
173} __attribute__ ((packed));
174
175struct x_cpu_info {
176	__u16 cpu_addr;
177	char  reserved1[2];
178	__u8  ctidx;
179	__u8  cflag;
180	__u16 weight;
181	__u64 acc_time;
182	__u64 lp_time;
183	__u16 min_weight;
184	__u16 cur_weight;
185	__u16 max_weight;
186	char  reseved2[2];
187	__u64 online_time;
188	__u64 wait_time;
189	__u32 pma_weight;
190	__u32 polar_weight;
191	char  reserved3[40];
192} __attribute__ ((packed));
193
194/* CPU info block */
195
196static inline int cpu_info__size(enum diag204_format type)
197{
198	if (type == INFO_SIMPLE)
199		return sizeof(struct cpu_info);
200	else /* INFO_EXT */
201		return sizeof(struct x_cpu_info);
202}
203
204static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
205{
206	if (type == INFO_SIMPLE)
207		return ((struct cpu_info *)hdr)->ctidx;
208	else /* INFO_EXT */
209		return ((struct x_cpu_info *)hdr)->ctidx;
210}
211
212static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
213{
214	if (type == INFO_SIMPLE)
215		return ((struct cpu_info *)hdr)->cpu_addr;
216	else /* INFO_EXT */
217		return ((struct x_cpu_info *)hdr)->cpu_addr;
218}
219
220static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
221{
222	if (type == INFO_SIMPLE)
223		return ((struct cpu_info *)hdr)->acc_time;
224	else /* INFO_EXT */
225		return ((struct x_cpu_info *)hdr)->acc_time;
226}
227
228static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
229{
230	if (type == INFO_SIMPLE)
231		return ((struct cpu_info *)hdr)->lp_time;
232	else /* INFO_EXT */
233		return ((struct x_cpu_info *)hdr)->lp_time;
234}
235
236static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
237{
238	if (type == INFO_SIMPLE)
239		return 0;	/* online_time not available in simple info */
240	else /* INFO_EXT */
241		return ((struct x_cpu_info *)hdr)->online_time;
242}
243
244/* Physical header */
245
246struct phys_hdr {
247	char reserved1[1];
248	__u8 cpus;
249	char reserved2[6];
250	char mgm_name[8];
251} __attribute__ ((packed));
252
253struct x_phys_hdr {
254	char reserved1[1];
255	__u8 cpus;
256	char reserved2[6];
257	char mgm_name[8];
258	char reserved3[80];
259} __attribute__ ((packed));
260
261static inline int phys_hdr__size(enum diag204_format type)
262{
263	if (type == INFO_SIMPLE)
264		return sizeof(struct phys_hdr);
265	else /* INFO_EXT */
266		return sizeof(struct x_phys_hdr);
267}
268
269static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
270{
271	if (type == INFO_SIMPLE)
272		return ((struct phys_hdr *)hdr)->cpus;
273	else /* INFO_EXT */
274		return ((struct x_phys_hdr *)hdr)->cpus;
275}
276
277/* Physical CPU info block */
278
279struct phys_cpu {
280	__u16 cpu_addr;
281	char  reserved1[2];
282	__u8  ctidx;
283	char  reserved2[3];
284	__u64 mgm_time;
285	char  reserved3[8];
286} __attribute__ ((packed));
287
288struct x_phys_cpu {
289	__u16 cpu_addr;
290	char  reserved1[2];
291	__u8  ctidx;
292	char  reserved2[3];
293	__u64 mgm_time;
294	char  reserved3[80];
295} __attribute__ ((packed));
296
297static inline int phys_cpu__size(enum diag204_format type)
298{
299	if (type == INFO_SIMPLE)
300		return sizeof(struct phys_cpu);
301	else /* INFO_EXT */
302		return sizeof(struct x_phys_cpu);
303}
304
305static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
306{
307	if (type == INFO_SIMPLE)
308		return ((struct phys_cpu *)hdr)->cpu_addr;
309	else /* INFO_EXT */
310		return ((struct x_phys_cpu *)hdr)->cpu_addr;
311}
312
313static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
314{
315	if (type == INFO_SIMPLE)
316		return ((struct phys_cpu *)hdr)->mgm_time;
317	else /* INFO_EXT */
318		return ((struct x_phys_cpu *)hdr)->mgm_time;
319}
320
321static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
322{
323	if (type == INFO_SIMPLE)
324		return ((struct phys_cpu *)hdr)->ctidx;
325	else /* INFO_EXT */
326		return ((struct x_phys_cpu *)hdr)->ctidx;
327}
328
329/* Diagnose 204 functions */
330
331static int diag204(unsigned long subcode, unsigned long size, void *addr)
332{
333	register unsigned long _subcode asm("0") = subcode;
334	register unsigned long _size asm("1") = size;
335
336	asm volatile(
337		"	diag	%2,%0,0x204\n"
338		"0:\n"
339		EX_TABLE(0b,0b)
340		: "+d" (_subcode), "+d" (_size) : "d" (addr) : "memory");
341	if (_subcode)
342		return -1;
343	return _size;
344}
345
346/*
347 * For the old diag subcode 4 with simple data format we have to use real
348 * memory. If we use subcode 6 or 7 with extended data format, we can (and
349 * should) use vmalloc, since we need a lot of memory in that case. Currently
350 * up to 93 pages!
351 */
352
353static void diag204_free_buffer(void)
354{
355	if (!diag204_buf)
356		return;
357	if (diag204_buf_vmalloc) {
358		vfree(diag204_buf_vmalloc);
359		diag204_buf_vmalloc = NULL;
360	} else {
361		free_pages((unsigned long) diag204_buf, 0);
362	}
363	diag204_buf_pages = 0;
364	diag204_buf = NULL;
365}
366
367static void *diag204_alloc_vbuf(int pages)
368{
369	/* The buffer has to be page aligned! */
370	diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
371	if (!diag204_buf_vmalloc)
372		return ERR_PTR(-ENOMEM);
373	diag204_buf = (void*)((unsigned long)diag204_buf_vmalloc
374				& ~0xfffUL) + 0x1000;
375	diag204_buf_pages = pages;
376	return diag204_buf;
377}
378
379static void *diag204_alloc_rbuf(void)
380{
381	diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0);
382	if (!diag204_buf)
383		return ERR_PTR(-ENOMEM);
384	diag204_buf_pages = 1;
385	return diag204_buf;
386}
387
388static void *diag204_get_buffer(enum diag204_format fmt, int *pages)
389{
390	if (diag204_buf) {
391		*pages = diag204_buf_pages;
392		return diag204_buf;
393	}
394	if (fmt == INFO_SIMPLE) {
395		*pages = 1;
396		return diag204_alloc_rbuf();
397	} else {/* INFO_EXT */
398		*pages = diag204((unsigned long)SUBC_RSI |
399				 (unsigned long)INFO_EXT, 0, NULL);
400		if (*pages <= 0)
401			return ERR_PTR(-ENOSYS);
402		else
403			return diag204_alloc_vbuf(*pages);
404	}
405}
406
407/*
408 * diag204_probe() has to find out, which type of diagnose 204 implementation
409 * we have on our machine. Currently there are three possible scanarios:
410 *   - subcode 4   + simple data format (only one page)
411 *   - subcode 4-6 + extended data format
412 *   - subcode 4-7 + extended data format
413 *
414 * Subcode 5 is used to retrieve the size of the data, provided by subcodes
415 * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition
416 * to subcode 6 it provides also information about secondary cpus.
417 * In order to get as much information as possible, we first try
418 * subcode 7, then 6 and if both fail, we use subcode 4.
419 */
420
421static int diag204_probe(void)
422{
423	void *buf;
424	int pages, rc;
425
426	buf = diag204_get_buffer(INFO_EXT, &pages);
427	if (!IS_ERR(buf)) {
428		if (diag204((unsigned long)SUBC_STIB7 |
429			    (unsigned long)INFO_EXT, pages, buf) >= 0) {
430			diag204_store_sc = SUBC_STIB7;
431			diag204_info_type = INFO_EXT;
432			goto out;
433		}
434		if (diag204((unsigned long)SUBC_STIB6 |
435			    (unsigned long)INFO_EXT, pages, buf) >= 0) {
436			diag204_store_sc = SUBC_STIB7;
437			diag204_info_type = INFO_EXT;
438			goto out;
439		}
440		diag204_free_buffer();
441	}
442
443	/* subcodes 6 and 7 failed, now try subcode 4 */
444
445	buf = diag204_get_buffer(INFO_SIMPLE, &pages);
446	if (IS_ERR(buf)) {
447		rc = PTR_ERR(buf);
448		goto fail_alloc;
449	}
450	if (diag204((unsigned long)SUBC_STIB4 |
451		    (unsigned long)INFO_SIMPLE, pages, buf) >= 0) {
452		diag204_store_sc = SUBC_STIB4;
453		diag204_info_type = INFO_SIMPLE;
454		goto out;
455	} else {
456		rc = -ENOSYS;
457		goto fail_store;
458	}
459out:
460	rc = 0;
461fail_store:
462	diag204_free_buffer();
463fail_alloc:
464	return rc;
465}
466
467static void *diag204_store(void)
468{
469	void *buf;
470	int pages;
471
472	buf = diag204_get_buffer(diag204_info_type, &pages);
473	if (IS_ERR(buf))
474		goto out;
475	if (diag204((unsigned long)diag204_store_sc |
476		    (unsigned long)diag204_info_type, pages, buf) < 0)
477		return ERR_PTR(-ENOSYS);
478out:
479	return buf;
480}
481
482/* Diagnose 224 functions */
483
484static int diag224(void *ptr)
485{
486	int rc = -ENOTSUPP;
487
488	asm volatile(
489		"	diag	%1,%2,0x224\n"
490		"0:	lhi	%0,0x0\n"
491		"1:\n"
492		EX_TABLE(0b,1b)
493		: "+d" (rc) :"d" (0), "d" (ptr) : "memory");
494	return rc;
495}
496
497static int diag224_get_name_table(void)
498{
499	/* memory must be below 2GB */
500	diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
501	if (!diag224_cpu_names)
502		return -ENOMEM;
503	if (diag224(diag224_cpu_names)) {
504		kfree(diag224_cpu_names);
505		return -ENOTSUPP;
506	}
507	EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
508	return 0;
509}
510
511static void diag224_delete_name_table(void)
512{
513	kfree(diag224_cpu_names);
514}
515
516static int diag224_idx2name(int index, char *name)
517{
518	memcpy(name, diag224_cpu_names + ((index + 1) * CPU_NAME_LEN),
519		CPU_NAME_LEN);
520	name[CPU_NAME_LEN] = 0;
521	strstrip(name);
522	return 0;
523}
524
525__init int hypfs_diag_init(void)
526{
527	int rc;
528
529	if (diag204_probe()) {
530		printk(KERN_ERR "hypfs: diag 204 not working.");
531		return -ENODATA;
532	}
533	rc = diag224_get_name_table();
534	if (rc) {
535		diag204_free_buffer();
536		printk(KERN_ERR "hypfs: could not get name table.\n");
537	}
538	return rc;
539}
540
541void hypfs_diag_exit(void)
542{
543	diag224_delete_name_table();
544	diag204_free_buffer();
545}
546
547/*
548 * Functions to create the directory structure
549 * *******************************************
550 */
551
552static int hypfs_create_cpu_files(struct super_block *sb,
553				  struct dentry *cpus_dir, void *cpu_info)
554{
555	struct dentry *cpu_dir;
556	char buffer[TMP_SIZE];
557	void *rc;
558
559	snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type,
560							    cpu_info));
561	cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
562	rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
563			      cpu_info__acc_time(diag204_info_type, cpu_info) -
564			      cpu_info__lp_time(diag204_info_type, cpu_info));
565	if (IS_ERR(rc))
566		return PTR_ERR(rc);
567	rc = hypfs_create_u64(sb, cpu_dir, "cputime",
568			      cpu_info__lp_time(diag204_info_type, cpu_info));
569	if (IS_ERR(rc))
570		return PTR_ERR(rc);
571	if (diag204_info_type == INFO_EXT) {
572		rc = hypfs_create_u64(sb, cpu_dir, "onlinetime",
573				      cpu_info__online_time(diag204_info_type,
574							    cpu_info));
575		if (IS_ERR(rc))
576			return PTR_ERR(rc);
577	}
578	diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer);
579	rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
580	if (IS_ERR(rc))
581		return PTR_ERR(rc);
582	return 0;
583}
584
585static void *hypfs_create_lpar_files(struct super_block *sb,
586				     struct dentry *systems_dir, void *part_hdr)
587{
588	struct dentry *cpus_dir;
589	struct dentry *lpar_dir;
590	char lpar_name[LPAR_NAME_LEN + 1];
591	void *cpu_info;
592	int i;
593
594	part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
595	lpar_name[LPAR_NAME_LEN] = 0;
596	lpar_dir = hypfs_mkdir(sb, systems_dir, lpar_name);
597	if (IS_ERR(lpar_dir))
598		return lpar_dir;
599	cpus_dir = hypfs_mkdir(sb, lpar_dir, "cpus");
600	if (IS_ERR(cpus_dir))
601		return cpus_dir;
602	cpu_info = part_hdr + part_hdr__size(diag204_info_type);
603	for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) {
604		int rc;
605		rc = hypfs_create_cpu_files(sb, cpus_dir, cpu_info);
606		if (rc)
607			return ERR_PTR(rc);
608		cpu_info += cpu_info__size(diag204_info_type);
609	}
610	return cpu_info;
611}
612
613static int hypfs_create_phys_cpu_files(struct super_block *sb,
614				       struct dentry *cpus_dir, void *cpu_info)
615{
616	struct dentry *cpu_dir;
617	char buffer[TMP_SIZE];
618	void *rc;
619
620	snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type,
621							    cpu_info));
622	cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
623	if (IS_ERR(cpu_dir))
624		return PTR_ERR(cpu_dir);
625	rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
626			      phys_cpu__mgm_time(diag204_info_type, cpu_info));
627	if (IS_ERR(rc))
628		return PTR_ERR(rc);
629	diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer);
630	rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
631	if (IS_ERR(rc))
632		return PTR_ERR(rc);
633	return 0;
634}
635
636static void *hypfs_create_phys_files(struct super_block *sb,
637				     struct dentry *parent_dir, void *phys_hdr)
638{
639	int i;
640	void *cpu_info;
641	struct dentry *cpus_dir;
642
643	cpus_dir = hypfs_mkdir(sb, parent_dir, "cpus");
644	if (IS_ERR(cpus_dir))
645		return cpus_dir;
646	cpu_info = phys_hdr + phys_hdr__size(diag204_info_type);
647	for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) {
648		int rc;
649		rc = hypfs_create_phys_cpu_files(sb, cpus_dir, cpu_info);
650		if (rc)
651			return ERR_PTR(rc);
652		cpu_info += phys_cpu__size(diag204_info_type);
653	}
654	return cpu_info;
655}
656
657int hypfs_diag_create_files(struct super_block *sb, struct dentry *root)
658{
659	struct dentry *systems_dir, *hyp_dir;
660	void *time_hdr, *part_hdr;
661	int i, rc;
662	void *buffer, *ptr;
663
664	buffer = diag204_store();
665	if (IS_ERR(buffer))
666		return PTR_ERR(buffer);
667
668	systems_dir = hypfs_mkdir(sb, root, "systems");
669	if (IS_ERR(systems_dir)) {
670		rc = PTR_ERR(systems_dir);
671		goto err_out;
672	}
673	time_hdr = (struct x_info_blk_hdr *)buffer;
674	part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type);
675	for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) {
676		part_hdr = hypfs_create_lpar_files(sb, systems_dir, part_hdr);
677		if (IS_ERR(part_hdr)) {
678			rc = PTR_ERR(part_hdr);
679			goto err_out;
680		}
681	}
682	if (info_blk_hdr__flags(diag204_info_type, time_hdr) & LPAR_PHYS_FLG) {
683		ptr = hypfs_create_phys_files(sb, root, part_hdr);
684		if (IS_ERR(ptr)) {
685			rc = PTR_ERR(ptr);
686			goto err_out;
687		}
688	}
689	hyp_dir = hypfs_mkdir(sb, root, "hyp");
690	if (IS_ERR(hyp_dir)) {
691		rc = PTR_ERR(hyp_dir);
692		goto err_out;
693	}
694	ptr = hypfs_create_str(sb, hyp_dir, "type", "LPAR Hypervisor");
695	if (IS_ERR(ptr)) {
696		rc = PTR_ERR(ptr);
697		goto err_out;
698	}
699	rc = 0;
700
701err_out:
702	return rc;
703}
704