1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Hypervisor filesystem for Linux on s390. Diag 204 and 224 4 * implementation. 5 * 6 * Copyright IBM Corp. 2006, 2008 7 * Author(s): Michael Holzheu <holzheu@de.ibm.com> 8 */ 9 10 #define KMSG_COMPONENT "hypfs" 11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 12 13 #include <linux/types.h> 14 #include <linux/errno.h> 15 #include <linux/slab.h> 16 #include <linux/string.h> 17 #include <linux/vmalloc.h> 18 #include <linux/mm.h> 19 #include <asm/diag.h> 20 #include <asm/ebcdic.h> 21 #include "hypfs_diag.h" 22 #include "hypfs.h" 23 24 #define DBFS_D204_HDR_VERSION 0 25 26 static enum diag204_sc diag204_store_sc; /* used subcode for store */ 27 static enum diag204_format diag204_info_type; /* used diag 204 data format */ 28 29 static void *diag204_buf; /* 4K aligned buffer for diag204 data */ 30 static int diag204_buf_pages; /* number of pages for diag204 data */ 31 32 static struct dentry *dbfs_d204_file; 33 34 enum diag204_format diag204_get_info_type(void) 35 { 36 return diag204_info_type; 37 } 38 39 static void diag204_set_info_type(enum diag204_format type) 40 { 41 diag204_info_type = type; 42 } 43 44 /* Diagnose 204 functions */ 45 /* 46 * For the old diag subcode 4 with simple data format we have to use real 47 * memory. If we use subcode 6 or 7 with extended data format, we can (and 48 * should) use vmalloc, since we need a lot of memory in that case. Currently 49 * up to 93 pages! 50 */ 51 52 static void diag204_free_buffer(void) 53 { 54 vfree(diag204_buf); 55 diag204_buf = NULL; 56 } 57 58 void *diag204_get_buffer(enum diag204_format fmt, int *pages) 59 { 60 if (diag204_buf) { 61 *pages = diag204_buf_pages; 62 return diag204_buf; 63 } 64 if (fmt == DIAG204_INFO_SIMPLE) { 65 *pages = 1; 66 } else {/* DIAG204_INFO_EXT */ 67 *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 68 (unsigned long)DIAG204_INFO_EXT, 0, NULL); 69 if (*pages <= 0) 70 return ERR_PTR(-EOPNOTSUPP); 71 } 72 diag204_buf = __vmalloc_node(array_size(*pages, PAGE_SIZE), 73 PAGE_SIZE, GFP_KERNEL, NUMA_NO_NODE, 74 __builtin_return_address(0)); 75 if (!diag204_buf) 76 return ERR_PTR(-ENOMEM); 77 diag204_buf_pages = *pages; 78 return diag204_buf; 79 } 80 81 /* 82 * diag204_probe() has to find out, which type of diagnose 204 implementation 83 * we have on our machine. Currently there are three possible scanarios: 84 * - subcode 4 + simple data format (only one page) 85 * - subcode 4-6 + extended data format 86 * - subcode 4-7 + extended data format 87 * 88 * Subcode 5 is used to retrieve the size of the data, provided by subcodes 89 * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 90 * to subcode 6 it provides also information about secondary cpus. 91 * In order to get as much information as possible, we first try 92 * subcode 7, then 6 and if both fail, we use subcode 4. 93 */ 94 95 static int diag204_probe(void) 96 { 97 void *buf; 98 int pages, rc; 99 100 buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 101 if (!IS_ERR(buf)) { 102 if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 103 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 104 diag204_store_sc = DIAG204_SUBC_STIB7; 105 diag204_set_info_type(DIAG204_INFO_EXT); 106 goto out; 107 } 108 if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 109 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 110 diag204_store_sc = DIAG204_SUBC_STIB6; 111 diag204_set_info_type(DIAG204_INFO_EXT); 112 goto out; 113 } 114 diag204_free_buffer(); 115 } 116 117 /* subcodes 6 and 7 failed, now try subcode 4 */ 118 119 buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 120 if (IS_ERR(buf)) { 121 rc = PTR_ERR(buf); 122 goto fail_alloc; 123 } 124 if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 125 (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 126 diag204_store_sc = DIAG204_SUBC_STIB4; 127 diag204_set_info_type(DIAG204_INFO_SIMPLE); 128 goto out; 129 } else { 130 rc = -EOPNOTSUPP; 131 goto fail_store; 132 } 133 out: 134 rc = 0; 135 fail_store: 136 diag204_free_buffer(); 137 fail_alloc: 138 return rc; 139 } 140 141 int diag204_store(void *buf, int pages) 142 { 143 int rc; 144 145 rc = diag204((unsigned long)diag204_store_sc | 146 (unsigned long)diag204_get_info_type(), pages, buf); 147 return rc < 0 ? -EOPNOTSUPP : 0; 148 } 149 150 struct dbfs_d204_hdr { 151 u64 len; /* Length of d204 buffer without header */ 152 u16 version; /* Version of header */ 153 u8 sc; /* Used subcode */ 154 char reserved[53]; 155 } __attribute__ ((packed)); 156 157 struct dbfs_d204 { 158 struct dbfs_d204_hdr hdr; /* 64 byte header */ 159 char buf[]; /* d204 buffer */ 160 } __attribute__ ((packed)); 161 162 static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 163 { 164 struct dbfs_d204 *d204; 165 int rc, buf_size; 166 void *base; 167 168 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 169 base = vzalloc(buf_size); 170 if (!base) 171 return -ENOMEM; 172 d204 = PTR_ALIGN(base + sizeof(d204->hdr), PAGE_SIZE) - sizeof(d204->hdr); 173 rc = diag204_store(d204->buf, diag204_buf_pages); 174 if (rc) { 175 vfree(base); 176 return rc; 177 } 178 d204->hdr.version = DBFS_D204_HDR_VERSION; 179 d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 180 d204->hdr.sc = diag204_store_sc; 181 *data = d204; 182 *data_free_ptr = base; 183 *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 184 return 0; 185 } 186 187 static struct hypfs_dbfs_file dbfs_file_d204 = { 188 .name = "diag_204", 189 .data_create = dbfs_d204_create, 190 .data_free = vfree, 191 }; 192 193 __init int hypfs_diag_init(void) 194 { 195 int rc; 196 197 if (diag204_probe()) { 198 pr_info("The hardware system does not support hypfs\n"); 199 return -ENODATA; 200 } 201 202 if (diag204_get_info_type() == DIAG204_INFO_EXT) 203 hypfs_dbfs_create_file(&dbfs_file_d204); 204 205 rc = hypfs_diag_fs_init(); 206 if (rc) { 207 pr_err("The hardware system does not provide all functions required by hypfs\n"); 208 debugfs_remove(dbfs_d204_file); 209 } 210 return rc; 211 } 212 213 void hypfs_diag_exit(void) 214 { 215 debugfs_remove(dbfs_d204_file); 216 hypfs_diag_fs_exit(); 217 diag204_free_buffer(); 218 hypfs_dbfs_remove_file(&dbfs_file_d204); 219 } 220