1 /* 2 * drivers/s390/cio/blacklist.c 3 * S/390 common I/O routines -- blacklisting of specific devices 4 * 5 * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, 6 * IBM Corporation 7 * Author(s): Ingo Adlung (adlung@de.ibm.com) 8 * Cornelia Huck (cornelia.huck@de.ibm.com) 9 * Arnd Bergmann (arndb@de.ibm.com) 10 */ 11 12 #include <linux/init.h> 13 #include <linux/vmalloc.h> 14 #include <linux/slab.h> 15 #include <linux/proc_fs.h> 16 #include <linux/seq_file.h> 17 #include <linux/ctype.h> 18 #include <linux/device.h> 19 20 #include <asm/cio.h> 21 #include <asm/uaccess.h> 22 23 #include "blacklist.h" 24 #include "cio.h" 25 #include "cio_debug.h" 26 #include "css.h" 27 #include "device.h" 28 29 /* 30 * "Blacklisting" of certain devices: 31 * Device numbers given in the commandline as cio_ignore=... won't be known 32 * to Linux. 33 * 34 * These can be single devices or ranges of devices 35 */ 36 37 /* 65536 bits for each set to indicate if a devno is blacklisted or not */ 38 #define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ 39 (8*sizeof(long))) 40 static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS]; 41 typedef enum {add, free} range_action; 42 43 /* 44 * Function: blacklist_range 45 * (Un-)blacklist the devices from-to 46 */ 47 static int blacklist_range(range_action action, unsigned int from_ssid, 48 unsigned int to_ssid, unsigned int from, 49 unsigned int to, int msgtrigger) 50 { 51 if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) { 52 if (msgtrigger) 53 printk(KERN_WARNING "cio: Invalid cio_ignore range " 54 "0.%x.%04x-0.%x.%04x\n", from_ssid, from, 55 to_ssid, to); 56 return 1; 57 } 58 59 while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) && 60 (from <= to))) { 61 if (action == add) 62 set_bit(from, bl_dev[from_ssid]); 63 else 64 clear_bit(from, bl_dev[from_ssid]); 65 from++; 66 if (from > __MAX_SUBCHANNEL) { 67 from_ssid++; 68 from = 0; 69 } 70 } 71 72 return 0; 73 } 74 75 static int pure_hex(char **cp, unsigned int *val, int min_digit, 76 int max_digit, int max_val) 77 { 78 int diff; 79 unsigned int value; 80 81 diff = 0; 82 *val = 0; 83 84 while (isxdigit(**cp) && (diff <= max_digit)) { 85 86 if (isdigit(**cp)) 87 value = **cp - '0'; 88 else 89 value = tolower(**cp) - 'a' + 10; 90 *val = *val * 16 + value; 91 (*cp)++; 92 diff++; 93 } 94 95 if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) 96 return 1; 97 98 return 0; 99 } 100 101 static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid, 102 unsigned int *devno, int msgtrigger) 103 { 104 char *str_work; 105 int val, rc, ret; 106 107 rc = 1; 108 109 if (*str == '\0') 110 goto out; 111 112 /* old style */ 113 str_work = str; 114 val = simple_strtoul(str, &str_work, 16); 115 116 if (*str_work == '\0') { 117 if (val <= __MAX_SUBCHANNEL) { 118 *devno = val; 119 *ssid = 0; 120 *cssid = 0; 121 rc = 0; 122 } 123 goto out; 124 } 125 126 /* new style */ 127 str_work = str; 128 ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); 129 if (ret || (str_work[0] != '.')) 130 goto out; 131 str_work++; 132 ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); 133 if (ret || (str_work[0] != '.')) 134 goto out; 135 str_work++; 136 ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); 137 if (ret || (str_work[0] != '\0')) 138 goto out; 139 140 rc = 0; 141 out: 142 if (rc && msgtrigger) 143 printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n", 144 str); 145 146 return rc; 147 } 148 149 static int blacklist_parse_parameters(char *str, range_action action, 150 int msgtrigger) 151 { 152 unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; 153 int rc, totalrc; 154 char *parm; 155 range_action ra; 156 157 totalrc = 0; 158 159 while ((parm = strsep(&str, ","))) { 160 rc = 0; 161 ra = action; 162 if (*parm == '!') { 163 if (ra == add) 164 ra = free; 165 else 166 ra = add; 167 parm++; 168 } 169 if (strcmp(parm, "all") == 0) { 170 from_cssid = 0; 171 from_ssid = 0; 172 from = 0; 173 to_cssid = __MAX_CSSID; 174 to_ssid = __MAX_SSID; 175 to = __MAX_SUBCHANNEL; 176 } else { 177 rc = parse_busid(strsep(&parm, "-"), &from_cssid, 178 &from_ssid, &from, msgtrigger); 179 if (!rc) { 180 if (parm != NULL) 181 rc = parse_busid(parm, &to_cssid, 182 &to_ssid, &to, 183 msgtrigger); 184 else { 185 to_cssid = from_cssid; 186 to_ssid = from_ssid; 187 to = from; 188 } 189 } 190 } 191 if (!rc) { 192 rc = blacklist_range(ra, from_ssid, to_ssid, from, to, 193 msgtrigger); 194 if (rc) 195 totalrc = -EINVAL; 196 } else 197 totalrc = -EINVAL; 198 } 199 200 return totalrc; 201 } 202 203 static int __init 204 blacklist_setup (char *str) 205 { 206 CIO_MSG_EVENT(6, "Reading blacklist parameters\n"); 207 if (blacklist_parse_parameters(str, add, 1)) 208 return 0; 209 return 1; 210 } 211 212 __setup ("cio_ignore=", blacklist_setup); 213 214 /* Checking if devices are blacklisted */ 215 216 /* 217 * Function: is_blacklisted 218 * Returns 1 if the given devicenumber can be found in the blacklist, 219 * otherwise 0. 220 * Used by validate_subchannel() 221 */ 222 int 223 is_blacklisted (int ssid, int devno) 224 { 225 return test_bit (devno, bl_dev[ssid]); 226 } 227 228 #ifdef CONFIG_PROC_FS 229 /* 230 * Function: blacklist_parse_proc_parameters 231 * parse the stuff which is piped to /proc/cio_ignore 232 */ 233 static int blacklist_parse_proc_parameters(char *buf) 234 { 235 int rc; 236 char *parm; 237 238 parm = strsep(&buf, " "); 239 240 if (strcmp("free", parm) == 0) 241 rc = blacklist_parse_parameters(buf, free, 0); 242 else if (strcmp("add", parm) == 0) 243 rc = blacklist_parse_parameters(buf, add, 0); 244 else if (strcmp("purge", parm) == 0) 245 return ccw_purge_blacklisted(); 246 else 247 return -EINVAL; 248 249 css_schedule_reprobe(); 250 251 return rc; 252 } 253 254 /* Iterator struct for all devices. */ 255 struct ccwdev_iter { 256 int devno; 257 int ssid; 258 int in_range; 259 }; 260 261 static void * 262 cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) 263 { 264 struct ccwdev_iter *iter; 265 266 if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) 267 return NULL; 268 iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL); 269 if (!iter) 270 return ERR_PTR(-ENOMEM); 271 iter->ssid = *offset / (__MAX_SUBCHANNEL + 1); 272 iter->devno = *offset % (__MAX_SUBCHANNEL + 1); 273 return iter; 274 } 275 276 static void 277 cio_ignore_proc_seq_stop(struct seq_file *s, void *it) 278 { 279 if (!IS_ERR(it)) 280 kfree(it); 281 } 282 283 static void * 284 cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset) 285 { 286 struct ccwdev_iter *iter; 287 288 if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) 289 return NULL; 290 iter = it; 291 if (iter->devno == __MAX_SUBCHANNEL) { 292 iter->devno = 0; 293 iter->ssid++; 294 if (iter->ssid > __MAX_SSID) 295 return NULL; 296 } else 297 iter->devno++; 298 (*offset)++; 299 return iter; 300 } 301 302 static int 303 cio_ignore_proc_seq_show(struct seq_file *s, void *it) 304 { 305 struct ccwdev_iter *iter; 306 307 iter = it; 308 if (!is_blacklisted(iter->ssid, iter->devno)) 309 /* Not blacklisted, nothing to output. */ 310 return 0; 311 if (!iter->in_range) { 312 /* First device in range. */ 313 if ((iter->devno == __MAX_SUBCHANNEL) || 314 !is_blacklisted(iter->ssid, iter->devno + 1)) 315 /* Singular device. */ 316 return seq_printf(s, "0.%x.%04x\n", 317 iter->ssid, iter->devno); 318 iter->in_range = 1; 319 return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno); 320 } 321 if ((iter->devno == __MAX_SUBCHANNEL) || 322 !is_blacklisted(iter->ssid, iter->devno + 1)) { 323 /* Last device in range. */ 324 iter->in_range = 0; 325 return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno); 326 } 327 return 0; 328 } 329 330 static ssize_t 331 cio_ignore_write(struct file *file, const char __user *user_buf, 332 size_t user_len, loff_t *offset) 333 { 334 char *buf; 335 size_t i; 336 ssize_t rc, ret; 337 338 if (*offset) 339 return -EINVAL; 340 if (user_len > 65536) 341 user_len = 65536; 342 buf = vmalloc (user_len + 1); /* maybe better use the stack? */ 343 if (buf == NULL) 344 return -ENOMEM; 345 memset(buf, 0, user_len + 1); 346 347 if (strncpy_from_user (buf, user_buf, user_len) < 0) { 348 rc = -EFAULT; 349 goto out_free; 350 } 351 352 i = user_len - 1; 353 while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) { 354 buf[i] = '\0'; 355 i--; 356 } 357 ret = blacklist_parse_proc_parameters(buf); 358 if (ret) 359 rc = ret; 360 else 361 rc = user_len; 362 363 out_free: 364 vfree (buf); 365 return rc; 366 } 367 368 static const struct seq_operations cio_ignore_proc_seq_ops = { 369 .start = cio_ignore_proc_seq_start, 370 .stop = cio_ignore_proc_seq_stop, 371 .next = cio_ignore_proc_seq_next, 372 .show = cio_ignore_proc_seq_show, 373 }; 374 375 static int 376 cio_ignore_proc_open(struct inode *inode, struct file *file) 377 { 378 return seq_open(file, &cio_ignore_proc_seq_ops); 379 } 380 381 static const struct file_operations cio_ignore_proc_fops = { 382 .open = cio_ignore_proc_open, 383 .read = seq_read, 384 .llseek = seq_lseek, 385 .release = seq_release, 386 .write = cio_ignore_write, 387 }; 388 389 static int 390 cio_ignore_proc_init (void) 391 { 392 struct proc_dir_entry *entry; 393 394 entry = proc_create("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, NULL, 395 &cio_ignore_proc_fops); 396 if (!entry) 397 return -ENOENT; 398 return 0; 399 } 400 401 __initcall (cio_ignore_proc_init); 402 403 #endif /* CONFIG_PROC_FS */ 404