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