xref: /openbmc/linux/arch/s390/hypfs/hypfs_vm.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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