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