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 unsigned long subcode; 144 int rc; 145 146 subcode = diag204_get_info_type(); 147 subcode |= diag204_store_sc; 148 if (diag204_has_bif()) 149 subcode |= DIAG204_BIF_BIT; 150 while (1) { 151 rc = diag204(subcode, pages, buf); 152 if (rc != -EBUSY) 153 break; 154 if (signal_pending(current)) 155 return -ERESTARTSYS; 156 schedule_timeout_interruptible(DIAG204_BUSY_WAIT); 157 } 158 return rc < 0 ? rc : 0; 159 } 160 161 struct dbfs_d204_hdr { 162 u64 len; /* Length of d204 buffer without header */ 163 u16 version; /* Version of header */ 164 u8 sc; /* Used subcode */ 165 char reserved[53]; 166 } __attribute__ ((packed)); 167 168 struct dbfs_d204 { 169 struct dbfs_d204_hdr hdr; /* 64 byte header */ 170 char buf[]; /* d204 buffer */ 171 } __attribute__ ((packed)); 172 173 static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 174 { 175 struct dbfs_d204 *d204; 176 int rc, buf_size; 177 void *base; 178 179 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 180 base = vzalloc(buf_size); 181 if (!base) 182 return -ENOMEM; 183 d204 = PTR_ALIGN(base + sizeof(d204->hdr), PAGE_SIZE) - sizeof(d204->hdr); 184 rc = diag204_store(d204->buf, diag204_buf_pages); 185 if (rc) { 186 vfree(base); 187 return rc; 188 } 189 d204->hdr.version = DBFS_D204_HDR_VERSION; 190 d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 191 d204->hdr.sc = diag204_store_sc; 192 *data = d204; 193 *data_free_ptr = base; 194 *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 195 return 0; 196 } 197 198 static struct hypfs_dbfs_file dbfs_file_d204 = { 199 .name = "diag_204", 200 .data_create = dbfs_d204_create, 201 .data_free = vfree, 202 }; 203 204 __init int hypfs_diag_init(void) 205 { 206 int rc; 207 208 if (diag204_probe()) { 209 pr_info("The hardware system does not support hypfs\n"); 210 return -ENODATA; 211 } 212 213 if (diag204_get_info_type() == DIAG204_INFO_EXT) 214 hypfs_dbfs_create_file(&dbfs_file_d204); 215 216 rc = hypfs_diag_fs_init(); 217 if (rc) { 218 pr_err("The hardware system does not provide all functions required by hypfs\n"); 219 debugfs_remove(dbfs_d204_file); 220 } 221 return rc; 222 } 223 224 void hypfs_diag_exit(void) 225 { 226 debugfs_remove(dbfs_d204_file); 227 hypfs_diag_fs_exit(); 228 diag204_free_buffer(); 229 hypfs_dbfs_remove_file(&dbfs_file_d204); 230 } 231
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.