]>
Commit | Line | Data |
---|---|---|
1815bff5 | 1 | /* |
2fc1e207 | 2 | * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. |
1815bff5 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
2fc1e207 A |
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 | |
1815bff5 A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2fc1e207 A |
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 | * | |
1815bff5 A |
20 | * @APPLE_LICENSE_HEADER_END@ |
21 | */ | |
22 | /* | |
83f6dbe8 | 23 | cc -o nvram nvram.c -framework CoreFoundation -framework IOKit -Wall |
1815bff5 A |
24 | */ |
25 | ||
26 | #include <stdio.h> | |
27 | #include <IOKit/IOKitLib.h> | |
83f6dbe8 | 28 | #include <IOKit/IOKitKeys.h> |
1815bff5 A |
29 | #include <CoreFoundation/CoreFoundation.h> |
30 | ||
31 | // Prototypes | |
32 | static void Error(char *format, long item); | |
33 | static void FatalError(long exitValue, char *format, long item); | |
34 | static void UsageMessage(char *message); | |
35 | static void ParseFile(char *fileName); | |
36 | static void SetOrGetOFVariable(char *str); | |
37 | static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef, | |
38 | CFTypeRef *valueRef); | |
39 | static kern_return_t SetOFVariable(char *name, char *value); | |
83f6dbe8 | 40 | static void DeleteOFVariable(char *name); |
1815bff5 A |
41 | static void PrintOFVariables(void); |
42 | static void PrintOFVariable(const void *key,const void *value,void *context); | |
43 | static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value); | |
44 | ||
45 | // Global Variables | |
46 | static char *gToolName; | |
47 | static io_registry_entry_t gOptionsRef; | |
48 | ||
49 | ||
50 | int main(int argc, char **argv) | |
51 | { | |
52 | long cnt; | |
53 | char *str, errorMessage[256]; | |
54 | kern_return_t result; | |
55 | mach_port_t masterPort; | |
56 | ||
57 | // Get the name of the command. | |
58 | gToolName = strrchr(argv[0], '/'); | |
59 | if (gToolName != 0) gToolName++; | |
60 | else gToolName = argv[0]; | |
61 | ||
62 | result = IOMasterPort(bootstrap_port, &masterPort); | |
63 | if (result != KERN_SUCCESS) { | |
64 | FatalError(-1, "Error (%d) getting the IOMaster port", result); | |
65 | exit(-1); | |
66 | } | |
67 | ||
68 | gOptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options"); | |
69 | if (gOptionsRef == 0) { | |
09fd88e4 | 70 | FatalError(-1, "nvram is not supported on this system.", -1); |
1815bff5 A |
71 | exit(-1); |
72 | } | |
73 | ||
74 | for (cnt = 1; cnt < argc; cnt++) { | |
75 | str = argv[cnt]; | |
76 | if (str[0] == '-' && str[1] != 0) { | |
77 | // Parse the options. | |
78 | for (str += 1 ; *str; str++) { | |
79 | switch (*str) { | |
80 | case 'p' : | |
81 | PrintOFVariables(); | |
82 | break; | |
83 | ||
84 | case 'f': | |
85 | cnt++; | |
86 | if (cnt < argc && *argv[cnt] != '-') { | |
87 | ParseFile(argv[cnt]); | |
88 | } else { | |
89 | UsageMessage("missing filename"); | |
90 | } | |
91 | break; | |
92 | ||
83f6dbe8 A |
93 | case 'd': |
94 | cnt++; | |
95 | if (cnt < argc && *argv[cnt] != '-') { | |
96 | DeleteOFVariable(argv[cnt]); | |
97 | } else { | |
98 | UsageMessage("missing name"); | |
99 | } | |
100 | break; | |
101 | ||
1815bff5 A |
102 | default: |
103 | strcpy(errorMessage, "no such option as --"); | |
104 | errorMessage[strlen(errorMessage)-1] = *str; | |
105 | UsageMessage(errorMessage); | |
106 | } | |
107 | } | |
108 | } else { | |
4c00c0ae | 109 | // Other arguments will be firmware variable requests. |
1815bff5 A |
110 | SetOrGetOFVariable(str); |
111 | } | |
112 | } | |
113 | ||
114 | IOObjectRelease(gOptionsRef); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | ||
120 | // Error(format, item) | |
121 | // | |
122 | // Print a message on standard error. | |
123 | // | |
124 | static void Error(char *format, long item) | |
125 | { | |
126 | fprintf(stderr, "%s: ", gToolName); | |
127 | fprintf(stderr, format, item); | |
128 | fprintf(stderr, "\n"); | |
129 | } | |
130 | ||
131 | ||
132 | // FatalError(exitValue, format, item) | |
133 | // | |
134 | // Print a message on standard error and exit with value. | |
135 | // | |
136 | static void FatalError(long exitValue, char *format, long item) | |
137 | { | |
138 | fprintf(stderr, "%s: ", gToolName); | |
139 | fprintf(stderr, format, item); | |
140 | fprintf(stderr, "\n"); | |
141 | ||
142 | exit(exitValue); | |
143 | } | |
144 | ||
145 | ||
146 | // UsageMessage(message) | |
147 | // | |
148 | // Print the usage information and exit. | |
149 | // | |
150 | static void UsageMessage(char *message) | |
151 | { | |
152 | Error("(usage: %s)", (long)message); | |
153 | ||
83f6dbe8 | 154 | printf("%s [-p] [-f filename] [-d name] name[=value] ...\n", gToolName); |
4c00c0ae A |
155 | printf("\t-p print all firmware variables\n"); |
156 | printf("\t-f set firmware variables from a text file\n"); | |
83f6dbe8 | 157 | printf("\t-d delete the named variable\n"); |
1815bff5 A |
158 | printf("\tname=value set named variable\n"); |
159 | printf("\tname print variable\n"); | |
160 | printf("Note that arguments and options are executed in order.\n"); | |
161 | ||
162 | exit(1); | |
163 | } | |
164 | ||
165 | ||
166 | // States for ParseFile. | |
167 | enum { | |
168 | kFirstColumn = 0, | |
169 | kScanComment, | |
170 | kFindName, | |
171 | kCollectName, | |
172 | kFindValue, | |
173 | kCollectValue, | |
174 | kContinueValue, | |
175 | kSetenv, | |
176 | ||
177 | kMaxStringSize = 0x800, | |
178 | kMaxNameSize = 0x100 | |
179 | }; | |
180 | ||
181 | ||
182 | // ParseFile(fileName) | |
183 | // | |
184 | // Open and parse the specified file. | |
185 | // | |
186 | static void ParseFile(char *fileName) | |
187 | { | |
188 | long state, tc, ni = 0, vi = 0; | |
189 | char name[kMaxNameSize]; | |
190 | char value[kMaxStringSize]; | |
191 | FILE *patches; | |
192 | ||
193 | patches = fopen(fileName, "r"); | |
194 | if (patches == 0) { | |
195 | FatalError(errno, "Couldn't open patch file - '%s'", (long)fileName); | |
196 | } | |
197 | ||
198 | state = kFirstColumn; | |
199 | while ((tc = getc(patches)) != EOF) { | |
200 | switch (state) { | |
201 | case kFirstColumn : | |
202 | ni = 0; | |
203 | vi = 0; | |
204 | if (tc == '#') { | |
205 | state = kScanComment; | |
206 | } else if (tc == '\n') { | |
207 | // state stays kFirstColumn. | |
208 | } else if (isspace(tc)) { | |
209 | state = kFindName; | |
210 | } else { | |
211 | state = kCollectName; | |
212 | name[ni++] = tc; | |
213 | } | |
214 | break; | |
215 | ||
216 | case kScanComment : | |
217 | if (tc == '\n') { | |
218 | state = kFirstColumn; | |
219 | } else { | |
220 | // state stays kScanComment. | |
221 | } | |
222 | break; | |
223 | ||
224 | case kFindName : | |
225 | if (tc == '\n') { | |
226 | state = kFirstColumn; | |
227 | } else if (isspace(tc)) { | |
228 | // state stays kFindName. | |
229 | } else { | |
230 | state = kCollectName; | |
231 | name[ni++] = tc; | |
232 | } | |
233 | break; | |
234 | ||
235 | case kCollectName : | |
236 | if (tc == '\n') { | |
237 | name[ni] = 0; | |
238 | Error("Name must be followed by white space - '%s'", (long)name); | |
239 | state = kFirstColumn; | |
240 | } else if (isspace(tc)) { | |
241 | state = kFindValue; | |
242 | } else { | |
243 | name[ni++] = tc; | |
244 | // state staus kCollectName. | |
245 | } | |
246 | break; | |
247 | ||
248 | case kFindValue : | |
249 | case kContinueValue : | |
250 | if (tc == '\n') { | |
251 | state = kSetenv; | |
252 | } else if (isspace(tc)) { | |
253 | // state stays kFindValue or kContinueValue. | |
254 | } else { | |
255 | state = kCollectValue; | |
256 | value[vi++] = tc; | |
257 | } | |
258 | break; | |
259 | ||
260 | case kCollectValue : | |
261 | if (tc == '\n') { | |
262 | if (value[vi-1] == '\\') { | |
263 | value[vi-1] = '\r'; | |
264 | state = kContinueValue; | |
265 | } else { | |
266 | state = kSetenv; | |
267 | } | |
268 | } else { | |
269 | // state stays kCollectValue. | |
270 | value[vi++] = tc; | |
271 | } | |
272 | break; | |
273 | } | |
274 | ||
275 | if (state == kSetenv) { | |
276 | name[ni] = 0; | |
277 | value[vi] = 0; | |
278 | if (SetOFVariable(name, value) != KERN_SUCCESS) { | |
279 | FatalError(-1, "Error (-1) setting variable - '%s'", (long)name); | |
280 | } | |
281 | state = kFirstColumn; | |
282 | } | |
283 | } | |
284 | ||
285 | if (state != kFirstColumn) { | |
286 | FatalError(-1, "Last line ended abruptly", 0); | |
287 | } | |
288 | } | |
289 | ||
290 | ||
291 | // SetOrGetOFVariable(str) | |
292 | // | |
2fc1e207 | 293 | // Parse the input string, then set or get the specified |
4c00c0ae | 294 | // firmware variable. |
1815bff5 A |
295 | // |
296 | static void SetOrGetOFVariable(char *str) | |
297 | { | |
298 | long set = 0; | |
299 | char *name; | |
300 | char *value; | |
301 | CFStringRef nameRef; | |
302 | CFTypeRef valueRef; | |
303 | kern_return_t result; | |
304 | ||
305 | // OF variable name is first. | |
306 | name = str; | |
307 | ||
308 | // Find the equal sign for set | |
309 | while (*str) { | |
310 | if (*str == '=') { | |
311 | set = 1; | |
312 | *str++ = '\0'; | |
313 | break; | |
314 | } | |
315 | str++; | |
316 | } | |
317 | ||
318 | if (set == 1) { | |
319 | // On sets, the OF variable's value follows the equal sign. | |
320 | value = str; | |
321 | ||
322 | result = SetOFVariable(name, value); | |
323 | if (result != KERN_SUCCESS) { | |
324 | FatalError(-1, "Error (-1) setting variable - '%s'", (long)name); | |
325 | } | |
326 | } else { | |
327 | result = GetOFVariable(name, &nameRef, &valueRef); | |
328 | if (result != KERN_SUCCESS) { | |
329 | FatalError(-1, "Error (-1) getting variable - '%s'", (long)name); | |
330 | } | |
331 | ||
332 | PrintOFVariable(nameRef, valueRef, 0); | |
333 | CFRelease(nameRef); | |
334 | CFRelease(valueRef); | |
335 | } | |
336 | } | |
337 | ||
338 | ||
339 | // GetOFVariable(name, nameRef, valueRef) | |
340 | // | |
4c00c0ae | 341 | // Get the named firmware variable. |
1815bff5 A |
342 | // Return it and it's symbol in valueRef and nameRef. |
343 | // | |
344 | static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef, | |
345 | CFTypeRef *valueRef) | |
346 | { | |
347 | *nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, | |
2fc1e207 | 348 | kCFStringEncodingUTF8); |
1815bff5 | 349 | if (*nameRef == 0) { |
2fc1e207 | 350 | FatalError(-1, "Error (-1) creating CFString for key %s", (long)name); |
1815bff5 A |
351 | } |
352 | ||
353 | *valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, *nameRef, 0, 0); | |
354 | if (*valueRef == 0) return -1; | |
355 | ||
356 | return KERN_SUCCESS; | |
357 | } | |
358 | ||
359 | ||
360 | // SetOFVariable(name, value) | |
361 | // | |
4c00c0ae | 362 | // Set or create an firmware variable with name and value. |
1815bff5 A |
363 | // |
364 | static kern_return_t SetOFVariable(char *name, char *value) | |
365 | { | |
366 | CFStringRef nameRef; | |
367 | CFTypeRef valueRef; | |
368 | CFTypeID typeID; | |
369 | kern_return_t result; | |
370 | ||
371 | nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, | |
2fc1e207 | 372 | kCFStringEncodingUTF8); |
1815bff5 A |
373 | if (nameRef == 0) { |
374 | FatalError(-1, "Error (-1) creating CFString for key %s", (long)name); | |
375 | } | |
376 | ||
377 | valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0); | |
378 | if (valueRef) { | |
379 | typeID = CFGetTypeID(valueRef); | |
380 | CFRelease(valueRef); | |
20e66415 A |
381 | |
382 | valueRef = ConvertValueToCFTypeRef(typeID, value); | |
383 | if (valueRef == 0) { | |
384 | FatalError(-1, "Error (-1) creating CFTypeRef for value %s",(long)value); | |
385 | } result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); | |
386 | } else { | |
387 | while (1) { | |
388 | // In the default case, try data, string, number, then boolean. | |
389 | ||
390 | valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value); | |
391 | if (valueRef != 0) { | |
392 | result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); | |
393 | if (result == KERN_SUCCESS) break; | |
394 | } | |
395 | ||
396 | valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value); | |
397 | if (valueRef != 0) { | |
398 | result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); | |
399 | if (result == KERN_SUCCESS) break; | |
400 | } | |
401 | ||
402 | valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value); | |
403 | if (valueRef != 0) { | |
404 | result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); | |
405 | if (result == KERN_SUCCESS) break; | |
406 | } | |
407 | ||
408 | valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value); | |
409 | if (valueRef != 0) { | |
410 | result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); | |
411 | if (result == KERN_SUCCESS) break; | |
412 | } | |
413 | ||
414 | result = -1; | |
415 | break; | |
416 | } | |
1815bff5 A |
417 | } |
418 | ||
1815bff5 A |
419 | CFRelease(nameRef); |
420 | ||
421 | return result; | |
422 | } | |
423 | ||
424 | ||
83f6dbe8 A |
425 | // DeleteOFVariable(name) |
426 | // | |
4c00c0ae | 427 | // Delete the named firmware variable. |
83f6dbe8 A |
428 | // |
429 | // | |
430 | static void DeleteOFVariable(char *name) | |
431 | { | |
432 | SetOFVariable(kIONVRAMDeletePropertyKey, name); | |
433 | } | |
434 | ||
435 | ||
1815bff5 A |
436 | // PrintOFVariables() |
437 | // | |
4c00c0ae | 438 | // Print all of the firmware variables. |
1815bff5 A |
439 | // |
440 | static void PrintOFVariables() | |
441 | { | |
442 | kern_return_t result; | |
443 | CFMutableDictionaryRef dict; | |
444 | ||
445 | result = IORegistryEntryCreateCFProperties(gOptionsRef, &dict, 0, 0); | |
446 | if (result != KERN_SUCCESS) { | |
4c00c0ae | 447 | FatalError(-1, "Error (%d) getting the firmware variables", result); |
1815bff5 A |
448 | } |
449 | CFDictionaryApplyFunction(dict, &PrintOFVariable, 0); | |
450 | ||
451 | CFRelease(dict); | |
452 | } | |
453 | ||
454 | ||
455 | // PrintOFVariable(key, value, context) | |
456 | // | |
4c00c0ae | 457 | // Print the given firmware variable. |
1815bff5 A |
458 | // |
459 | static void PrintOFVariable(const void *key, const void *value, void *context) | |
460 | { | |
461 | long cnt, cnt2; | |
2fc1e207 A |
462 | CFIndex nameLen; |
463 | char *nameBuffer = 0; | |
1815bff5 A |
464 | const char *nameString; |
465 | char numberBuffer[10]; | |
b51d5b5f A |
466 | const uint8_t *dataPtr; |
467 | uint8_t dataChar; | |
1815bff5 | 468 | char *dataBuffer = 0; |
2fc1e207 A |
469 | CFIndex valueLen; |
470 | char *valueBuffer = 0; | |
b51d5b5f A |
471 | const char *valueString = 0; |
472 | uint32_t number, length; | |
1815bff5 A |
473 | CFTypeID typeID; |
474 | ||
475 | // Get the OF variable's name. | |
2fc1e207 A |
476 | nameLen = CFStringGetLength(key) + 1; |
477 | nameBuffer = malloc(nameLen); | |
478 | if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) ) | |
479 | nameString = nameBuffer; | |
480 | else { | |
481 | Error("Error (-1) Unable to convert property name to C string", 0); | |
482 | nameString = "<UNPRINTABLE>"; | |
483 | } | |
1815bff5 A |
484 | |
485 | // Get the OF variable's type. | |
486 | typeID = CFGetTypeID(value); | |
487 | ||
2fc1e207 | 488 | if (typeID == CFBooleanGetTypeID()) { |
1815bff5 A |
489 | if (CFBooleanGetValue(value)) valueString = "true"; |
490 | else valueString = "false"; | |
491 | } else if (typeID == CFNumberGetTypeID()) { | |
492 | CFNumberGetValue(value, kCFNumberSInt32Type, &number); | |
493 | if (number == 0xFFFFFFFF) sprintf(numberBuffer, "-1"); | |
494 | else if (number < 1000) sprintf(numberBuffer, "%d", number); | |
495 | else sprintf(numberBuffer, "0x%x", number); | |
496 | valueString = numberBuffer; | |
497 | } else if (typeID == CFStringGetTypeID()) { | |
2fc1e207 A |
498 | valueLen = CFStringGetLength(value) + 1; |
499 | valueBuffer = malloc(valueLen + 1); | |
500 | if ( valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8) ) | |
501 | valueString = valueBuffer; | |
502 | else { | |
503 | Error("Error (-1) Unable to convert value to C string", 0); | |
504 | valueString = "<UNPRINTABLE>"; | |
505 | } | |
1815bff5 A |
506 | } else if (typeID == CFDataGetTypeID()) { |
507 | length = CFDataGetLength(value); | |
508 | if (length == 0) valueString = ""; | |
509 | else { | |
510 | dataBuffer = malloc(length * 3 + 1); | |
511 | if (dataBuffer != 0) { | |
512 | dataPtr = CFDataGetBytePtr(value); | |
513 | for (cnt = cnt2 = 0; cnt < length; cnt++) { | |
514 | dataChar = dataPtr[cnt]; | |
515 | if (isprint(dataChar)) dataBuffer[cnt2++] = dataChar; | |
516 | else { | |
517 | sprintf(dataBuffer + cnt2, "%%%02x", dataChar); | |
518 | cnt2 += 3; | |
519 | } | |
520 | } | |
521 | dataBuffer[cnt2] = '\0'; | |
522 | valueString = dataBuffer; | |
523 | } | |
524 | } | |
2fc1e207 A |
525 | } else { |
526 | valueString="<INVALID>"; | |
527 | } | |
1815bff5 A |
528 | |
529 | if ((nameString != 0) && (valueString != 0)) | |
530 | printf("%s\t%s\n", nameString, valueString); | |
531 | ||
532 | if (dataBuffer != 0) free(dataBuffer); | |
2fc1e207 A |
533 | if (nameBuffer != 0) free(nameBuffer); |
534 | if (valueBuffer != 0) free(valueBuffer); | |
1815bff5 A |
535 | } |
536 | ||
537 | ||
538 | // ConvertValueToCFTypeRef(typeID, value) | |
539 | // | |
540 | // Convert the value into a CFType given the typeID. | |
541 | // | |
542 | static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value) | |
543 | { | |
544 | CFTypeRef valueRef = 0; | |
545 | long cnt, cnt2, length; | |
546 | unsigned long number, tmp; | |
547 | ||
548 | if (typeID == CFBooleanGetTypeID()) { | |
549 | if (!strcmp("true", value)) valueRef = kCFBooleanTrue; | |
550 | else if (!strcmp("false", value)) valueRef = kCFBooleanFalse; | |
551 | } else if (typeID == CFNumberGetTypeID()) { | |
552 | number = strtol(value, 0, 0); | |
553 | valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, | |
554 | &number); | |
555 | } else if (typeID == CFStringGetTypeID()) { | |
556 | valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, | |
2fc1e207 | 557 | kCFStringEncodingUTF8); |
1815bff5 A |
558 | } else if (typeID == CFDataGetTypeID()) { |
559 | length = strlen(value); | |
560 | for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) { | |
561 | if (value[cnt] == '%') { | |
562 | if (!ishexnumber(value[cnt + 1]) || | |
563 | !ishexnumber(value[cnt + 2])) return 0; | |
564 | number = toupper(value[++cnt]) - '0'; | |
565 | if (number > 9) number -= 7; | |
566 | tmp = toupper(value[++cnt]) - '0'; | |
567 | if (tmp > 9) tmp -= 7; | |
568 | number = (number << 4) + tmp; | |
569 | value[cnt2] = number; | |
570 | } else value[cnt2] = value[cnt]; | |
571 | } | |
572 | valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, value, | |
573 | cnt2, kCFAllocatorDefault); | |
574 | } else return 0; | |
575 | ||
576 | return valueRef; | |
577 | } | |
09fd88e4 | 578 |