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
35typedef boolean_t (*argsep_func_t) (char c);
36
37static boolean_t isargsep( char c);
38static boolean_t israngesep( char c);
39#if defined(__x86_64__)
40static int argstrcpy(char *from, char *to);
41#endif
42static int argstrcpy2(char *from, char *to, unsigned maxlen);
43static int argnumcpy(long long val, void *to, unsigned maxlen);
44static int getval(char *s, long long *val, argsep_func_t issep, boolean_t skip_equal_sign);
45boolean_t get_range_bounds(char * c, int64_t * lower, int64_t * upper);
46
47extern int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize);
48#if defined(CONFIG_XNUPOST)
49kern_return_t parse_boot_arg_test(void);
50#endif
51
52struct i24 {
53 int32_t i24 : 24;
54 int32_t _pad : 8;
55};
56
57#define NUM 0
58#define STR 1
59
60static boolean_t
61PE_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 }
175gotit:
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
190boolean_t
191PE_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
199boolean_t
200PE_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
210boolean_t
211PE_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)
220kern_return_t
221parse_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
319static boolean_t
320isargsep(char c)
321{
322 if (c == ' ' || c == '\0' || c == '\t') {
323 return TRUE;
324 } else {
325 return FALSE;
326 }
327}
328
329static boolean_t
330israngesep(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__)
340static int
341argstrcpy(
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
356static int
357argstrcpy2(
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
372static int
373argnumcpy(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
404static int
405getval(
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
500boolean_t
501PE_imgsrc_mount_supported()
502{
503 return TRUE;
504}
505
506boolean_t
507PE_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 */
555boolean_t
556get_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