xref: /xnu-8796.101.5/pexpert/gen/bootargs.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
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_parse_boot_arg_str(const char * arg_string,char * arg_ptr,int strlen)200 PE_parse_boot_arg_str(
201 	const char      *arg_string,
202 	char            *arg_ptr,
203 	int                     strlen)
204 {
205 	return PE_parse_boot_argn_internal(PE_boot_args(), arg_string, arg_ptr, strlen, TRUE);
206 }
207 
208 #if defined(CONFIG_XNUPOST)
209 kern_return_t
parse_boot_arg_test(void)210 parse_boot_arg_test(void)
211 {
212 	// Tests are derived from libc/tests/darwin_bsd.c
213 	static struct string_test_case {
214 		char *args;
215 		const char *argname;
216 		char *argvalue;
217 		boolean_t found;
218 	} string_test_cases[] = {
219 		{"-x -a b=3 y=42", "-a", "1", TRUE},
220 		{"-x -a b=3 y=42", "b", "3", TRUE},
221 		{"-x -a b=2 ba=3 y=42", "b", "2", TRUE},
222 		{"-x -a ba=3 b=2 y=42", "b", "2", TRUE},
223 		{"-x -a b=2 ba=3 y=42", "ba", "3", TRUE},
224 		{"-x -a ba=3 b=2 y=42", "ba", "3", TRUE},
225 		{"-x -ab -aa y=42", "-a", NULL, FALSE},
226 		{"-x b=96 y=42", "bx", NULL, FALSE},
227 		{"-x ab=96 y=42", "a", NULL, FALSE},
228 		{"hello=world -foobar abc debug=0xBAADF00D", "notarealthing", NULL,
229 		 FALSE},
230 		{"hello=world -foobar abc debug=0xBAADF00D", "hello", "world", TRUE},
231 		{"hello=world -foobar abc debug=0xBAADF00D", "debug", "0xBAADF00D",
232 		 TRUE},
233 		{"hello=world -foobar abc debug=0xBAADF00D", "-foobar", "1", TRUE},
234 		{"hello=world -foobar abc debug=0xBAADF00D", "abc", "1", TRUE},
235 	};
236 
237 	T_LOG("Testing boot-arg string parsing.\n");
238 	for (int i = 0; i < (int)(sizeof(string_test_cases) /
239 	    sizeof(string_test_cases[0])); i++) {
240 		struct string_test_case *test_case = &string_test_cases[i];
241 
242 		char result[256] = "NOT_FOUND";
243 		boolean_t found = PE_parse_boot_argn_internal(test_case->args,
244 		    test_case->argname, result, sizeof(result), TRUE);
245 
246 		if (test_case->found) {
247 			T_LOG("\"%s\": Looking for \"%s\", expecting \"%s\" found",
248 			    test_case->args, test_case->argname, test_case->argvalue);
249 			T_EXPECT(found, "Should find argument");
250 			T_EXPECT_EQ_STR(result, test_case->argvalue,
251 			    "Should find correct result");
252 		} else {
253 			T_LOG("\"%s\": Looking for \"%s\", expecting not found",
254 			    test_case->args, test_case->argname, test_case->argvalue);
255 			T_EXPECT(!found, "Should not find argument");
256 		}
257 	}
258 
259 	static struct integer_test_case {
260 		char *args;
261 		const char *argname;
262 		int argvalue;
263 		boolean_t found;
264 	} integer_test_cases[] = {
265 		{"-x -a b=3 y=42", "-a", 1, TRUE},
266 		{"-x -a b=3 y=42", "b", 3, TRUE},
267 		{"-x -a b=2 ba=3 y=42", "b", 2, TRUE},
268 		{"-x -a ba=3 b=2 y=42", "b", 2, TRUE},
269 		{"-x -a b=2 ba=3 y=42", "ba", 3, TRUE},
270 		{"-x -a ba=3 b=2 y=42", "ba", 3, TRUE},
271 		{"-x -ab -aa y=42", "-a", 0, FALSE},
272 		{"-x b=96 y=42", "bx", 0, FALSE},
273 		{"-x ab=96 y=42", "a", 0, FALSE},
274 		{"hello=world -foobar abc debug=0xBAADF00D", "notarealthing", 0, FALSE},
275 		{"hello=world -foobar abc debug=0xBAADF00D", "hello",
276 		 0x00726F77 /* "wor" */, TRUE},
277 		{"hello=world -foobar abc debug=0xBAADF00D", "debug", 0xBAADF00D, TRUE},
278 		{"hello=world -foobar abc debug=0xBAADF00D", "-foobar", 1, TRUE},
279 		{"hello=world -foobar abc debug=0xBAADF00D", "abc", 1, TRUE},
280 	};
281 
282 	T_LOG("Testing boot-arg integer parsing.\n");
283 	for (int i = 0; i < (int)(sizeof(integer_test_cases) /
284 	    sizeof(integer_test_cases[0])); i++) {
285 		struct integer_test_case *test_case = &integer_test_cases[i];
286 
287 		int result = 0xCAFEFACE;
288 		boolean_t found = PE_parse_boot_argn_internal(test_case->args,
289 		    test_case->argname, &result, sizeof(result), FALSE);
290 
291 		if (test_case->found) {
292 			T_LOG("\"%s\": Looking for \"%s\", expecting %d found",
293 			    test_case->args, test_case->argname, test_case->argvalue);
294 			T_EXPECT(found, "Should find argument");
295 			T_EXPECT_EQ_INT(result, test_case->argvalue,
296 			    "Should find correct result");
297 		} else {
298 			T_LOG("\"%s\": Looking for \"%s\", expecting not found",
299 			    test_case->args, test_case->argname, test_case->argvalue);
300 			T_EXPECT(!found, "Should not find argument");
301 		}
302 	}
303 
304 	return KERN_SUCCESS;
305 }
306 #endif /* defined(CONFIG_XNUPOST) */
307 
308 static boolean_t
isargsep(char c)309 isargsep(char c)
310 {
311 	if (c == ' ' || c == '\0' || c == '\t') {
312 		return TRUE;
313 	} else {
314 		return FALSE;
315 	}
316 }
317 
318 static boolean_t
israngesep(char c)319 israngesep(char c)
320 {
321 	if (isargsep(c) || c == '_' || c == ',') {
322 		return TRUE;
323 	} else {
324 		return FALSE;
325 	}
326 }
327 
328 #if defined(__x86_64__)
329 static int
argstrcpy(char * from,char * to)330 argstrcpy(
331 	char *from,
332 	char *to)
333 {
334 	int i = 0;
335 
336 	while (!isargsep(*from)) {
337 		i++;
338 		*to++ = *from++;
339 	}
340 	*to = 0;
341 	return i;
342 }
343 #endif
344 
345 static int
argstrcpy2(char * from,char * to,unsigned maxlen)346 argstrcpy2(
347 	char *from,
348 	char *to,
349 	unsigned maxlen)
350 {
351 	unsigned int i = 0;
352 
353 	while (!isargsep(*from) && i < maxlen) {
354 		i++;
355 		*to++ = *from++;
356 	}
357 	*to = 0;
358 	return i;
359 }
360 
361 static int
argnumcpy(long long val,void * to,unsigned maxlen)362 argnumcpy(long long val, void *to, unsigned maxlen)
363 {
364 	switch (maxlen) {
365 	case 0:
366 		/* No write-back, caller just wants to know if arg was found */
367 		break;
368 	case 1:
369 		*(int8_t *)to = (int8_t)val;
370 		break;
371 	case 2:
372 		*(int16_t *)to = (int16_t)val;
373 		break;
374 	case 3:
375 		/* Unlikely in practice */
376 		((struct i24 *)to)->i24 = (int32_t)val;
377 		break;
378 	case 4:
379 		*(int32_t *)to = (int32_t)val;
380 		break;
381 	case 8:
382 		*(int64_t *)to = (int64_t)val;
383 		break;
384 	default:
385 		*(int32_t *)to = (int32_t)val;
386 		maxlen = 4;
387 		break;
388 	}
389 
390 	return (int)maxlen;
391 }
392 
393 static int
getval(char * s,long long * val,argsep_func_t issep,boolean_t skip_equal_sign)394 getval(
395 	char *s,
396 	long long *val,
397 	argsep_func_t issep,
398 	boolean_t skip_equal_sign )
399 {
400 	unsigned long long radix, intval;
401 	unsigned char c;
402 	int sign = 1;
403 	boolean_t has_value = FALSE;
404 
405 	if (*s == '=') {
406 		s++;
407 		has_value = TRUE;
408 	}
409 
410 	if (has_value || skip_equal_sign) {
411 		if (*s == '-') {
412 			sign = -1;
413 			s++;
414 		}
415 		intval = *s++ - '0';
416 		radix = 10;
417 		if (intval == 0) {
418 			switch (*s) {
419 			case 'x':
420 				radix = 16;
421 				s++;
422 				break;
423 
424 			case 'b':
425 				radix = 2;
426 				s++;
427 				break;
428 
429 			case '0': case '1': case '2': case '3':
430 			case '4': case '5': case '6': case '7':
431 				intval = *s - '0';
432 				s++;
433 				radix = 8;
434 				break;
435 
436 			default:
437 				if (!issep(*s)) {
438 					return STR;
439 				}
440 			}
441 		} else if (intval >= radix) {
442 			return STR;
443 		}
444 		for (;;) {
445 			c = *s++;
446 			if (issep(c)) {
447 				break;
448 			}
449 			if ((radix <= 10) &&
450 			    ((c >= '0') && (c <= ('9' - (10 - radix))))) {
451 				c -= '0';
452 			} else if ((radix == 16) &&
453 			    ((c >= '0') && (c <= '9'))) {
454 				c -= '0';
455 			} else if ((radix == 16) &&
456 			    ((c >= 'a') && (c <= 'f'))) {
457 				c -= 'a' - 10;
458 			} else if ((radix == 16) &&
459 			    ((c >= 'A') && (c <= 'F'))) {
460 				c -= 'A' - 10;
461 			} else if (c == 'k' || c == 'K') {
462 				sign *= 1024;
463 				break;
464 			} else if (c == 'm' || c == 'M') {
465 				sign *= 1024 * 1024;
466 				break;
467 			} else if (c == 'g' || c == 'G') {
468 				sign *= 1024 * 1024 * 1024;
469 				break;
470 			} else {
471 				return STR;
472 			}
473 			if (c >= radix) {
474 				return STR;
475 			}
476 			intval *= radix;
477 			intval += c;
478 		}
479 		if (!issep(c) && !issep(*s)) {
480 			return STR;
481 		}
482 		*val = intval * sign;
483 		return NUM;
484 	}
485 	*val = 1;
486 	return NUM;
487 }
488 
489 boolean_t
PE_imgsrc_mount_supported()490 PE_imgsrc_mount_supported()
491 {
492 	return TRUE;
493 }
494 
495 boolean_t
PE_get_default(const char * property_name,void * property_ptr,unsigned int max_property)496 PE_get_default(
497 	const char      *property_name,
498 	void            *property_ptr,
499 	unsigned int max_property)
500 {
501 	DTEntry         dte;
502 	void const      *property_data;
503 	unsigned int property_size;
504 
505 	/*
506 	 * Look for the property using the PE DT support.
507 	 */
508 	if (kSuccess == SecureDTLookupEntry(NULL, "/defaults", &dte)) {
509 		/*
510 		 * We have a /defaults node, look for the named property.
511 		 */
512 		if (kSuccess != SecureDTGetProperty(dte, property_name, &property_data, &property_size)) {
513 			return FALSE;
514 		}
515 
516 		/*
517 		 * This would be a fine place to do smart argument size management for 32/64
518 		 * translation, but for now we'll insist that callers know how big their
519 		 * default values are.
520 		 */
521 		if (property_size > max_property) {
522 			return FALSE;
523 		}
524 
525 		/*
526 		 * Copy back the precisely-sized result.
527 		 */
528 		memcpy(property_ptr, property_data, property_size);
529 		return TRUE;
530 	}
531 
532 	/*
533 	 * Look for the property using I/O Kit's DT support.
534 	 */
535 	return IODTGetDefault(property_name, property_ptr, max_property) ? FALSE : TRUE;
536 }
537 
538 /* function: get_range_bounds
539  * Parse a range string like "1_3,5_20" and return 1,3 as lower and upper.
540  * Note: '_' is separator for bounds integer delimiter and
541  *       ',' is considered as separator for range pair.
542  * returns TRUE when both range values are found
543  */
544 boolean_t
get_range_bounds(char * c,int64_t * lower,int64_t * upper)545 get_range_bounds(char *c, int64_t *lower, int64_t *upper)
546 {
547 	if (c == NULL || lower == NULL || upper == NULL) {
548 		return FALSE;
549 	}
550 
551 	if (NUM != getval(c, lower, israngesep, TRUE)) {
552 		return FALSE;
553 	}
554 
555 	while (*c != '\0') {
556 		if (*c == '_') {
557 			break;
558 		}
559 		c++;
560 	}
561 
562 	if (*c == '_') {
563 		c++;
564 		if (NUM != getval(c, upper, israngesep, TRUE)) {
565 			return FALSE;
566 		}
567 	} else {
568 		return FALSE;
569 	}
570 	return TRUE;
571 }
572