1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
231cb4bd3SMichael Holzheu /*
331cb4bd3SMichael Holzheu * Hypervisor filesystem for Linux on s390. z/VM implementation.
431cb4bd3SMichael Holzheu *
5a53c8fabSHeiko Carstens * Copyright IBM Corp. 2006
631cb4bd3SMichael Holzheu * Author(s): Michael Holzheu <holzheu@de.ibm.com>
731cb4bd3SMichael Holzheu */
831cb4bd3SMichael Holzheu
931cb4bd3SMichael Holzheu #include <linux/types.h>
1031cb4bd3SMichael Holzheu #include <linux/errno.h>
1131cb4bd3SMichael Holzheu #include <linux/string.h>
1231cb4bd3SMichael Holzheu #include <linux/vmalloc.h>
13d09a307fSHeiko Carstens #include <asm/extable.h>
141ec2772eSMartin Schwidefsky #include <asm/diag.h>
1531cb4bd3SMichael Holzheu #include <asm/ebcdic.h>
1657b28f66SMichael Holzheu #include <asm/timex.h>
17*3325b4d8SHeiko Carstens #include "hypfs_vm.h"
1831cb4bd3SMichael Holzheu #include "hypfs.h"
1931cb4bd3SMichael Holzheu
2057b28f66SMichael Holzheu #define DBFS_D2FC_HDR_VERSION 0
2131cb4bd3SMichael Holzheu
2231cb4bd3SMichael Holzheu static char local_guest[] = " ";
2331cb4bd3SMichael Holzheu static char all_guests[] = "* ";
24663d34c8SVasily Gorbik static char *all_groups = all_guests;
25*3325b4d8SHeiko Carstens char *diag2fc_guest_query;
2631cb4bd3SMichael Holzheu
diag2fc(int size,char * query,void * addr)2731cb4bd3SMichael Holzheu static int diag2fc(int size, char* query, void *addr)
2831cb4bd3SMichael Holzheu {
2931cb4bd3SMichael Holzheu unsigned long residual_cnt;
3031cb4bd3SMichael Holzheu unsigned long rc;
3131cb4bd3SMichael Holzheu struct diag2fc_parm_list parm_list;
3231cb4bd3SMichael Holzheu
33*3325b4d8SHeiko Carstens memcpy(parm_list.userid, query, DIAG2FC_NAME_LEN);
34*3325b4d8SHeiko Carstens ASCEBC(parm_list.userid, DIAG2FC_NAME_LEN);
35*3325b4d8SHeiko Carstens memcpy(parm_list.aci_grp, all_groups, DIAG2FC_NAME_LEN);
36*3325b4d8SHeiko Carstens ASCEBC(parm_list.aci_grp, DIAG2FC_NAME_LEN);
3731cb4bd3SMichael Holzheu parm_list.addr = (unsigned long)addr;
3831cb4bd3SMichael Holzheu parm_list.size = size;
3931cb4bd3SMichael Holzheu parm_list.fmt = 0x02;
4031cb4bd3SMichael Holzheu rc = -1;
4131cb4bd3SMichael Holzheu
421ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X2FC);
4331cb4bd3SMichael Holzheu asm volatile(
4431cb4bd3SMichael Holzheu " diag %0,%1,0x2fc\n"
456c22c986SHeiko Carstens "0: nopr %%r7\n"
4631cb4bd3SMichael Holzheu EX_TABLE(0b,0b)
4731cb4bd3SMichael Holzheu : "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory");
4831cb4bd3SMichael Holzheu
4931cb4bd3SMichael Holzheu if ((rc != 0 ) && (rc != -2))
5031cb4bd3SMichael Holzheu return rc;
5131cb4bd3SMichael Holzheu else
5231cb4bd3SMichael Holzheu return -residual_cnt;
5331cb4bd3SMichael Holzheu }
5431cb4bd3SMichael Holzheu
5557b28f66SMichael Holzheu /*
5657b28f66SMichael Holzheu * Allocate buffer for "query" and store diag 2fc at "offset"
5757b28f66SMichael Holzheu */
diag2fc_store(char * query,unsigned int * count,int offset)58*3325b4d8SHeiko Carstens void *diag2fc_store(char *query, unsigned int *count, int offset)
5931cb4bd3SMichael Holzheu {
6057b28f66SMichael Holzheu void *data;
6131cb4bd3SMichael Holzheu int size;
6231cb4bd3SMichael Holzheu
6331cb4bd3SMichael Holzheu do {
6431cb4bd3SMichael Holzheu size = diag2fc(0, query, NULL);
6531cb4bd3SMichael Holzheu if (size < 0)
6631cb4bd3SMichael Holzheu return ERR_PTR(-EACCES);
6757b28f66SMichael Holzheu data = vmalloc(size + offset);
6831cb4bd3SMichael Holzheu if (!data)
6931cb4bd3SMichael Holzheu return ERR_PTR(-ENOMEM);
7057b28f66SMichael Holzheu if (diag2fc(size, query, data + offset) == 0)
7131cb4bd3SMichael Holzheu break;
7231cb4bd3SMichael Holzheu vfree(data);
7331cb4bd3SMichael Holzheu } while (1);
7457b28f66SMichael Holzheu *count = (size / sizeof(struct diag2fc_data));
7531cb4bd3SMichael Holzheu
7631cb4bd3SMichael Holzheu return data;
7731cb4bd3SMichael Holzheu }
7831cb4bd3SMichael Holzheu
diag2fc_free(const void * data)79*3325b4d8SHeiko Carstens void diag2fc_free(const void *data)
8031cb4bd3SMichael Holzheu {
8131cb4bd3SMichael Holzheu vfree(data);
8231cb4bd3SMichael Holzheu }
8331cb4bd3SMichael Holzheu
8457b28f66SMichael Holzheu struct dbfs_d2fc_hdr {
8557b28f66SMichael Holzheu u64 len; /* Length of d2fc buffer without header */
8657b28f66SMichael Holzheu u16 version; /* Version of header */
8701f224b9SHeiko Carstens union tod_clock tod_ext; /* TOD clock for d2fc */
8857b28f66SMichael Holzheu u64 count; /* Number of VM guests in d2fc buffer */
8957b28f66SMichael Holzheu char reserved[30];
9057b28f66SMichael Holzheu } __attribute__ ((packed));
9157b28f66SMichael Holzheu
9257b28f66SMichael Holzheu struct dbfs_d2fc {
9357b28f66SMichael Holzheu struct dbfs_d2fc_hdr hdr; /* 64 byte header */
9457b28f66SMichael Holzheu char buf[]; /* d2fc buffer */
9557b28f66SMichael Holzheu } __attribute__ ((packed));
9657b28f66SMichael Holzheu
dbfs_diag2fc_create(void ** data,void ** data_free_ptr,size_t * size)972fcb3686SMichael Holzheu static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size)
9857b28f66SMichael Holzheu {
992fcb3686SMichael Holzheu struct dbfs_d2fc *d2fc;
10057b28f66SMichael Holzheu unsigned int count;
10157b28f66SMichael Holzheu
102*3325b4d8SHeiko Carstens d2fc = diag2fc_store(diag2fc_guest_query, &count, sizeof(d2fc->hdr));
1032fcb3686SMichael Holzheu if (IS_ERR(d2fc))
1042fcb3686SMichael Holzheu return PTR_ERR(d2fc);
10501f224b9SHeiko Carstens store_tod_clock_ext(&d2fc->hdr.tod_ext);
1062fcb3686SMichael Holzheu d2fc->hdr.len = count * sizeof(struct diag2fc_data);
1072fcb3686SMichael Holzheu d2fc->hdr.version = DBFS_D2FC_HDR_VERSION;
1082fcb3686SMichael Holzheu d2fc->hdr.count = count;
1092fcb3686SMichael Holzheu memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved));
1102fcb3686SMichael Holzheu *data = d2fc;
1112fcb3686SMichael Holzheu *data_free_ptr = d2fc;
1122fcb3686SMichael Holzheu *size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr);
11357b28f66SMichael Holzheu return 0;
11457b28f66SMichael Holzheu }
11557b28f66SMichael Holzheu
1162fcb3686SMichael Holzheu static struct hypfs_dbfs_file dbfs_file_2fc = {
1172fcb3686SMichael Holzheu .name = "diag_2fc",
1182fcb3686SMichael Holzheu .data_create = dbfs_diag2fc_create,
1192fcb3686SMichael Holzheu .data_free = diag2fc_free,
12057b28f66SMichael Holzheu };
12157b28f66SMichael Holzheu
hypfs_vm_init(void)12231cb4bd3SMichael Holzheu int hypfs_vm_init(void)
12331cb4bd3SMichael Holzheu {
12457b28f66SMichael Holzheu if (!MACHINE_IS_VM)
12557b28f66SMichael Holzheu return 0;
12631cb4bd3SMichael Holzheu if (diag2fc(0, all_guests, NULL) > 0)
127*3325b4d8SHeiko Carstens diag2fc_guest_query = all_guests;
12831cb4bd3SMichael Holzheu else if (diag2fc(0, local_guest, NULL) > 0)
129*3325b4d8SHeiko Carstens diag2fc_guest_query = local_guest;
13031cb4bd3SMichael Holzheu else
13131cb4bd3SMichael Holzheu return -EACCES;
132f36108c4SGreg Kroah-Hartman hypfs_dbfs_create_file(&dbfs_file_2fc);
133f36108c4SGreg Kroah-Hartman return 0;
13431cb4bd3SMichael Holzheu }
13557b28f66SMichael Holzheu
hypfs_vm_exit(void)13657b28f66SMichael Holzheu void hypfs_vm_exit(void)
13757b28f66SMichael Holzheu {
13857b28f66SMichael Holzheu if (!MACHINE_IS_VM)
13957b28f66SMichael Holzheu return;
1402fcb3686SMichael Holzheu hypfs_dbfs_remove_file(&dbfs_file_2fc);
14157b28f66SMichael Holzheu }
142