xref: /xnu-11215/pexpert/gen/bootargs.c (revision 1031c584)
1 /*
2  * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 #include <pexpert/pexpert.h>
29 #include <pexpert/device_tree.h>
30 
31 #if defined(CONFIG_XNUPOST)
32 #include <tests/xnupost.h>
33 #endif
34 
35 typedef boolean_t (*argsep_func_t) (char c);
36 
37 static boolean_t isargsep( char c);
38 static boolean_t israngesep( char c);
39 #if defined(__x86_64__)
40 static int argstrcpy(char *from, char *to);
41 #endif
42 static int argstrcpy2(char *from, char *to, unsigned maxlen);
43 static int argnumcpy(long long val, void *to, unsigned maxlen);
44 static int getval(char *s, long long *val, argsep_func_t issep, boolean_t skip_equal_sign);
45 boolean_t get_range_bounds(char * c, int64_t * lower, int64_t * upper);
46 
47 extern int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize);
48 #if defined(CONFIG_XNUPOST)
49 kern_return_t parse_boot_arg_test(void);
50 #endif
51 
52 struct i24 {
53 	int32_t i24 : 24;
54 	int32_t _pad : 8;
55 };
56 
57 #define NUM     0
58 #define STR     1
59 
60 static boolean_t
PE_parse_boot_argn_internal(char * args,const char * arg_string,void * arg_ptr,int max_len,boolean_t force_string)61 PE_parse_boot_argn_internal(
62 	char *args,
63 	const char *arg_string,
64 	void *      arg_ptr,
65 	int         max_len,
66 	boolean_t   force_string)
67 {
68 	char *cp, c;
69 	uintptr_t i;
70 	long long val = 0;
71 	boolean_t arg_boolean;
72 	boolean_t arg_found;
73 
74 	if (*args == '\0') {
75 		return FALSE;
76 	}
77 
78 #if !defined(__x86_64__)
79 	if (max_len == -1) {
80 		return FALSE;
81 	}
82 #endif
83 
84 	arg_found = FALSE;
85 
86 	while (*args && isargsep(*args)) {
87 		args++;
88 	}
89 
90 	while (*args) {
91 		if (*args == '-') {
92 			arg_boolean = TRUE;
93 		} else {
94 			arg_boolean = FALSE;
95 		}
96 
97 		cp = args;
98 		while (!isargsep(*cp) && *cp != '=') {
99 			cp++;
100 		}
101 
102 		c = *cp;
103 
104 		i = cp - args;
105 		if (strncmp(args, arg_string, i) ||
106 		    (i != strlen(arg_string))) {
107 			goto gotit;
108 		}
109 
110 		if (arg_boolean) {
111 			if (max_len > 0) {
112 				if (force_string) {
113 					argstrcpy2("1", arg_ptr, max_len);
114 				} else {
115 					argnumcpy(1, arg_ptr, max_len);/* max_len of 0 performs no copy at all*/
116 				}
117 				arg_found = TRUE;
118 			} else if (max_len == 0) {
119 				arg_found = TRUE;
120 			}
121 			break;
122 		} else {
123 			while (*cp && isargsep(*cp)) {
124 				cp++;
125 			}
126 			if (*cp == '=' && c != '=') {
127 				args = cp + 1;
128 				goto gotit;
129 			}
130 			if ('_' == *arg_string) { /* Force a string copy if the argument name begins with an underscore */
131 				if (max_len > 0) {
132 					int hacklen = 17 > max_len ? 17 : max_len;
133 					argstrcpy2(++cp, (char *)arg_ptr, hacklen - 1);  /* Hack - terminate after 16 characters */
134 					arg_found = TRUE;
135 				} else if (max_len == 0) {
136 					arg_found = TRUE;
137 				}
138 				break;
139 			}
140 			switch (force_string ? STR : getval(cp, &val, isargsep, FALSE)) {
141 			case NUM:
142 				if (max_len > 0) {
143 					argnumcpy(val, arg_ptr, max_len);
144 					arg_found = TRUE;
145 				} else if (max_len == 0) {
146 					arg_found = TRUE;
147 				}
148 				break;
149 			case STR:
150 				if (*cp == '=') {
151 					if (max_len > 0) {
152 						argstrcpy2(++cp, (char *)arg_ptr, max_len - 1);        /*max_len of 0 performs no copy at all*/
153 						arg_found = TRUE;
154 					} else if (max_len == 0) {
155 						arg_found = TRUE;
156 					}
157 #if defined(__x86_64__)
158 					else if (max_len == -1) {         /* unreachable on embedded */
159 						argstrcpy(++cp, (char *)arg_ptr);
160 						arg_found = TRUE;
161 					}
162 #endif
163 				} else {
164 					if (max_len > 0) {
165 						argstrcpy2("1", arg_ptr, max_len);
166 						arg_found = TRUE;
167 					} else if (max_len == 0) {
168 						arg_found = TRUE;
169 					}
170 				}
171 				break;
172 			}
173 			goto gotit;
174 		}
175 gotit:
176 		/* Skip over current arg */
177 		while (!isargsep(*args)) {
178 			args++;
179 		}
180 
181 		/* Skip leading white space (catch end of args) */
182 		while (*args && isargsep(*args)) {
183 			args++;
184 		}
185 	}
186 
187 	return arg_found;
188 }
189 
190 boolean_t
PE_parse_boot_argn(const char * arg_string,void * arg_ptr,int max_len)191 PE_parse_boot_argn(
192 	const char      *arg_string,
193 	void            *arg_ptr,
194 	int                     max_len)
195 {
196 	return PE_parse_boot_argn_internal(PE_boot_args(), arg_string, arg_ptr, max_len, FALSE);
197 }
198 
199 boolean_t
PE_boot_arg_uint64_eq(const char * arg_string,uint64_t value)200 PE_boot_arg_uint64_eq(const char *arg_string, uint64_t value)
201 {
202 	uint64_t tmp;
203 	if (!PE_parse_boot_argn(arg_string, &tmp, sizeof(tmp))) {
204 		return false;
205 	}
206 
207 	return tmp == value;
208 }
209 
210 boolean_t
PE_parse_boot_arg_str(const char * arg_string,char * arg_ptr,int strlen)211 PE_parse_boot_arg_str(
212 	const char      *arg_string,
213 	char            *arg_ptr,
214 	int                     strlen)
215 {
216 	return PE_parse_boot_argn_internal(PE_boot_args(), arg_string, arg_ptr, strlen, TRUE);
217 }
218 
219 #if defined(CONFIG_XNUPOST)
220 kern_return_t
parse_boot_arg_test(void)221 parse_boot_arg_test(void)
222 {
223 	// Tests are derived from libc/tests/darwin_bsd.c
224 	static struct string_test_case {
225 		char *args;
226 		const char *argname;
227 		char *argvalue;
228 		boolean_t found;
229 	} string_test_cases[] = {
230 		{"-x -a b=3 y=42", "-a", "1", TRUE},
231 		{"-x -a b=3 y=42", "b", "3", TRUE},
232 		{"-x -a b=2 ba=3 y=42", "b", "2", TRUE},
233 		{"-x -a ba=3 b=2 y=42", "b", "2", TRUE},
234 		{"-x -a b=2 ba=3 y=42", "ba", "3", TRUE},
235 		{"-x -a ba=3 b=2 y=42", "ba", "3", TRUE},
236 		{"-x -ab -aa y=42", "-a", NULL, FALSE},
237 		{"-x b=96 y=42", "bx", NULL, FALSE},
238 		{"-x ab=96 y=42", "a", NULL, FALSE},
239 		{"hello=world -foobar abc debug=0xBAADF00D", "notarealthing", NULL,
240 		 FALSE},
241 		{"hello=world -foobar abc debug=0xBAADF00D", "hello", "world", TRUE},
242 		{"hello=world -foobar abc debug=0xBAADF00D", "debug", "0xBAADF00D",
243 		 TRUE},
244 		{"hello=world -foobar abc debug=0xBAADF00D", "-foobar", "1", TRUE},
245 		{"hello=world -foobar abc debug=0xBAADF00D", "abc", "1", TRUE},
246 	};
247 
248 	T_LOG("Testing boot-arg string parsing.\n");
249 	for (int i = 0; i < (int)(sizeof(string_test_cases) /
250 	    sizeof(string_test_cases[0])); i++) {
251 		struct string_test_case *test_case = &string_test_cases[i];
252 
253 		char result[256] = "NOT_FOUND";
254 		boolean_t found = PE_parse_boot_argn_internal(test_case->args,
255 		    test_case->argname, result, sizeof(result), TRUE);
256 
257 		if (test_case->found) {
258 			T_LOG("\"%s\": Looking for \"%s\", expecting \"%s\" found",
259 			    test_case->args, test_case->argname, test_case->argvalue);
260 			T_EXPECT(found, "Should find argument");
261 			T_EXPECT_EQ_STR(result, test_case->argvalue,
262 			    "Should find correct result");
263 		} else {
264 			T_LOG("\"%s\": Looking for \"%s\", expecting not found",
265 			    test_case->args, test_case->argname, test_case->argvalue);
266 			T_EXPECT(!found, "Should not find argument");
267 		}
268 	}
269 
270 	static struct integer_test_case {
271 		char *args;
272 		const char *argname;
273 		int argvalue;
274 		boolean_t found;
275 	} integer_test_cases[] = {
276 		{"-x -a b=3 y=42", "-a", 1, TRUE},
277 		{"-x -a b=3 y=42", "b", 3, TRUE},
278 		{"-x -a b=2 ba=3 y=42", "b", 2, TRUE},
279 		{"-x -a ba=3 b=2 y=42", "b", 2, TRUE},
280 		{"-x -a b=2 ba=3 y=42", "ba", 3, TRUE},
281 		{"-x -a ba=3 b=2 y=42", "ba", 3, TRUE},
282 		{"-x -ab -aa y=42", "-a", 0, FALSE},
283 		{"-x b=96 y=42", "bx", 0, FALSE},
284 		{"-x ab=96 y=42", "a", 0, FALSE},
285 		{"hello=world -foobar abc debug=0xBAADF00D", "notarealthing", 0, FALSE},
286 		{"hello=world -foobar abc debug=0xBAADF00D", "hello",
287 		 0x00726F77 /* "wor" */, TRUE},
288 		{"hello=world -foobar abc debug=0xBAADF00D", "debug", 0xBAADF00D, TRUE},
289 		{"hello=world -foobar abc debug=0xBAADF00D", "-foobar", 1, TRUE},
290 		{"hello=world -foobar abc debug=0xBAADF00D", "abc", 1, TRUE},
291 	};
292 
293 	T_LOG("Testing boot-arg integer parsing.\n");
294 	for (int i = 0; i < (int)(sizeof(integer_test_cases) /
295 	    sizeof(integer_test_cases[0])); i++) {
296 		struct integer_test_case *test_case = &integer_test_cases[i];
297 
298 		int result = 0xCAFEFACE;
299 		boolean_t found = PE_parse_boot_argn_internal(test_case->args,
300 		    test_case->argname, &result, sizeof(result), FALSE);
301 
302 		if (test_case->found) {
303 			T_LOG("\"%s\": Looking for \"%s\", expecting %d found",
304 			    test_case->args, test_case->argname, test_case->argvalue);
305 			T_EXPECT(found, "Should find argument");
306 			T_EXPECT_EQ_INT(result, test_case->argvalue,
307 			    "Should find correct result");
308 		} else {
309 			T_LOG("\"%s\": Looking for \"%s\", expecting not found",
310 			    test_case->args, test_case->argname, test_case->argvalue);
311 			T_EXPECT(!found, "Should not find argument");
312 		}
313 	}
314 
315 	return KERN_SUCCESS;
316 }
317 #endif /* defined(CONFIG_XNUPOST) */
318 
319 static boolean_t
isargsep(char c)320 isargsep(char c)
321 {
322 	if (c == ' ' || c == '\0' || c == '\t') {
323 		return TRUE;
324 	} else {
325 		return FALSE;
326 	}
327 }
328 
329 static boolean_t
israngesep(char c)330 israngesep(char c)
331 {
332 	if (isargsep(c) || c == '_' || c == ',') {
333 		return TRUE;
334 	} else {
335 		return FALSE;
336 	}
337 }
338 
339 #if defined(__x86_64__)
340 static int
argstrcpy(char * from,char * to)341 argstrcpy(
342 	char *from,
343 	char *to)
344 {
345 	int i = 0;
346 
347 	while (!isargsep(*from)) {
348 		i++;
349 		*to++ = *from++;
350 	}
351 	*to = 0;
352 	return i;
353 }
354 #endif
355 
356 static int
argstrcpy2(char * from,char * to,unsigned maxlen)357 argstrcpy2(
358 	char *from,
359 	char *to,
360 	unsigned maxlen)
361 {
362 	unsigned int i = 0;
363 
364 	while (!isargsep(*from) && i < maxlen) {
365 		i++;
366 		*to++ = *from++;
367 	}
368 	*to = 0;
369 	return i;
370 }
371 
372 static int
argnumcpy(long long val,void * to,unsigned maxlen)373 argnumcpy(long long val, void *to, unsigned maxlen)
374 {
375 	switch (maxlen) {
376 	case 0:
377 		/* No write-back, caller just wants to know if arg was found */
378 		break;
379 	case 1:
380 		*(int8_t *)to = (int8_t)val;
381 		break;
382 	case 2:
383 		*(int16_t *)to = (int16_t)val;
384 		break;
385 	case 3:
386 		/* Unlikely in practice */
387 		((struct i24 *)to)->i24 = (int32_t)val;
388 		break;
389 	case 4:
390 		*(int32_t *)to = (int32_t)val;
391 		break;
392 	case 8:
393 		*(int64_t *)to = (int64_t)val;
394 		break;
395 	default:
396 		*(int32_t *)to = (int32_t)val;
397 		maxlen = 4;
398 		break;
399 	}
400 
401 	return (int)maxlen;
402 }
403 
404 static int
getval(char * s,long long * val,argsep_func_t issep,boolean_t skip_equal_sign)405 getval(
406 	char *s,
407 	long long *val,
408 	argsep_func_t issep,
409 	boolean_t skip_equal_sign )
410 {
411 	unsigned long long radix, intval;
412 	unsigned char c;
413 	int sign = 1;
414 	boolean_t has_value = FALSE;
415 
416 	if (*s == '=') {
417 		s++;
418 		has_value = TRUE;
419 	}
420 
421 	if (has_value || skip_equal_sign) {
422 		if (*s == '-') {
423 			sign = -1;
424 			s++;
425 		}
426 		intval = *s++ - '0';
427 		radix = 10;
428 		if (intval == 0) {
429 			switch (*s) {
430 			case 'x':
431 				radix = 16;
432 				s++;
433 				break;
434 
435 			case 'b':
436 				radix = 2;
437 				s++;
438 				break;
439 
440 			case '0': case '1': case '2': case '3':
441 			case '4': case '5': case '6': case '7':
442 				intval = *s - '0';
443 				s++;
444 				radix = 8;
445 				break;
446 
447 			default:
448 				if (!issep(*s)) {
449 					return STR;
450 				}
451 			}
452 		} else if (intval >= radix) {
453 			return STR;
454 		}
455 		for (;;) {
456 			c = *s++;
457 			if (issep(c)) {
458 				break;
459 			}
460 			if ((radix <= 10) &&
461 			    ((c >= '0') && (c <= ('9' - (10 - radix))))) {
462 				c -= '0';
463 			} else if ((radix == 16) &&
464 			    ((c >= '0') && (c <= '9'))) {
465 				c -= '0';
466 			} else if ((radix == 16) &&
467 			    ((c >= 'a') && (c <= 'f'))) {
468 				c -= 'a' - 10;
469 			} else if ((radix == 16) &&
470 			    ((c >= 'A') && (c <= 'F'))) {
471 				c -= 'A' - 10;
472 			} else if (c == 'k' || c == 'K') {
473 				sign *= 1024;
474 				break;
475 			} else if (c == 'm' || c == 'M') {
476 				sign *= 1024 * 1024;
477 				break;
478 			} else if (c == 'g' || c == 'G') {
479 				sign *= 1024 * 1024 * 1024;
480 				break;
481 			} else {
482 				return STR;
483 			}
484 			if (c >= radix) {
485 				return STR;
486 			}
487 			intval *= radix;
488 			intval += c;
489 		}
490 		if (!issep(c) && !issep(*s)) {
491 			return STR;
492 		}
493 		*val = intval * sign;
494 		return NUM;
495 	}
496 	*val = 1;
497 	return NUM;
498 }
499 
500 boolean_t
PE_imgsrc_mount_supported()501 PE_imgsrc_mount_supported()
502 {
503 	return TRUE;
504 }
505 
506 boolean_t
PE_get_default(const char * property_name,void * property_ptr,unsigned int max_property)507 PE_get_default(
508 	const char      *property_name,
509 	void            *property_ptr,
510 	unsigned int max_property)
511 {
512 	DTEntry         dte;
513 	void const      *property_data;
514 	unsigned int property_size;
515 
516 	/*
517 	 * Look for the property using the PE DT support.
518 	 */
519 	if (kSuccess == SecureDTLookupEntry(NULL, "/defaults", &dte)) {
520 		/*
521 		 * We have a /defaults node, look for the named property.
522 		 */
523 		if (kSuccess != SecureDTGetProperty(dte, property_name, &property_data, &property_size)) {
524 			return FALSE;
525 		}
526 
527 		/*
528 		 * This would be a fine place to do smart argument size management for 32/64
529 		 * translation, but for now we'll insist that callers know how big their
530 		 * default values are.
531 		 */
532 		if (property_size > max_property) {
533 			return FALSE;
534 		}
535 
536 		/*
537 		 * Copy back the precisely-sized result.
538 		 */
539 		memcpy(property_ptr, property_data, property_size);
540 		return TRUE;
541 	}
542 
543 	/*
544 	 * Look for the property using I/O Kit's DT support.
545 	 */
546 	return IODTGetDefault(property_name, property_ptr, max_property) ? FALSE : TRUE;
547 }
548 
549 /* function: get_range_bounds
550  * Parse a range string like "1_3,5_20" and return 1,3 as lower and upper.
551  * Note: '_' is separator for bounds integer delimiter and
552  *       ',' is considered as separator for range pair.
553  * returns TRUE when both range values are found
554  */
555 boolean_t
get_range_bounds(char * c,int64_t * lower,int64_t * upper)556 get_range_bounds(char *c, int64_t *lower, int64_t *upper)
557 {
558 	if (c == NULL || lower == NULL || upper == NULL) {
559 		return FALSE;
560 	}
561 
562 	if (NUM != getval(c, lower, israngesep, TRUE)) {
563 		return FALSE;
564 	}
565 
566 	while (*c != '\0') {
567 		if (*c == '_') {
568 			break;
569 		}
570 		c++;
571 	}
572 
573 	if (*c == '_') {
574 		c++;
575 		if (NUM != getval(c, upper, israngesep, TRUE)) {
576 			return FALSE;
577 		}
578 	} else {
579 		return FALSE;
580 	}
581 	return TRUE;
582 }
583