1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 224bbb1faSMichael Holzheu /* 324bbb1faSMichael Holzheu * Hypervisor filesystem for Linux on s390. Diag 204 and 224 424bbb1faSMichael Holzheu * implementation. 524bbb1faSMichael Holzheu * 6f55495baSMichael Holzheu * Copyright IBM Corp. 2006, 2008 724bbb1faSMichael Holzheu * Author(s): Michael Holzheu <holzheu@de.ibm.com> 824bbb1faSMichael Holzheu */ 924bbb1faSMichael Holzheu 10f55495baSMichael Holzheu #define KMSG_COMPONENT "hypfs" 11f55495baSMichael Holzheu #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 12f55495baSMichael Holzheu 1324bbb1faSMichael Holzheu #include <linux/types.h> 1424bbb1faSMichael Holzheu #include <linux/errno.h> 1533b26d79SHeiko Carstens #include <linux/slab.h> 1624bbb1faSMichael Holzheu #include <linux/string.h> 1724bbb1faSMichael Holzheu #include <linux/vmalloc.h> 1857b28f66SMichael Holzheu #include <linux/mm.h> 191ec2772eSMartin Schwidefsky #include <asm/diag.h> 2024bbb1faSMichael Holzheu #include <asm/ebcdic.h> 213325b4d8SHeiko Carstens #include "hypfs_diag.h" 2224bbb1faSMichael Holzheu #include "hypfs.h" 2324bbb1faSMichael Holzheu 2457b28f66SMichael Holzheu #define DBFS_D204_HDR_VERSION 0 2557b28f66SMichael Holzheu 2624bbb1faSMichael Holzheu static enum diag204_sc diag204_store_sc; /* used subcode for store */ 2724bbb1faSMichael Holzheu static enum diag204_format diag204_info_type; /* used diag 204 data format */ 2824bbb1faSMichael Holzheu 2924bbb1faSMichael Holzheu static void *diag204_buf; /* 4K aligned buffer for diag204 data */ 3024bbb1faSMichael Holzheu static int diag204_buf_pages; /* number of pages for diag204 data */ 3124bbb1faSMichael Holzheu 3257b28f66SMichael Holzheu static struct dentry *dbfs_d204_file; 3357b28f66SMichael Holzheu 343325b4d8SHeiko Carstens enum diag204_format diag204_get_info_type(void) 3524bbb1faSMichael Holzheu { 363325b4d8SHeiko Carstens return diag204_info_type; 3724bbb1faSMichael Holzheu } 3824bbb1faSMichael Holzheu 393325b4d8SHeiko Carstens static void diag204_set_info_type(enum diag204_format type) 4024bbb1faSMichael Holzheu { 413325b4d8SHeiko Carstens diag204_info_type = type; 4224bbb1faSMichael Holzheu } 4324bbb1faSMichael Holzheu 4424bbb1faSMichael Holzheu /* Diagnose 204 functions */ 4524bbb1faSMichael Holzheu /* 4624bbb1faSMichael Holzheu * For the old diag subcode 4 with simple data format we have to use real 4724bbb1faSMichael Holzheu * memory. If we use subcode 6 or 7 with extended data format, we can (and 4824bbb1faSMichael Holzheu * should) use vmalloc, since we need a lot of memory in that case. Currently 4924bbb1faSMichael Holzheu * up to 93 pages! 5024bbb1faSMichael Holzheu */ 5124bbb1faSMichael Holzheu 5224bbb1faSMichael Holzheu static void diag204_free_buffer(void) 5324bbb1faSMichael Holzheu { 5483f95671SHeiko Carstens vfree(diag204_buf); 5524bbb1faSMichael Holzheu diag204_buf = NULL; 5624bbb1faSMichael Holzheu } 5724bbb1faSMichael Holzheu 583325b4d8SHeiko Carstens void *diag204_get_buffer(enum diag204_format fmt, int *pages) 5924bbb1faSMichael Holzheu { 6024bbb1faSMichael Holzheu if (diag204_buf) { 6124bbb1faSMichael Holzheu *pages = diag204_buf_pages; 6224bbb1faSMichael Holzheu return diag204_buf; 6324bbb1faSMichael Holzheu } 64e65f30e0SJanosch Frank if (fmt == DIAG204_INFO_SIMPLE) { 6524bbb1faSMichael Holzheu *pages = 1; 66e65f30e0SJanosch Frank } else {/* DIAG204_INFO_EXT */ 67e65f30e0SJanosch Frank *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 68e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_EXT, 0, NULL); 6924bbb1faSMichael Holzheu if (*pages <= 0) 70*5216d853SSven Schnelle return ERR_PTR(-EOPNOTSUPP); 7124bbb1faSMichael Holzheu } 7283f95671SHeiko Carstens diag204_buf = __vmalloc_node(array_size(*pages, PAGE_SIZE), 7383f95671SHeiko Carstens PAGE_SIZE, GFP_KERNEL, NUMA_NO_NODE, 7483f95671SHeiko Carstens __builtin_return_address(0)); 7583f95671SHeiko Carstens if (!diag204_buf) 7683f95671SHeiko Carstens return ERR_PTR(-ENOMEM); 7783f95671SHeiko Carstens diag204_buf_pages = *pages; 7883f95671SHeiko Carstens return diag204_buf; 7924bbb1faSMichael Holzheu } 8024bbb1faSMichael Holzheu 8124bbb1faSMichael Holzheu /* 8224bbb1faSMichael Holzheu * diag204_probe() has to find out, which type of diagnose 204 implementation 8324bbb1faSMichael Holzheu * we have on our machine. Currently there are three possible scanarios: 8424bbb1faSMichael Holzheu * - subcode 4 + simple data format (only one page) 8524bbb1faSMichael Holzheu * - subcode 4-6 + extended data format 8624bbb1faSMichael Holzheu * - subcode 4-7 + extended data format 8724bbb1faSMichael Holzheu * 8824bbb1faSMichael Holzheu * Subcode 5 is used to retrieve the size of the data, provided by subcodes 8924bbb1faSMichael Holzheu * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 9024bbb1faSMichael Holzheu * to subcode 6 it provides also information about secondary cpus. 9124bbb1faSMichael Holzheu * In order to get as much information as possible, we first try 9224bbb1faSMichael Holzheu * subcode 7, then 6 and if both fail, we use subcode 4. 9324bbb1faSMichael Holzheu */ 9424bbb1faSMichael Holzheu 9524bbb1faSMichael Holzheu static int diag204_probe(void) 9624bbb1faSMichael Holzheu { 9724bbb1faSMichael Holzheu void *buf; 9824bbb1faSMichael Holzheu int pages, rc; 9924bbb1faSMichael Holzheu 100e65f30e0SJanosch Frank buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 10124bbb1faSMichael Holzheu if (!IS_ERR(buf)) { 102e65f30e0SJanosch Frank if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 103e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 104e65f30e0SJanosch Frank diag204_store_sc = DIAG204_SUBC_STIB7; 1053325b4d8SHeiko Carstens diag204_set_info_type(DIAG204_INFO_EXT); 10624bbb1faSMichael Holzheu goto out; 10724bbb1faSMichael Holzheu } 108e65f30e0SJanosch Frank if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 109e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 110e65f30e0SJanosch Frank diag204_store_sc = DIAG204_SUBC_STIB6; 1113325b4d8SHeiko Carstens diag204_set_info_type(DIAG204_INFO_EXT); 11224bbb1faSMichael Holzheu goto out; 11324bbb1faSMichael Holzheu } 11424bbb1faSMichael Holzheu diag204_free_buffer(); 11524bbb1faSMichael Holzheu } 11624bbb1faSMichael Holzheu 11724bbb1faSMichael Holzheu /* subcodes 6 and 7 failed, now try subcode 4 */ 11824bbb1faSMichael Holzheu 119e65f30e0SJanosch Frank buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 12024bbb1faSMichael Holzheu if (IS_ERR(buf)) { 12124bbb1faSMichael Holzheu rc = PTR_ERR(buf); 12224bbb1faSMichael Holzheu goto fail_alloc; 12324bbb1faSMichael Holzheu } 124e65f30e0SJanosch Frank if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 125e65f30e0SJanosch Frank (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 126e65f30e0SJanosch Frank diag204_store_sc = DIAG204_SUBC_STIB4; 1273325b4d8SHeiko Carstens diag204_set_info_type(DIAG204_INFO_SIMPLE); 12824bbb1faSMichael Holzheu goto out; 12924bbb1faSMichael Holzheu } else { 130*5216d853SSven Schnelle rc = -EOPNOTSUPP; 13124bbb1faSMichael Holzheu goto fail_store; 13224bbb1faSMichael Holzheu } 13324bbb1faSMichael Holzheu out: 13424bbb1faSMichael Holzheu rc = 0; 13524bbb1faSMichael Holzheu fail_store: 13624bbb1faSMichael Holzheu diag204_free_buffer(); 13724bbb1faSMichael Holzheu fail_alloc: 13824bbb1faSMichael Holzheu return rc; 13924bbb1faSMichael Holzheu } 14024bbb1faSMichael Holzheu 1413325b4d8SHeiko Carstens int diag204_store(void *buf, int pages) 14257b28f66SMichael Holzheu { 14357b28f66SMichael Holzheu int rc; 14457b28f66SMichael Holzheu 14557b28f66SMichael Holzheu rc = diag204((unsigned long)diag204_store_sc | 1463325b4d8SHeiko Carstens (unsigned long)diag204_get_info_type(), pages, buf); 147*5216d853SSven Schnelle return rc < 0 ? -EOPNOTSUPP : 0; 14857b28f66SMichael Holzheu } 14957b28f66SMichael Holzheu 15057b28f66SMichael Holzheu struct dbfs_d204_hdr { 15157b28f66SMichael Holzheu u64 len; /* Length of d204 buffer without header */ 15257b28f66SMichael Holzheu u16 version; /* Version of header */ 15357b28f66SMichael Holzheu u8 sc; /* Used subcode */ 15457b28f66SMichael Holzheu char reserved[53]; 15557b28f66SMichael Holzheu } __attribute__ ((packed)); 15657b28f66SMichael Holzheu 15757b28f66SMichael Holzheu struct dbfs_d204 { 15857b28f66SMichael Holzheu struct dbfs_d204_hdr hdr; /* 64 byte header */ 15957b28f66SMichael Holzheu char buf[]; /* d204 buffer */ 16057b28f66SMichael Holzheu } __attribute__ ((packed)); 16157b28f66SMichael Holzheu 1622fcb3686SMichael Holzheu static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 16357b28f66SMichael Holzheu { 16457b28f66SMichael Holzheu struct dbfs_d204 *d204; 16557b28f66SMichael Holzheu int rc, buf_size; 1662fcb3686SMichael Holzheu void *base; 16757b28f66SMichael Holzheu 16857b28f66SMichael Holzheu buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 169dc8a5c99SJoe Perches base = vzalloc(buf_size); 1702fcb3686SMichael Holzheu if (!base) 1712fcb3686SMichael Holzheu return -ENOMEM; 172b7857accSHeiko Carstens d204 = PTR_ALIGN(base + sizeof(d204->hdr), PAGE_SIZE) - sizeof(d204->hdr); 1733325b4d8SHeiko Carstens rc = diag204_store(d204->buf, diag204_buf_pages); 1742fcb3686SMichael Holzheu if (rc) { 1752fcb3686SMichael Holzheu vfree(base); 1762fcb3686SMichael Holzheu return rc; 17757b28f66SMichael Holzheu } 17857b28f66SMichael Holzheu d204->hdr.version = DBFS_D204_HDR_VERSION; 17957b28f66SMichael Holzheu d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 18057b28f66SMichael Holzheu d204->hdr.sc = diag204_store_sc; 1812fcb3686SMichael Holzheu *data = d204; 1822fcb3686SMichael Holzheu *data_free_ptr = base; 1832fcb3686SMichael Holzheu *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 18457b28f66SMichael Holzheu return 0; 18557b28f66SMichael Holzheu } 18657b28f66SMichael Holzheu 1872fcb3686SMichael Holzheu static struct hypfs_dbfs_file dbfs_file_d204 = { 1882fcb3686SMichael Holzheu .name = "diag_204", 1892fcb3686SMichael Holzheu .data_create = dbfs_d204_create, 1902fcb3686SMichael Holzheu .data_free = vfree, 19157b28f66SMichael Holzheu }; 19257b28f66SMichael Holzheu 19324bbb1faSMichael Holzheu __init int hypfs_diag_init(void) 19424bbb1faSMichael Holzheu { 19524bbb1faSMichael Holzheu int rc; 19624bbb1faSMichael Holzheu 19724bbb1faSMichael Holzheu if (diag204_probe()) { 1987b6670b0SJuergen Gross pr_info("The hardware system does not support hypfs\n"); 19924bbb1faSMichael Holzheu return -ENODATA; 20024bbb1faSMichael Holzheu } 201f36108c4SGreg Kroah-Hartman 2023325b4d8SHeiko Carstens if (diag204_get_info_type() == DIAG204_INFO_EXT) 203f36108c4SGreg Kroah-Hartman hypfs_dbfs_create_file(&dbfs_file_d204); 204f36108c4SGreg Kroah-Hartman 2053325b4d8SHeiko Carstens rc = hypfs_diag_fs_init(); 2063c8ebca0SMichael Holzheu if (rc) { 2073325b4d8SHeiko Carstens pr_err("The hardware system does not provide all functions required by hypfs\n"); 2083c8ebca0SMichael Holzheu debugfs_remove(dbfs_d204_file); 2093325b4d8SHeiko Carstens } 2103c8ebca0SMichael Holzheu return rc; 2113c8ebca0SMichael Holzheu } 21224bbb1faSMichael Holzheu 2131375fc1fSHeiko Carstens void hypfs_diag_exit(void) 21424bbb1faSMichael Holzheu { 21557b28f66SMichael Holzheu debugfs_remove(dbfs_d204_file); 2163325b4d8SHeiko Carstens hypfs_diag_fs_exit(); 21724bbb1faSMichael Holzheu diag204_free_buffer(); 2182fcb3686SMichael Holzheu hypfs_dbfs_remove_file(&dbfs_file_d204); 21924bbb1faSMichael Holzheu } 220