162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hypervisor filesystem for Linux on s390. Diag 204 and 224 462306a36Sopenharmony_ci * implementation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright IBM Corp. 2006, 2008 762306a36Sopenharmony_ci * Author(s): Michael Holzheu <holzheu@de.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define KMSG_COMPONENT "hypfs" 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/vmalloc.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <asm/diag.h> 2062306a36Sopenharmony_ci#include <asm/ebcdic.h> 2162306a36Sopenharmony_ci#include "hypfs_diag.h" 2262306a36Sopenharmony_ci#include "hypfs.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DBFS_D204_HDR_VERSION 0 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic enum diag204_sc diag204_store_sc; /* used subcode for store */ 2762306a36Sopenharmony_cistatic enum diag204_format diag204_info_type; /* used diag 204 data format */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void *diag204_buf; /* 4K aligned buffer for diag204 data */ 3062306a36Sopenharmony_cistatic int diag204_buf_pages; /* number of pages for diag204 data */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct dentry *dbfs_d204_file; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cienum diag204_format diag204_get_info_type(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return diag204_info_type; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void diag204_set_info_type(enum diag204_format type) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci diag204_info_type = type; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Diagnose 204 functions */ 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * For the old diag subcode 4 with simple data format we have to use real 4762306a36Sopenharmony_ci * memory. If we use subcode 6 or 7 with extended data format, we can (and 4862306a36Sopenharmony_ci * should) use vmalloc, since we need a lot of memory in that case. Currently 4962306a36Sopenharmony_ci * up to 93 pages! 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void diag204_free_buffer(void) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci vfree(diag204_buf); 5562306a36Sopenharmony_ci diag204_buf = NULL; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid *diag204_get_buffer(enum diag204_format fmt, int *pages) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (diag204_buf) { 6162306a36Sopenharmony_ci *pages = diag204_buf_pages; 6262306a36Sopenharmony_ci return diag204_buf; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci if (fmt == DIAG204_INFO_SIMPLE) { 6562306a36Sopenharmony_ci *pages = 1; 6662306a36Sopenharmony_ci } else {/* DIAG204_INFO_EXT */ 6762306a36Sopenharmony_ci *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 6862306a36Sopenharmony_ci (unsigned long)DIAG204_INFO_EXT, 0, NULL); 6962306a36Sopenharmony_ci if (*pages <= 0) 7062306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci diag204_buf = __vmalloc_node(array_size(*pages, PAGE_SIZE), 7362306a36Sopenharmony_ci PAGE_SIZE, GFP_KERNEL, NUMA_NO_NODE, 7462306a36Sopenharmony_ci __builtin_return_address(0)); 7562306a36Sopenharmony_ci if (!diag204_buf) 7662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 7762306a36Sopenharmony_ci diag204_buf_pages = *pages; 7862306a36Sopenharmony_ci return diag204_buf; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * diag204_probe() has to find out, which type of diagnose 204 implementation 8362306a36Sopenharmony_ci * we have on our machine. Currently there are three possible scanarios: 8462306a36Sopenharmony_ci * - subcode 4 + simple data format (only one page) 8562306a36Sopenharmony_ci * - subcode 4-6 + extended data format 8662306a36Sopenharmony_ci * - subcode 4-7 + extended data format 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * Subcode 5 is used to retrieve the size of the data, provided by subcodes 8962306a36Sopenharmony_ci * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 9062306a36Sopenharmony_ci * to subcode 6 it provides also information about secondary cpus. 9162306a36Sopenharmony_ci * In order to get as much information as possible, we first try 9262306a36Sopenharmony_ci * subcode 7, then 6 and if both fail, we use subcode 4. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int diag204_probe(void) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci void *buf; 9862306a36Sopenharmony_ci int pages, rc; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 10162306a36Sopenharmony_ci if (!IS_ERR(buf)) { 10262306a36Sopenharmony_ci if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 10362306a36Sopenharmony_ci (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 10462306a36Sopenharmony_ci diag204_store_sc = DIAG204_SUBC_STIB7; 10562306a36Sopenharmony_ci diag204_set_info_type(DIAG204_INFO_EXT); 10662306a36Sopenharmony_ci goto out; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 10962306a36Sopenharmony_ci (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 11062306a36Sopenharmony_ci diag204_store_sc = DIAG204_SUBC_STIB6; 11162306a36Sopenharmony_ci diag204_set_info_type(DIAG204_INFO_EXT); 11262306a36Sopenharmony_ci goto out; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci diag204_free_buffer(); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* subcodes 6 and 7 failed, now try subcode 4 */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 12062306a36Sopenharmony_ci if (IS_ERR(buf)) { 12162306a36Sopenharmony_ci rc = PTR_ERR(buf); 12262306a36Sopenharmony_ci goto fail_alloc; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 12562306a36Sopenharmony_ci (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 12662306a36Sopenharmony_ci diag204_store_sc = DIAG204_SUBC_STIB4; 12762306a36Sopenharmony_ci diag204_set_info_type(DIAG204_INFO_SIMPLE); 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci rc = -EOPNOTSUPP; 13162306a36Sopenharmony_ci goto fail_store; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ciout: 13462306a36Sopenharmony_ci rc = 0; 13562306a36Sopenharmony_cifail_store: 13662306a36Sopenharmony_ci diag204_free_buffer(); 13762306a36Sopenharmony_cifail_alloc: 13862306a36Sopenharmony_ci return rc; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciint diag204_store(void *buf, int pages) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int rc; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci rc = diag204((unsigned long)diag204_store_sc | 14662306a36Sopenharmony_ci (unsigned long)diag204_get_info_type(), pages, buf); 14762306a36Sopenharmony_ci return rc < 0 ? -EOPNOTSUPP : 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct dbfs_d204_hdr { 15162306a36Sopenharmony_ci u64 len; /* Length of d204 buffer without header */ 15262306a36Sopenharmony_ci u16 version; /* Version of header */ 15362306a36Sopenharmony_ci u8 sc; /* Used subcode */ 15462306a36Sopenharmony_ci char reserved[53]; 15562306a36Sopenharmony_ci} __attribute__ ((packed)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct dbfs_d204 { 15862306a36Sopenharmony_ci struct dbfs_d204_hdr hdr; /* 64 byte header */ 15962306a36Sopenharmony_ci char buf[]; /* d204 buffer */ 16062306a36Sopenharmony_ci} __attribute__ ((packed)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct dbfs_d204 *d204; 16562306a36Sopenharmony_ci int rc, buf_size; 16662306a36Sopenharmony_ci void *base; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 16962306a36Sopenharmony_ci base = vzalloc(buf_size); 17062306a36Sopenharmony_ci if (!base) 17162306a36Sopenharmony_ci return -ENOMEM; 17262306a36Sopenharmony_ci d204 = PTR_ALIGN(base + sizeof(d204->hdr), PAGE_SIZE) - sizeof(d204->hdr); 17362306a36Sopenharmony_ci rc = diag204_store(d204->buf, diag204_buf_pages); 17462306a36Sopenharmony_ci if (rc) { 17562306a36Sopenharmony_ci vfree(base); 17662306a36Sopenharmony_ci return rc; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci d204->hdr.version = DBFS_D204_HDR_VERSION; 17962306a36Sopenharmony_ci d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 18062306a36Sopenharmony_ci d204->hdr.sc = diag204_store_sc; 18162306a36Sopenharmony_ci *data = d204; 18262306a36Sopenharmony_ci *data_free_ptr = base; 18362306a36Sopenharmony_ci *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct hypfs_dbfs_file dbfs_file_d204 = { 18862306a36Sopenharmony_ci .name = "diag_204", 18962306a36Sopenharmony_ci .data_create = dbfs_d204_create, 19062306a36Sopenharmony_ci .data_free = vfree, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci__init int hypfs_diag_init(void) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int rc; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (diag204_probe()) { 19862306a36Sopenharmony_ci pr_info("The hardware system does not support hypfs\n"); 19962306a36Sopenharmony_ci return -ENODATA; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (diag204_get_info_type() == DIAG204_INFO_EXT) 20362306a36Sopenharmony_ci hypfs_dbfs_create_file(&dbfs_file_d204); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci rc = hypfs_diag_fs_init(); 20662306a36Sopenharmony_ci if (rc) { 20762306a36Sopenharmony_ci pr_err("The hardware system does not provide all functions required by hypfs\n"); 20862306a36Sopenharmony_ci debugfs_remove(dbfs_d204_file); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci return rc; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid hypfs_diag_exit(void) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci debugfs_remove(dbfs_d204_file); 21662306a36Sopenharmony_ci hypfs_diag_fs_exit(); 21762306a36Sopenharmony_ci diag204_free_buffer(); 21862306a36Sopenharmony_ci hypfs_dbfs_remove_file(&dbfs_file_d204); 21962306a36Sopenharmony_ci} 220