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