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