]> git.saurik.com Git - apple/system_cmds.git/blob - nvram.tproj/nvram.c
system_cmds-735.50.6.tar.gz
[apple/system_cmds.git] / nvram.tproj / nvram.c
1 /*
2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 cc -o nvram nvram.c -framework CoreFoundation -framework IOKit -Wall
24 */
25
26 #include <stdio.h>
27 #include <IOKit/IOKitLib.h>
28 #include <IOKit/IOKitKeys.h>
29 #include <IOKit/IOKitKeysPrivate.h>
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <err.h>
32 #include <mach/mach_error.h>
33 #include <sys/stat.h>
34
35 // Prototypes
36 static void UsageMessage(char *message);
37 static void ParseFile(char *fileName);
38 static void ParseXMLFile(char *fileName);
39 static void SetOrGetOFVariable(char *str);
40 static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef,
41 CFTypeRef *valueRef);
42 static kern_return_t SetOFVariable(char *name, char *value);
43 static void DeleteOFVariable(char *name);
44 static void PrintOFVariables(void);
45 static void PrintOFVariable(const void *key,const void *value,void *context);
46 static void SetOFVariableFromFile(const void *key, const void *value, void *context);
47 static void ClearOFVariables(void);
48 static void ClearOFVariable(const void *key,const void *value,void *context);
49 static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value);
50
51 static void NVRamSyncNow(char *name);
52
53 // Global Variables
54 static char *gToolName;
55 static io_registry_entry_t gOptionsRef;
56 static bool gUseXML;
57 static bool gUseForceSync;
58
59
60 int main(int argc, char **argv)
61 {
62 long cnt;
63 char *str, errorMessage[256];
64 kern_return_t result;
65 mach_port_t masterPort;
66 int argcount = 0;
67
68 // Get the name of the command.
69 gToolName = strrchr(argv[0], '/');
70 if (gToolName != 0) gToolName++;
71 else gToolName = argv[0];
72
73 result = IOMasterPort(bootstrap_port, &masterPort);
74 if (result != KERN_SUCCESS) {
75 errx(1, "Error getting the IOMaster port: %s",
76 mach_error_string(result));
77 }
78
79 gOptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options");
80 if (gOptionsRef == 0) {
81 errx(1, "nvram is not supported on this system");
82 }
83
84 for (cnt = 1; cnt < argc; cnt++) {
85 str = argv[cnt];
86 if (str[0] == '-' && str[1] != 0) {
87 // Parse the options.
88 for (str += 1 ; *str; str++) {
89 switch (*str) {
90 case 'p' :
91 PrintOFVariables();
92 break;
93
94 case 'x' :
95 gUseXML = true;
96 break;
97
98 case 'f':
99 cnt++;
100 if (cnt < argc && *argv[cnt] != '-') {
101 ParseFile(argv[cnt]);
102 } else {
103 UsageMessage("missing filename");
104 }
105 break;
106
107 case 'd':
108 cnt++;
109 if (cnt < argc && *argv[cnt] != '-') {
110 DeleteOFVariable(argv[cnt]);
111 } else {
112 UsageMessage("missing name");
113 }
114 break;
115
116 case 'c':
117 ClearOFVariables();
118 break;
119 case 's':
120 // -s option is unadvertised -- advises the kernel more forcibly to
121 // commit the variable to nonvolatile storage
122 gUseForceSync = true;
123 break;
124 default:
125 strcpy(errorMessage, "no such option as --");
126 errorMessage[strlen(errorMessage)-1] = *str;
127 UsageMessage(errorMessage);
128 }
129 }
130 } else {
131 // Other arguments will be firmware variable requests.
132 argcount++;
133 SetOrGetOFVariable(str);
134 }
135 }
136
137 // radar:25206371
138 if (argcount == 0 && gUseForceSync == true) {
139 NVRamSyncNow("");
140 }
141
142 IOObjectRelease(gOptionsRef);
143
144 return 0;
145 }
146
147 // UsageMessage(message)
148 //
149 // Print the usage information and exit.
150 //
151 static void UsageMessage(char *message)
152 {
153 warnx("(usage: %s)", message);
154
155 printf("%s [-x] [-p] [-f filename] [-d name] [-c] name[=value] ...\n", gToolName);
156 printf("\t-x use XML format for printing or reading variables\n");
157 printf("\t (must appear before -p or -f)\n");
158 printf("\t-p print all firmware variables\n");
159 printf("\t-f set firmware variables from a text file\n");
160 printf("\t-d delete the named variable\n");
161 printf("\t-c delete all variables\n");
162 printf("\tname=value set named variable\n");
163 printf("\tname print variable\n");
164 printf("Note that arguments and options are executed in order.\n");
165
166 exit(1);
167 }
168
169
170 // States for ParseFile.
171 enum {
172 kFirstColumn = 0,
173 kScanComment,
174 kFindName,
175 kCollectName,
176 kFindValue,
177 kCollectValue,
178 kContinueValue,
179 kSetenv,
180
181 kMaxStringSize = 0x800,
182 kMaxNameSize = 0x100
183 };
184
185
186 // ParseFile(fileName)
187 //
188 // Open and parse the specified file.
189 //
190 static void ParseFile(char *fileName)
191 {
192 long state, ni = 0, vi = 0;
193 int tc;
194 char name[kMaxNameSize];
195 char value[kMaxStringSize];
196 FILE *patches;
197 kern_return_t kret;
198
199 if (gUseXML) {
200 ParseXMLFile(fileName);
201 return;
202 }
203
204 patches = fopen(fileName, "r");
205 if (patches == 0) {
206 err(1, "Couldn't open patch file - '%s'", fileName);
207 }
208
209 state = kFirstColumn;
210 while ((tc = getc(patches)) != EOF) {
211 if(ni==(kMaxNameSize-1))
212 errx(1, "Name exceeded max length of %d", kMaxNameSize);
213 if(vi==(kMaxStringSize-1))
214 errx(1, "Value exceeded max length of %d", kMaxStringSize);
215 switch (state) {
216 case kFirstColumn :
217 ni = 0;
218 vi = 0;
219 if (tc == '#') {
220 state = kScanComment;
221 } else if (tc == '\n') {
222 // state stays kFirstColumn.
223 } else if (isspace(tc)) {
224 state = kFindName;
225 } else {
226 state = kCollectName;
227 name[ni++] = tc;
228 }
229 break;
230
231 case kScanComment :
232 if (tc == '\n') {
233 state = kFirstColumn;
234 } else {
235 // state stays kScanComment.
236 }
237 break;
238
239 case kFindName :
240 if (tc == '\n') {
241 state = kFirstColumn;
242 } else if (isspace(tc)) {
243 // state stays kFindName.
244 } else {
245 state = kCollectName;
246 name[ni++] = tc;
247 }
248 break;
249
250 case kCollectName :
251 if (tc == '\n') {
252 name[ni] = 0;
253 warnx("Name must be followed by white space - '%s'", name);
254 state = kFirstColumn;
255 } else if (isspace(tc)) {
256 state = kFindValue;
257 } else {
258 name[ni++] = tc;
259 // state staus kCollectName.
260 }
261 break;
262
263 case kFindValue :
264 case kContinueValue :
265 if (tc == '\n') {
266 state = kSetenv;
267 } else if (isspace(tc)) {
268 // state stays kFindValue or kContinueValue.
269 } else {
270 state = kCollectValue;
271 value[vi++] = tc;
272 }
273 break;
274
275 case kCollectValue :
276 if (tc == '\n') {
277 if (value[vi-1] == '\\') {
278 value[vi-1] = '\r';
279 state = kContinueValue;
280 } else {
281 state = kSetenv;
282 }
283 } else {
284 // state stays kCollectValue.
285 value[vi++] = tc;
286 }
287 break;
288 }
289
290 if (state == kSetenv) {
291 name[ni] = 0;
292 value[vi] = 0;
293 if ((kret = SetOFVariable(name, value)) != KERN_SUCCESS) {
294 errx(1, "Error setting variable - '%s': %s", name,
295 mach_error_string(kret));
296 }
297 state = kFirstColumn;
298 }
299 }
300
301 if (state != kFirstColumn) {
302 errx(1, "Last line ended abruptly");
303 }
304 }
305
306 // ParseXMLFile(fileName)
307 //
308 // Open and parse the specified file in XML format,
309 // and set variables appropriately.
310 //
311 static void ParseXMLFile(char *fileName)
312 {
313 CFPropertyListRef plist;
314 int fd;
315 struct stat sb;
316 char *buffer;
317 CFReadStreamRef stream;
318 CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0;
319
320 fd = open(fileName, O_RDONLY | O_NOFOLLOW, S_IFREG);
321 if (fd == -1) {
322 errx(1, "Could not open %s: %s", fileName, strerror(errno));
323 }
324
325 if (fstat(fd, &sb) == -1) {
326 errx(1, "Could not fstat %s: %s", fileName, strerror(errno));
327 }
328
329 if (sb.st_size > UINT32_MAX) {
330 errx(1, "too big for our purposes");
331 }
332
333 buffer = malloc((size_t)sb.st_size);
334 if (buffer == NULL) {
335 errx(1, "Could not allocate buffer");
336 }
337
338 if (read(fd, buffer, (size_t)sb.st_size) != sb.st_size) {
339 errx(1, "Could not read %s: %s", fileName, strerror(errno));
340 }
341
342 close(fd);
343
344 stream = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault,
345 (const UInt8 *)buffer,
346 (CFIndex)sb.st_size,
347 kCFAllocatorNull);
348 if (stream == NULL) {
349 errx(1, "Could not create stream from serialized data");
350 }
351
352 if (!CFReadStreamOpen(stream)) {
353 errx(1, "Could not open the stream");
354 }
355
356 plist = CFPropertyListCreateWithStream(kCFAllocatorDefault,
357 stream,
358 (CFIndex)sb.st_size,
359 kCFPropertyListImmutable,
360 &format,
361 NULL);
362
363 if (plist == NULL) {
364 errx(1, "Error parsing XML file");
365 }
366
367 CFReadStreamClose(stream);
368
369 CFRelease(stream);
370
371 free(buffer);
372
373 CFDictionaryApplyFunction(plist, &SetOFVariableFromFile, 0);
374
375 CFRelease(plist);
376 }
377
378 // SetOrGetOFVariable(str)
379 //
380 // Parse the input string, then set or get the specified
381 // firmware variable.
382 //
383 static void SetOrGetOFVariable(char *str)
384 {
385 long set = 0;
386 char *name;
387 char *value;
388 CFStringRef nameRef;
389 CFTypeRef valueRef;
390 kern_return_t result;
391
392 // OF variable name is first.
393 name = str;
394
395 // Find the equal sign for set
396 while (*str) {
397 if (*str == '=') {
398 set = 1;
399 *str++ = '\0';
400 break;
401 }
402 str++;
403 }
404
405 if (set == 1) {
406 // On sets, the OF variable's value follows the equal sign.
407 value = str;
408
409 result = SetOFVariable(name, value);
410 NVRamSyncNow(name); /* Try syncing the new data to device, best effort! */
411 if (result != KERN_SUCCESS) {
412 errx(1, "Error setting variable - '%s': %s", name,
413 mach_error_string(result));
414 }
415 } else {
416 result = GetOFVariable(name, &nameRef, &valueRef);
417 if (result != KERN_SUCCESS) {
418 errx(1, "Error getting variable - '%s': %s", name,
419 mach_error_string(result));
420 }
421
422 PrintOFVariable(nameRef, valueRef, 0);
423 CFRelease(nameRef);
424 CFRelease(valueRef);
425 }
426 }
427
428
429 // GetOFVariable(name, nameRef, valueRef)
430 //
431 // Get the named firmware variable.
432 // Return it and it's symbol in valueRef and nameRef.
433 //
434 static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef,
435 CFTypeRef *valueRef)
436 {
437 *nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
438 kCFStringEncodingUTF8);
439 if (*nameRef == 0) {
440 errx(1, "Error creating CFString for key %s", name);
441 }
442
443 *valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, *nameRef, 0, 0);
444 if (*valueRef == 0) return kIOReturnNotFound;
445
446 return KERN_SUCCESS;
447 }
448
449
450 // SetOFVariable(name, value)
451 //
452 // Set or create an firmware variable with name and value.
453 //
454 static kern_return_t SetOFVariable(char *name, char *value)
455 {
456 CFStringRef nameRef;
457 CFTypeRef valueRef;
458 CFTypeID typeID;
459 kern_return_t result = KERN_SUCCESS;
460
461 nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
462 kCFStringEncodingUTF8);
463 if (nameRef == 0) {
464 errx(1, "Error creating CFString for key %s", name);
465 }
466
467 valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0);
468 if (valueRef) {
469 typeID = CFGetTypeID(valueRef);
470 CFRelease(valueRef);
471
472 valueRef = ConvertValueToCFTypeRef(typeID, value);
473 if (valueRef == 0) {
474 errx(1, "Error creating CFTypeRef for value %s", value);
475 } result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
476 } else {
477 while (1) {
478 // In the default case, try data, string, number, then boolean.
479
480 valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value);
481 if (valueRef != 0) {
482 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
483 if (result == KERN_SUCCESS) break;
484 }
485
486 valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value);
487 if (valueRef != 0) {
488 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
489 if (result == KERN_SUCCESS) break;
490 }
491
492 valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value);
493 if (valueRef != 0) {
494 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
495 if (result == KERN_SUCCESS) break;
496 }
497
498 valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value);
499 if (valueRef != 0) {
500 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
501 if (result == KERN_SUCCESS) break;
502 }
503
504 break;
505 }
506 }
507
508 CFRelease(nameRef);
509
510 return result;
511 }
512
513
514 // DeleteOFVariable(name)
515 //
516 // Delete the named firmware variable.
517 //
518 //
519 static void DeleteOFVariable(char *name)
520 {
521 SetOFVariable(kIONVRAMDeletePropertyKey, name);
522 }
523
524 static void NVRamSyncNow(char *name)
525 {
526 if (!gUseForceSync) {
527 SetOFVariable(kIONVRAMSyncNowPropertyKey, name);
528 } else {
529 SetOFVariable(kIONVRAMForceSyncNowPropertyKey, name);
530 }
531 }
532
533 // PrintOFVariables()
534 //
535 // Print all of the firmware variables.
536 //
537 static void PrintOFVariables(void)
538 {
539 kern_return_t result;
540 CFMutableDictionaryRef dict;
541
542 result = IORegistryEntryCreateCFProperties(gOptionsRef, &dict, 0, 0);
543 if (result != KERN_SUCCESS) {
544 errx(1, "Error getting the firmware variables: %s", mach_error_string(result));
545 }
546
547 if (gUseXML) {
548 CFDataRef data;
549
550 data = CFPropertyListCreateData( kCFAllocatorDefault, dict, kCFPropertyListXMLFormat_v1_0, 0, NULL );
551 if (data == NULL) {
552 errx(1, "Error converting variables to xml");
553 }
554
555 fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout);
556
557 CFRelease(data);
558
559 } else {
560
561 CFDictionaryApplyFunction(dict, &PrintOFVariable, 0);
562
563 }
564
565 CFRelease(dict);
566 }
567
568 // PrintOFVariable(key, value, context)
569 //
570 // Print the given firmware variable.
571 //
572 static void PrintOFVariable(const void *key, const void *value, void *context)
573 {
574 long cnt, cnt2;
575 CFIndex nameLen;
576 char *nameBuffer = 0;
577 const char *nameString;
578 char numberBuffer[10];
579 const uint8_t *dataPtr;
580 uint8_t dataChar;
581 char *dataBuffer = 0;
582 CFIndex valueLen;
583 char *valueBuffer = 0;
584 const char *valueString = 0;
585 uint32_t number;
586 long length;
587 CFTypeID typeID;
588
589 // Get the OF variable's name.
590 nameLen = CFStringGetLength(key) + 1;
591 nameBuffer = malloc(nameLen);
592 if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) )
593 nameString = nameBuffer;
594 else {
595 warnx("Unable to convert property name to C string");
596 nameString = "<UNPRINTABLE>";
597 }
598
599 // Get the OF variable's type.
600 typeID = CFGetTypeID(value);
601
602 if (typeID == CFBooleanGetTypeID()) {
603 if (CFBooleanGetValue(value)) valueString = "true";
604 else valueString = "false";
605 } else if (typeID == CFNumberGetTypeID()) {
606 CFNumberGetValue(value, kCFNumberSInt32Type, &number);
607 if (number == 0xFFFFFFFF) sprintf(numberBuffer, "-1");
608 else if (number < 1000) sprintf(numberBuffer, "%d", number);
609 else sprintf(numberBuffer, "0x%x", number);
610 valueString = numberBuffer;
611 } else if (typeID == CFStringGetTypeID()) {
612 valueLen = CFStringGetLength(value) + 1;
613 valueBuffer = malloc(valueLen + 1);
614 if ( valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8) )
615 valueString = valueBuffer;
616 else {
617 warnx("Unable to convert value to C string");
618 valueString = "<UNPRINTABLE>";
619 }
620 } else if (typeID == CFDataGetTypeID()) {
621 length = CFDataGetLength(value);
622 if (length == 0) valueString = "";
623 else {
624 dataBuffer = malloc(length * 3 + 1);
625 if (dataBuffer != 0) {
626 dataPtr = CFDataGetBytePtr(value);
627 for (cnt = cnt2 = 0; cnt < length; cnt++) {
628 dataChar = dataPtr[cnt];
629 if (isprint(dataChar) && dataChar != '%') {
630 dataBuffer[cnt2++] = dataChar;
631 } else {
632 sprintf(dataBuffer + cnt2, "%%%02x", dataChar);
633 cnt2 += 3;
634 }
635 }
636 dataBuffer[cnt2] = '\0';
637 valueString = dataBuffer;
638 }
639 }
640 } else {
641 valueString="<INVALID>";
642 }
643
644 if ((nameString != 0) && (valueString != 0))
645 printf("%s\t%s\n", nameString, valueString);
646
647 if (dataBuffer != 0) free(dataBuffer);
648 if (nameBuffer != 0) free(nameBuffer);
649 if (valueBuffer != 0) free(valueBuffer);
650 }
651
652 // ClearOFVariables()
653 //
654 // Deletes all OF variables
655 //
656 static void ClearOFVariables(void)
657 {
658 kern_return_t result;
659 CFMutableDictionaryRef dict;
660
661 result = IORegistryEntryCreateCFProperties(gOptionsRef, &dict, 0, 0);
662 if (result != KERN_SUCCESS) {
663 errx(1, "Error getting the firmware variables: %s", mach_error_string(result));
664 }
665 CFDictionaryApplyFunction(dict, &ClearOFVariable, 0);
666
667 CFRelease(dict);
668 }
669
670 static void ClearOFVariable(const void *key, const void *value, void *context)
671 {
672 kern_return_t result;
673 result = IORegistryEntrySetCFProperty(gOptionsRef,
674 CFSTR(kIONVRAMDeletePropertyKey), key);
675 if (result != KERN_SUCCESS) {
676 errx(1, "Error clearing firmware variables: %s", mach_error_string(result));
677 }
678 }
679
680 // ConvertValueToCFTypeRef(typeID, value)
681 //
682 // Convert the value into a CFType given the typeID.
683 //
684 static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value)
685 {
686 CFTypeRef valueRef = 0;
687 long cnt, cnt2, length;
688 unsigned long number, tmp;
689
690 if (typeID == CFBooleanGetTypeID()) {
691 if (!strcmp("true", value)) valueRef = kCFBooleanTrue;
692 else if (!strcmp("false", value)) valueRef = kCFBooleanFalse;
693 } else if (typeID == CFNumberGetTypeID()) {
694 number = strtol(value, 0, 0);
695 valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
696 &number);
697 } else if (typeID == CFStringGetTypeID()) {
698 valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value,
699 kCFStringEncodingUTF8);
700 } else if (typeID == CFDataGetTypeID()) {
701 length = strlen(value);
702 for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) {
703 if (value[cnt] == '%') {
704 if (!ishexnumber(value[cnt + 1]) ||
705 !ishexnumber(value[cnt + 2])) return 0;
706 number = toupper(value[++cnt]) - '0';
707 if (number > 9) number -= 7;
708 tmp = toupper(value[++cnt]) - '0';
709 if (tmp > 9) tmp -= 7;
710 number = (number << 4) + tmp;
711 value[cnt2] = number;
712 } else value[cnt2] = value[cnt];
713 }
714 valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value,
715 cnt2, kCFAllocatorDefault);
716 } else return 0;
717
718 return valueRef;
719 }
720
721 static void SetOFVariableFromFile(const void *key, const void *value, void *context)
722 {
723 kern_return_t result;
724
725 result = IORegistryEntrySetCFProperty(gOptionsRef, key, value);
726 if ( result != KERN_SUCCESS ) {
727 long nameLen;
728 char *nameBuffer;
729 char *nameString;
730
731 // Get the variable's name.
732 nameLen = CFStringGetLength(key) + 1;
733 nameBuffer = malloc(nameLen);
734 if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) )
735 nameString = nameBuffer;
736 else {
737 warnx("Unable to convert property name to C string");
738 nameString = "<UNPRINTABLE>";
739 }
740 errx(1, "Error setting variable - '%s': %s", nameString,
741 mach_error_string(result));
742 }
743 }