xref: /openbmc/linux/drivers/s390/cio/blacklist.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1 /*
2  *  drivers/s390/cio/blacklist.c
3  *   S/390 common I/O routines -- blacklisting of specific devices
4  *   $Revision: 1.33 $
5  *
6  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
7  *			      IBM Corporation
8  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
9  *		 Cornelia Huck (cohuck@de.ibm.com)
10  *		 Arnd Bergmann (arndb@de.ibm.com)
11  */
12 
13 #include <linux/config.h>
14 #include <linux/init.h>
15 #include <linux/vmalloc.h>
16 #include <linux/slab.h>
17 #include <linux/proc_fs.h>
18 #include <linux/ctype.h>
19 #include <linux/device.h>
20 
21 #include <asm/cio.h>
22 #include <asm/uaccess.h>
23 
24 #include "blacklist.h"
25 #include "cio.h"
26 #include "cio_debug.h"
27 #include "css.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 to indicate if a devno is blacklisted or not */
38 #define __BL_DEV_WORDS (__MAX_SUBCHANNELS + (8*sizeof(long) - 1) / \
39 			 (8*sizeof(long)))
40 static unsigned long bl_dev[__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 inline void
48 blacklist_range (range_action action, unsigned int from, unsigned int to)
49 {
50 	if (!to)
51 		to = from;
52 
53 	if (from > to || to > __MAX_SUBCHANNELS) {
54 		printk (KERN_WARNING "Invalid blacklist range "
55 			"0x%04x to 0x%04x, skipping\n", from, to);
56 		return;
57 	}
58 	for (; from <= to; from++) {
59 		if (action == add)
60 			set_bit (from, bl_dev);
61 		else
62 			clear_bit (from, bl_dev);
63 	}
64 }
65 
66 /*
67  * Function: blacklist_busid
68  * Get devno/busid from given string.
69  * Shamelessly grabbed from dasd_devmap.c.
70  */
71 static inline int
72 blacklist_busid(char **str, int *id0, int *id1, int *devno)
73 {
74 	int val, old_style;
75 	char *sav;
76 
77 	sav = *str;
78 
79 	/* check for leading '0x' */
80 	old_style = 0;
81 	if ((*str)[0] == '0' && (*str)[1] == 'x') {
82 		*str += 2;
83 		old_style = 1;
84 	}
85 	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
86 		goto confused;
87 	val = simple_strtoul(*str, str, 16);
88 	if (old_style || (*str)[0] != '.') {
89 		*id0 = *id1 = 0;
90 		if (val < 0 || val > 0xffff)
91 			goto confused;
92 		*devno = val;
93 		if ((*str)[0] != ',' && (*str)[0] != '-' &&
94 		    (*str)[0] != '\n' && (*str)[0] != '\0')
95 			goto confused;
96 		return 0;
97 	}
98 	/* New style x.y.z busid */
99 	if (val < 0 || val > 0xff)
100 		goto confused;
101 	*id0 = val;
102 	(*str)++;
103 	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
104 		goto confused;
105 	val = simple_strtoul(*str, str, 16);
106 	if (val < 0 || val > 0xff || (*str)++[0] != '.')
107 		goto confused;
108 	*id1 = val;
109 	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
110 		goto confused;
111 	val = simple_strtoul(*str, str, 16);
112 	if (val < 0 || val > 0xffff)
113 		goto confused;
114 	*devno = val;
115 	if ((*str)[0] != ',' && (*str)[0] != '-' &&
116 	    (*str)[0] != '\n' && (*str)[0] != '\0')
117 		goto confused;
118 	return 0;
119 confused:
120 	strsep(str, ",\n");
121 	printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
122 	return 1;
123 }
124 
125 static inline int
126 blacklist_parse_parameters (char *str, range_action action)
127 {
128 	unsigned int from, to, from_id0, to_id0, from_id1, to_id1;
129 
130 	while (*str != 0 && *str != '\n') {
131 		range_action ra = action;
132 		while(*str == ',')
133 			str++;
134 		if (*str == '!') {
135 			ra = !action;
136 			++str;
137 		}
138 
139 		/*
140 		 * Since we have to parse the proc commands and the
141 		 * kernel arguments we have to check four cases
142 		 */
143 		if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
144 		    strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
145 			from = 0;
146 			to = __MAX_SUBCHANNELS;
147 			str += 3;
148 		} else {
149 			int rc;
150 
151 			rc = blacklist_busid(&str, &from_id0,
152 					     &from_id1, &from);
153 			if (rc)
154 				continue;
155 			to = from;
156 			to_id0 = from_id0;
157 			to_id1 = from_id1;
158 			if (*str == '-') {
159 				str++;
160 				rc = blacklist_busid(&str, &to_id0,
161 						     &to_id1, &to);
162 				if (rc)
163 					continue;
164 			}
165 			if (*str == '-') {
166 				printk(KERN_WARNING "invalid cio_ignore "
167 					"parameter '%s'\n",
168 					strsep(&str, ",\n"));
169 				continue;
170 			}
171 			if ((from_id0 != to_id0) || (from_id1 != to_id1)) {
172 				printk(KERN_WARNING "invalid cio_ignore range "
173 					"%x.%x.%04x-%x.%x.%04x\n",
174 					from_id0, from_id1, from,
175 					to_id0, to_id1, to);
176 				continue;
177 			}
178 		}
179 		/* FIXME: ignoring id0 and id1 here. */
180 		pr_debug("blacklist_setup: adding range "
181 			 "from 0.0.%04x to 0.0.%04x\n", from, to);
182 		blacklist_range (ra, from, to);
183 	}
184 	return 1;
185 }
186 
187 /* Parsing the commandline for blacklist parameters, e.g. to blacklist
188  * bus ids 0.0.1234, 0.0.1235 and 0.0.1236, you could use any of:
189  * - cio_ignore=1234-1236
190  * - cio_ignore=0x1234-0x1235,1236
191  * - cio_ignore=0x1234,1235-1236
192  * - cio_ignore=1236 cio_ignore=1234-0x1236
193  * - cio_ignore=1234 cio_ignore=1236 cio_ignore=0x1235
194  * - cio_ignore=0.0.1234-0.0.1236
195  * - cio_ignore=0.0.1234,0x1235,1236
196  * - ...
197  */
198 static int __init
199 blacklist_setup (char *str)
200 {
201 	CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
202 	return blacklist_parse_parameters (str, add);
203 }
204 
205 __setup ("cio_ignore=", blacklist_setup);
206 
207 /* Checking if devices are blacklisted */
208 
209 /*
210  * Function: is_blacklisted
211  * Returns 1 if the given devicenumber can be found in the blacklist,
212  * otherwise 0.
213  * Used by validate_subchannel()
214  */
215 int
216 is_blacklisted (int devno)
217 {
218 	return test_bit (devno, bl_dev);
219 }
220 
221 #ifdef CONFIG_PROC_FS
222 /*
223  * Function: s390_redo_validation
224  * Look for no longer blacklisted devices
225  * FIXME: there must be a better way to do this */
226 static inline void
227 s390_redo_validation (void)
228 {
229 	unsigned int irq;
230 
231 	CIO_TRACE_EVENT (0, "redoval");
232 	for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
233 		int ret;
234 		struct subchannel *sch;
235 
236 		sch = get_subchannel_by_schid(irq);
237 		if (sch) {
238 			/* Already known. */
239 			put_device(&sch->dev);
240 			continue;
241 		}
242 		ret = css_probe_device(irq);
243 		if (ret == -ENXIO)
244 			break; /* We're through. */
245 		if (ret == -ENOMEM)
246 			/*
247 			 * Stop validation for now. Bad, but no need for a
248 			 * panic.
249 			 */
250 			break;
251 	}
252 }
253 
254 /*
255  * Function: blacklist_parse_proc_parameters
256  * parse the stuff which is piped to /proc/cio_ignore
257  */
258 static inline void
259 blacklist_parse_proc_parameters (char *buf)
260 {
261 	if (strncmp (buf, "free ", 5) == 0) {
262 		blacklist_parse_parameters (buf + 5, free);
263 	} else if (strncmp (buf, "add ", 4) == 0) {
264 		/*
265 		 * We don't need to check for known devices since
266 		 * css_probe_device will handle this correctly.
267 		 */
268 		blacklist_parse_parameters (buf + 4, add);
269 	} else {
270 		printk (KERN_WARNING "cio_ignore: Parse error; \n"
271 			KERN_WARNING "try using 'free all|<devno-range>,"
272 				     "<devno-range>,...'\n"
273 			KERN_WARNING "or 'add <devno-range>,"
274 				     "<devno-range>,...'\n");
275 		return;
276 	}
277 
278 	s390_redo_validation ();
279 }
280 
281 /* FIXME: These should be real bus ids and not home-grown ones! */
282 static int cio_ignore_read (char *page, char **start, off_t off,
283 			    int count, int *eof, void *data)
284 {
285 	const unsigned int entry_size = 18; /* "0.0.ABCD-0.0.EFGH\n" */
286 	long devno;
287 	int len;
288 
289 	len = 0;
290 	for (devno = off; /* abuse the page variable
291 			   * as counter, see fs/proc/generic.c */
292 	     devno <= __MAX_SUBCHANNELS && len + entry_size < count; devno++) {
293 		if (!test_bit(devno, bl_dev))
294 			continue;
295 		len += sprintf(page + len, "0.0.%04lx", devno);
296 		if (test_bit(devno + 1, bl_dev)) { /* print range */
297 			while (++devno < __MAX_SUBCHANNELS)
298 				if (!test_bit(devno, bl_dev))
299 					break;
300 			len += sprintf(page + len, "-0.0.%04lx", --devno);
301 		}
302 		len += sprintf(page + len, "\n");
303 	}
304 
305 	if (devno <= __MAX_SUBCHANNELS)
306 		*eof = 1;
307 	*start = (char *) (devno - off); /* number of checked entries */
308 	return len;
309 }
310 
311 static int cio_ignore_write(struct file *file, const char __user *user_buf,
312 			     unsigned long user_len, void *data)
313 {
314 	char *buf;
315 
316 	if (user_len > 65536)
317 		user_len = 65536;
318 	buf = vmalloc (user_len + 1); /* maybe better use the stack? */
319 	if (buf == NULL)
320 		return -ENOMEM;
321 	if (strncpy_from_user (buf, user_buf, user_len) < 0) {
322 		vfree (buf);
323 		return -EFAULT;
324 	}
325 	buf[user_len] = '\0';
326 
327 	blacklist_parse_proc_parameters (buf);
328 
329 	vfree (buf);
330 	return user_len;
331 }
332 
333 static int
334 cio_ignore_proc_init (void)
335 {
336 	struct proc_dir_entry *entry;
337 
338 	entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
339 				   &proc_root);
340 	if (!entry)
341 		return 0;
342 
343 	entry->read_proc  = cio_ignore_read;
344 	entry->write_proc = cio_ignore_write;
345 
346 	return 1;
347 }
348 
349 __initcall (cio_ignore_proc_init);
350 
351 #endif /* CONFIG_PROC_FS */
352