18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    Hypervisor filesystem for Linux on s390. z/VM implementation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2006
68c2ecf20Sopenharmony_ci *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/types.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/string.h>
128c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
138c2ecf20Sopenharmony_ci#include <asm/diag.h>
148c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
158c2ecf20Sopenharmony_ci#include <asm/timex.h>
168c2ecf20Sopenharmony_ci#include "hypfs.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define NAME_LEN 8
198c2ecf20Sopenharmony_ci#define DBFS_D2FC_HDR_VERSION 0
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic char local_guest[] = "        ";
228c2ecf20Sopenharmony_cistatic char all_guests[] = "*       ";
238c2ecf20Sopenharmony_cistatic char *all_groups = all_guests;
248c2ecf20Sopenharmony_cistatic char *guest_query;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct diag2fc_data {
278c2ecf20Sopenharmony_ci	__u32 version;
288c2ecf20Sopenharmony_ci	__u32 flags;
298c2ecf20Sopenharmony_ci	__u64 used_cpu;
308c2ecf20Sopenharmony_ci	__u64 el_time;
318c2ecf20Sopenharmony_ci	__u64 mem_min_kb;
328c2ecf20Sopenharmony_ci	__u64 mem_max_kb;
338c2ecf20Sopenharmony_ci	__u64 mem_share_kb;
348c2ecf20Sopenharmony_ci	__u64 mem_used_kb;
358c2ecf20Sopenharmony_ci	__u32 pcpus;
368c2ecf20Sopenharmony_ci	__u32 lcpus;
378c2ecf20Sopenharmony_ci	__u32 vcpus;
388c2ecf20Sopenharmony_ci	__u32 ocpus;
398c2ecf20Sopenharmony_ci	__u32 cpu_max;
408c2ecf20Sopenharmony_ci	__u32 cpu_shares;
418c2ecf20Sopenharmony_ci	__u32 cpu_use_samp;
428c2ecf20Sopenharmony_ci	__u32 cpu_delay_samp;
438c2ecf20Sopenharmony_ci	__u32 page_wait_samp;
448c2ecf20Sopenharmony_ci	__u32 idle_samp;
458c2ecf20Sopenharmony_ci	__u32 other_samp;
468c2ecf20Sopenharmony_ci	__u32 total_samp;
478c2ecf20Sopenharmony_ci	char  guest_name[NAME_LEN];
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct diag2fc_parm_list {
518c2ecf20Sopenharmony_ci	char userid[NAME_LEN];
528c2ecf20Sopenharmony_ci	char aci_grp[NAME_LEN];
538c2ecf20Sopenharmony_ci	__u64 addr;
548c2ecf20Sopenharmony_ci	__u32 size;
558c2ecf20Sopenharmony_ci	__u32 fmt;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int diag2fc(int size, char* query, void *addr)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	unsigned long residual_cnt;
618c2ecf20Sopenharmony_ci	unsigned long rc;
628c2ecf20Sopenharmony_ci	struct diag2fc_parm_list parm_list;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	memcpy(parm_list.userid, query, NAME_LEN);
658c2ecf20Sopenharmony_ci	ASCEBC(parm_list.userid, NAME_LEN);
668c2ecf20Sopenharmony_ci	memcpy(parm_list.aci_grp, all_groups, NAME_LEN);
678c2ecf20Sopenharmony_ci	ASCEBC(parm_list.aci_grp, NAME_LEN);
688c2ecf20Sopenharmony_ci	parm_list.addr = (unsigned long)addr;
698c2ecf20Sopenharmony_ci	parm_list.size = size;
708c2ecf20Sopenharmony_ci	parm_list.fmt = 0x02;
718c2ecf20Sopenharmony_ci	rc = -1;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	diag_stat_inc(DIAG_STAT_X2FC);
748c2ecf20Sopenharmony_ci	asm volatile(
758c2ecf20Sopenharmony_ci		"	diag    %0,%1,0x2fc\n"
768c2ecf20Sopenharmony_ci		"0:	nopr	%%r7\n"
778c2ecf20Sopenharmony_ci		EX_TABLE(0b,0b)
788c2ecf20Sopenharmony_ci		: "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory");
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if ((rc != 0 ) && (rc != -2))
818c2ecf20Sopenharmony_ci		return rc;
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		return -residual_cnt;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * Allocate buffer for "query" and store diag 2fc at "offset"
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic void *diag2fc_store(char *query, unsigned int *count, int offset)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	void *data;
928c2ecf20Sopenharmony_ci	int size;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	do {
958c2ecf20Sopenharmony_ci		size = diag2fc(0, query, NULL);
968c2ecf20Sopenharmony_ci		if (size < 0)
978c2ecf20Sopenharmony_ci			return ERR_PTR(-EACCES);
988c2ecf20Sopenharmony_ci		data = vmalloc(size + offset);
998c2ecf20Sopenharmony_ci		if (!data)
1008c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
1018c2ecf20Sopenharmony_ci		if (diag2fc(size, query, data + offset) == 0)
1028c2ecf20Sopenharmony_ci			break;
1038c2ecf20Sopenharmony_ci		vfree(data);
1048c2ecf20Sopenharmony_ci	} while (1);
1058c2ecf20Sopenharmony_ci	*count = (size / sizeof(struct diag2fc_data));
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return data;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void diag2fc_free(const void *data)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	vfree(data);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define ATTRIBUTE(dir, name, member) \
1168c2ecf20Sopenharmony_cido { \
1178c2ecf20Sopenharmony_ci	void *rc; \
1188c2ecf20Sopenharmony_ci	rc = hypfs_create_u64(dir, name, member); \
1198c2ecf20Sopenharmony_ci	if (IS_ERR(rc)) \
1208c2ecf20Sopenharmony_ci		return PTR_ERR(rc); \
1218c2ecf20Sopenharmony_ci} while(0)
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int hypfs_vm_create_guest(struct dentry *systems_dir,
1248c2ecf20Sopenharmony_ci				 struct diag2fc_data *data)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	char guest_name[NAME_LEN + 1] = {};
1278c2ecf20Sopenharmony_ci	struct dentry *guest_dir, *cpus_dir, *samples_dir, *mem_dir;
1288c2ecf20Sopenharmony_ci	int dedicated_flag, capped_value;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	capped_value = (data->flags & 0x00000006) >> 1;
1318c2ecf20Sopenharmony_ci	dedicated_flag = (data->flags & 0x00000008) >> 3;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* guest dir */
1348c2ecf20Sopenharmony_ci	memcpy(guest_name, data->guest_name, NAME_LEN);
1358c2ecf20Sopenharmony_ci	EBCASC(guest_name, NAME_LEN);
1368c2ecf20Sopenharmony_ci	strim(guest_name);
1378c2ecf20Sopenharmony_ci	guest_dir = hypfs_mkdir(systems_dir, guest_name);
1388c2ecf20Sopenharmony_ci	if (IS_ERR(guest_dir))
1398c2ecf20Sopenharmony_ci		return PTR_ERR(guest_dir);
1408c2ecf20Sopenharmony_ci	ATTRIBUTE(guest_dir, "onlinetime_us", data->el_time);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* logical cpu information */
1438c2ecf20Sopenharmony_ci	cpus_dir = hypfs_mkdir(guest_dir, "cpus");
1448c2ecf20Sopenharmony_ci	if (IS_ERR(cpus_dir))
1458c2ecf20Sopenharmony_ci		return PTR_ERR(cpus_dir);
1468c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "cputime_us", data->used_cpu);
1478c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "capped", capped_value);
1488c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "dedicated", dedicated_flag);
1498c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "count", data->vcpus);
1508c2ecf20Sopenharmony_ci	/*
1518c2ecf20Sopenharmony_ci	 * Note: The "weight_min" attribute got the wrong name.
1528c2ecf20Sopenharmony_ci	 * The value represents the number of non-stopped (operating)
1538c2ecf20Sopenharmony_ci	 * CPUS.
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "weight_min", data->ocpus);
1568c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "weight_max", data->cpu_max);
1578c2ecf20Sopenharmony_ci	ATTRIBUTE(cpus_dir, "weight_cur", data->cpu_shares);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* memory information */
1608c2ecf20Sopenharmony_ci	mem_dir = hypfs_mkdir(guest_dir, "mem");
1618c2ecf20Sopenharmony_ci	if (IS_ERR(mem_dir))
1628c2ecf20Sopenharmony_ci		return PTR_ERR(mem_dir);
1638c2ecf20Sopenharmony_ci	ATTRIBUTE(mem_dir, "min_KiB", data->mem_min_kb);
1648c2ecf20Sopenharmony_ci	ATTRIBUTE(mem_dir, "max_KiB", data->mem_max_kb);
1658c2ecf20Sopenharmony_ci	ATTRIBUTE(mem_dir, "used_KiB", data->mem_used_kb);
1668c2ecf20Sopenharmony_ci	ATTRIBUTE(mem_dir, "share_KiB", data->mem_share_kb);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* samples */
1698c2ecf20Sopenharmony_ci	samples_dir = hypfs_mkdir(guest_dir, "samples");
1708c2ecf20Sopenharmony_ci	if (IS_ERR(samples_dir))
1718c2ecf20Sopenharmony_ci		return PTR_ERR(samples_dir);
1728c2ecf20Sopenharmony_ci	ATTRIBUTE(samples_dir, "cpu_using", data->cpu_use_samp);
1738c2ecf20Sopenharmony_ci	ATTRIBUTE(samples_dir, "cpu_delay", data->cpu_delay_samp);
1748c2ecf20Sopenharmony_ci	ATTRIBUTE(samples_dir, "mem_delay", data->page_wait_samp);
1758c2ecf20Sopenharmony_ci	ATTRIBUTE(samples_dir, "idle", data->idle_samp);
1768c2ecf20Sopenharmony_ci	ATTRIBUTE(samples_dir, "other", data->other_samp);
1778c2ecf20Sopenharmony_ci	ATTRIBUTE(samples_dir, "total", data->total_samp);
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciint hypfs_vm_create_files(struct dentry *root)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct dentry *dir, *file;
1848c2ecf20Sopenharmony_ci	struct diag2fc_data *data;
1858c2ecf20Sopenharmony_ci	unsigned int count = 0;
1868c2ecf20Sopenharmony_ci	int rc, i;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	data = diag2fc_store(guest_query, &count, 0);
1898c2ecf20Sopenharmony_ci	if (IS_ERR(data))
1908c2ecf20Sopenharmony_ci		return PTR_ERR(data);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Hpervisor Info */
1938c2ecf20Sopenharmony_ci	dir = hypfs_mkdir(root, "hyp");
1948c2ecf20Sopenharmony_ci	if (IS_ERR(dir)) {
1958c2ecf20Sopenharmony_ci		rc = PTR_ERR(dir);
1968c2ecf20Sopenharmony_ci		goto failed;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	file = hypfs_create_str(dir, "type", "z/VM Hypervisor");
1998c2ecf20Sopenharmony_ci	if (IS_ERR(file)) {
2008c2ecf20Sopenharmony_ci		rc = PTR_ERR(file);
2018c2ecf20Sopenharmony_ci		goto failed;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* physical cpus */
2058c2ecf20Sopenharmony_ci	dir = hypfs_mkdir(root, "cpus");
2068c2ecf20Sopenharmony_ci	if (IS_ERR(dir)) {
2078c2ecf20Sopenharmony_ci		rc = PTR_ERR(dir);
2088c2ecf20Sopenharmony_ci		goto failed;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	file = hypfs_create_u64(dir, "count", data->lcpus);
2118c2ecf20Sopenharmony_ci	if (IS_ERR(file)) {
2128c2ecf20Sopenharmony_ci		rc = PTR_ERR(file);
2138c2ecf20Sopenharmony_ci		goto failed;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* guests */
2178c2ecf20Sopenharmony_ci	dir = hypfs_mkdir(root, "systems");
2188c2ecf20Sopenharmony_ci	if (IS_ERR(dir)) {
2198c2ecf20Sopenharmony_ci		rc = PTR_ERR(dir);
2208c2ecf20Sopenharmony_ci		goto failed;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
2248c2ecf20Sopenharmony_ci		rc = hypfs_vm_create_guest(dir, &(data[i]));
2258c2ecf20Sopenharmony_ci		if (rc)
2268c2ecf20Sopenharmony_ci			goto failed;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	diag2fc_free(data);
2298c2ecf20Sopenharmony_ci	return 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cifailed:
2328c2ecf20Sopenharmony_ci	diag2fc_free(data);
2338c2ecf20Sopenharmony_ci	return rc;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistruct dbfs_d2fc_hdr {
2378c2ecf20Sopenharmony_ci	u64	len;		/* Length of d2fc buffer without header */
2388c2ecf20Sopenharmony_ci	u16	version;	/* Version of header */
2398c2ecf20Sopenharmony_ci	char	tod_ext[STORE_CLOCK_EXT_SIZE]; /* TOD clock for d2fc */
2408c2ecf20Sopenharmony_ci	u64	count;		/* Number of VM guests in d2fc buffer */
2418c2ecf20Sopenharmony_ci	char	reserved[30];
2428c2ecf20Sopenharmony_ci} __attribute__ ((packed));
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistruct dbfs_d2fc {
2458c2ecf20Sopenharmony_ci	struct dbfs_d2fc_hdr	hdr;	/* 64 byte header */
2468c2ecf20Sopenharmony_ci	char			buf[];	/* d2fc buffer */
2478c2ecf20Sopenharmony_ci} __attribute__ ((packed));
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct dbfs_d2fc *d2fc;
2528c2ecf20Sopenharmony_ci	unsigned int count;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr));
2558c2ecf20Sopenharmony_ci	if (IS_ERR(d2fc))
2568c2ecf20Sopenharmony_ci		return PTR_ERR(d2fc);
2578c2ecf20Sopenharmony_ci	get_tod_clock_ext(d2fc->hdr.tod_ext);
2588c2ecf20Sopenharmony_ci	d2fc->hdr.len = count * sizeof(struct diag2fc_data);
2598c2ecf20Sopenharmony_ci	d2fc->hdr.version = DBFS_D2FC_HDR_VERSION;
2608c2ecf20Sopenharmony_ci	d2fc->hdr.count = count;
2618c2ecf20Sopenharmony_ci	memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved));
2628c2ecf20Sopenharmony_ci	*data = d2fc;
2638c2ecf20Sopenharmony_ci	*data_free_ptr = d2fc;
2648c2ecf20Sopenharmony_ci	*size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr);
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic struct hypfs_dbfs_file dbfs_file_2fc = {
2698c2ecf20Sopenharmony_ci	.name		= "diag_2fc",
2708c2ecf20Sopenharmony_ci	.data_create	= dbfs_diag2fc_create,
2718c2ecf20Sopenharmony_ci	.data_free	= diag2fc_free,
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ciint hypfs_vm_init(void)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	if (!MACHINE_IS_VM)
2778c2ecf20Sopenharmony_ci		return 0;
2788c2ecf20Sopenharmony_ci	if (diag2fc(0, all_guests, NULL) > 0)
2798c2ecf20Sopenharmony_ci		guest_query = all_guests;
2808c2ecf20Sopenharmony_ci	else if (diag2fc(0, local_guest, NULL) > 0)
2818c2ecf20Sopenharmony_ci		guest_query = local_guest;
2828c2ecf20Sopenharmony_ci	else
2838c2ecf20Sopenharmony_ci		return -EACCES;
2848c2ecf20Sopenharmony_ci	hypfs_dbfs_create_file(&dbfs_file_2fc);
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_civoid hypfs_vm_exit(void)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	if (!MACHINE_IS_VM)
2918c2ecf20Sopenharmony_ci		return;
2928c2ecf20Sopenharmony_ci	hypfs_dbfs_remove_file(&dbfs_file_2fc);
2938c2ecf20Sopenharmony_ci}
294