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