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