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 |
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(c: *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(c: *cp) && *cp != '=') { |
99 | cp++; |
100 | } |
101 | |
102 | c = *cp; |
103 | |
104 | i = cp - args; |
105 | if (strncmp(s1: args, s2: arg_string, n: i) || |
106 | (i != strlen(s: arg_string))) { |
107 | goto gotit; |
108 | } |
109 | |
110 | if (arg_boolean) { |
111 | if (max_len > 0) { |
112 | if (force_string) { |
113 | argstrcpy2(from: "1" , to: arg_ptr, maxlen: max_len); |
114 | } else { |
115 | argnumcpy(val: 1, to: arg_ptr, maxlen: 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(c: *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(from: ++cp, to: (char *)arg_ptr, maxlen: 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(s: cp, val: &val, issep: isargsep, FALSE)) { |
141 | case NUM: |
142 | if (max_len > 0) { |
143 | argnumcpy(val, to: arg_ptr, maxlen: 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(from: ++cp, to: (char *)arg_ptr, maxlen: 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(from: "1" , to: arg_ptr, maxlen: 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(c: *args)) { |
178 | args++; |
179 | } |
180 | |
181 | /* Skip leading white space (catch end of args) */ |
182 | while (*args && isargsep(c: *args)) { |
183 | args++; |
184 | } |
185 | } |
186 | |
187 | return arg_found; |
188 | } |
189 | |
190 | boolean_t |
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(args: PE_boot_args(), arg_string, arg_ptr, max_len, FALSE); |
197 | } |
198 | |
199 | boolean_t |
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, arg_ptr: &tmp, max_len: sizeof(tmp))) { |
204 | return false; |
205 | } |
206 | |
207 | return tmp == value; |
208 | } |
209 | |
210 | boolean_t |
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(args: PE_boot_args(), arg_string, arg_ptr, max_len: strlen, TRUE); |
217 | } |
218 | |
219 | #if defined(CONFIG_XNUPOST) |
220 | kern_return_t |
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 |
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 |
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 |
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 |
357 | argstrcpy2( |
358 | char *from, |
359 | char *to, |
360 | unsigned maxlen) |
361 | { |
362 | unsigned int i = 0; |
363 | |
364 | while (!isargsep(c: *from) && i < maxlen) { |
365 | i++; |
366 | *to++ = *from++; |
367 | } |
368 | *to = 0; |
369 | return i; |
370 | } |
371 | |
372 | static int |
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 |
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 |
501 | PE_imgsrc_mount_supported() |
502 | { |
503 | return TRUE; |
504 | } |
505 | |
506 | boolean_t |
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, pathName: "/defaults" , foundEntry: &dte)) { |
520 | /* |
521 | * We have a /defaults node, look for the named property. |
522 | */ |
523 | if (kSuccess != SecureDTGetProperty(entry: dte, propertyName: property_name, propertyValue: &property_data, propertySize: &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(dst: property_ptr, src: property_data, n: property_size); |
540 | return TRUE; |
541 | } |
542 | |
543 | /* |
544 | * Look for the property using I/O Kit's DT support. |
545 | */ |
546 | return IODTGetDefault(key: property_name, infoAddr: property_ptr, infoSize: 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 |
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(s: c, val: lower, issep: 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(s: c, val: upper, issep: israngesep, TRUE)) { |
576 | return FALSE; |
577 | } |
578 | } else { |
579 | return FALSE; |
580 | } |
581 | return TRUE; |
582 | } |
583 | |