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