1 #include <IOKit/IOKitLib.h>
2 #include <CoreFoundation/CoreFoundation.h>
3 #include <darwintest.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include "nvram_helper.h"
8
9 // Ascii value of 'A' (65) - Ascii value of '9' (57)
10 #define ASCII_OFFSET 7
11 #define NVRAM_BYTE_LEN 3
12
13 // NVRAM helper functions from https://stashweb.sd.apple.com/projects/COREOS/repos/system_cmds/browse/nvram.tproj/nvram.c
14
15 /**
16 * @brief Print the given firmware variable.
17 */
18 static void
PrintVariable(const void * key,const void * value,void * context)19 PrintVariable(const void *key, const void *value, void *context)
20 {
21 if (CFGetTypeID(key) != CFStringGetTypeID()) {
22 printf("Variable name passed in isn't a string");
23 return;
24 }
25 long cnt, cnt2;
26 CFIndex nameLen;
27 char *nameBuffer = 0;
28 const char *nameString;
29 char numberBuffer[10];
30 const uint8_t *dataPtr;
31 uint8_t dataChar;
32 char *dataBuffer = 0;
33 CFIndex valueLen;
34 char *valueBuffer = 0;
35 const char *valueString = 0;
36 uint32_t number;
37 long length;
38 CFTypeID typeID;
39 // Get the variable's name.
40 nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), kCFStringEncodingUTF8) + 1;
41
42 nameBuffer = malloc(nameLen);
43
44 if (nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8)) {
45 nameString = nameBuffer;
46 } else {
47 printf("Unable to convert property name to C string");
48 nameString = "<UNPRINTABLE>";
49 }
50
51 // Get the variable's type.
52 typeID = CFGetTypeID(value);
53
54 if (typeID == CFBooleanGetTypeID()) {
55 if (CFBooleanGetValue(value)) {
56 valueString = "true";
57 } else {
58 valueString = "false";
59 }
60 } else if (typeID == CFNumberGetTypeID()) {
61 CFNumberGetValue(value, kCFNumberSInt32Type, &number);
62 if (number == 0xFFFFFFFF) {
63 sprintf(numberBuffer, "-1");
64 } else {
65 sprintf(numberBuffer, "0x%x", number);
66 }
67 valueString = numberBuffer;
68 } else if (typeID == CFStringGetTypeID()) {
69 valueLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value),
70 kCFStringEncodingUTF8) +
71 1;
72 valueBuffer = malloc(valueLen + 1);
73 if (valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8)) {
74 valueString = valueBuffer;
75 } else {
76 printf("Unable to convert value to C string");
77 valueString = "<UNPRINTABLE>";
78 }
79 } else if (typeID == CFDataGetTypeID()) {
80 length = CFDataGetLength(value);
81 if (length == 0) {
82 valueString = "";
83 } else {
84 dataBuffer = malloc(length * NVRAM_BYTE_LEN + NVRAM_BYTE_LEN);
85 if (dataBuffer != 0) {
86 dataPtr = CFDataGetBytePtr(value);
87 cnt = cnt2 = 0;
88 for (; cnt < length; cnt++) {
89 dataChar = dataPtr[cnt];
90 if (isprint(dataChar) && dataChar != '%') {
91 dataBuffer[cnt2++] = dataChar;
92 } else {
93 sprintf(dataBuffer + cnt2, "%%%02x", dataChar);
94 cnt2 += NVRAM_BYTE_LEN;
95 }
96 }
97 dataBuffer[cnt2] = '\0';
98 valueString = dataBuffer;
99 }
100 }
101 } else {
102 valueString = "<INVALID>";
103 }
104
105 if ((nameString != 0) && (valueString != 0)) {
106 printf("%s\t%s\n", nameString, valueString);
107 }
108
109 if (dataBuffer != 0) {
110 free(dataBuffer);
111 }
112 if (nameBuffer != 0) {
113 free(nameBuffer);
114 }
115 if (valueBuffer != 0) {
116 free(valueBuffer);
117 }
118 }
119
120 /**
121 * @brief Convert the value into a CFType given the typeID
122 */
123 static CFTypeRef
ConvertValueToCFTypeRef(CFTypeID typeID,const char * value)124 ConvertValueToCFTypeRef(CFTypeID typeID, const char *value)
125 {
126 CFTypeRef valueRef = 0;
127 long cnt, cnt2, length;
128 unsigned long number, tmp;
129
130 if (typeID == CFBooleanGetTypeID()) {
131 if (value == NULL) {
132 return valueRef;
133 }
134 if (!strcmp("true", value)) {
135 valueRef = kCFBooleanTrue;
136 } else if (!strcmp("false", value)) {
137 valueRef = kCFBooleanFalse;
138 }
139 } else if (typeID == CFNumberGetTypeID()) {
140 number = strtol(value, 0, 0);
141 valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
142 &number);
143 } else if (typeID == CFStringGetTypeID()) {
144 valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value,
145 kCFStringEncodingUTF8);
146 } else if (typeID == CFDataGetTypeID()) {
147 if (value == NULL) {
148 length = 0;
149 } else {
150 length = strlen(value);
151 }
152
153 char valueCopy[length + 1];
154
155 for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) {
156 if (value[cnt] == '%') {
157 if ((cnt + 2 > length) ||
158 !ishexnumber(value[cnt + 1]) ||
159 !ishexnumber(value[cnt + 2])) {
160 return 0;
161 }
162 number = toupper(value[++cnt]) - '0';
163 if (number > 9) {
164 number -= ASCII_OFFSET;
165 }
166 tmp = toupper(value[++cnt]) - '0';
167 if (tmp > 9) {
168 tmp -= ASCII_OFFSET;
169 }
170 number = (number << 4) + tmp;
171 valueCopy[cnt2] = number;
172 } else {
173 valueCopy[cnt2] = value[cnt];
174 }
175 }
176 valueRef = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)valueCopy, cnt2);
177 } else {
178 return 0;
179 }
180
181 return valueRef;
182 }
183
184 /**
185 * @brief Prints the variable. returns kIOReturnNotFound if not found
186 */
187 kern_return_t
GetVariable(const char * name,io_registry_entry_t optionsRef)188 GetVariable(const char *name, io_registry_entry_t optionsRef)
189 {
190 CFStringRef nameRef = NULL;
191 CFTypeRef valueRef = NULL;
192 nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
193 kCFStringEncodingUTF8);
194 if (nameRef == 0) {
195 printf("Error creating CFString for key %s", name);
196 return KERN_FAILURE;
197 }
198
199 valueRef = IORegistryEntryCreateCFProperty(optionsRef, nameRef, 0, 0);
200 if (valueRef == 0) {
201 return kIOReturnNotFound;
202 }
203
204 PrintVariable(nameRef, valueRef, 0);
205
206 return KERN_SUCCESS;
207 }
208
209 /**
210 * @brief Set the named variable with the value passed in
211 */
212 kern_return_t
SetVariable(const char * name,const char * value,io_registry_entry_t optionsRef)213 SetVariable(const char *name, const char *value, io_registry_entry_t optionsRef)
214 {
215 CFStringRef nameRef;
216 CFTypeRef valueRef;
217 CFTypeID typeID;
218 kern_return_t result = KERN_FAILURE;
219
220 nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
221 kCFStringEncodingUTF8);
222 if (nameRef == 0) {
223 printf("Error creating CFString for key %s", name);
224 return result;
225 }
226
227 valueRef = IORegistryEntryCreateCFProperty(optionsRef, nameRef, 0, 0);
228 if (valueRef) {
229 typeID = CFGetTypeID(valueRef);
230 CFRelease(valueRef);
231 valueRef = ConvertValueToCFTypeRef(typeID, value);
232 if (valueRef == 0) {
233 printf("Error creating CFTypeRef for value %s", value);
234 return result;
235 }
236 result = IORegistryEntrySetCFProperty(optionsRef, nameRef, valueRef);
237 } else {
238 // skip testing different CFTypeIDs if there is no entry and it's a delete operation.
239 if (value == NULL) {
240 return result;
241 }
242 // In the default case, try data, string, number, then boolean.
243 CFTypeID types[] = {CFDataGetTypeID(),
244 CFStringGetTypeID(), CFNumberGetTypeID(), CFBooleanGetTypeID()};
245 for (int i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
246 valueRef = ConvertValueToCFTypeRef(types[i], value);
247 if (valueRef != 0) {
248 result = IORegistryEntrySetCFProperty(optionsRef, nameRef, valueRef);
249 if (result == KERN_SUCCESS || result == kIOReturnNoMemory || result == kIOReturnNoSpace) {
250 break;
251 }
252 }
253 }
254 }
255
256 CFRelease(nameRef);
257
258 return result;
259 }
260
261 /**
262 * @brief Delete named variable
263 */
264 kern_return_t
DeleteVariable(const char * name,io_registry_entry_t optionsRef)265 DeleteVariable(const char *name, io_registry_entry_t optionsRef)
266 {
267 // Since delete always returns ok, read to make sure it is deleted.
268 if (SetVariable(kIONVRAMDeletePropertyKey, name, optionsRef) == KERN_SUCCESS) {
269 if (GetVariable(name, optionsRef) == kIOReturnNotFound) {
270 return KERN_SUCCESS;
271 }
272 }
273 return KERN_FAILURE;
274 }
275
276 /**
277 * @brief Reset NVram
278 */
279 kern_return_t
ResetNVram(io_registry_entry_t optionsRef)280 ResetNVram(io_registry_entry_t optionsRef)
281 {
282 if (SetVariable("ResetNVRam", "1", optionsRef) == KERN_SUCCESS) {
283 return KERN_SUCCESS;
284 }
285 return KERN_FAILURE;
286 }
287
288 /**
289 * @brief Get the Options object
290 */
291 io_registry_entry_t
GetOptions(void)292 GetOptions(void)
293 {
294 io_registry_entry_t optionsRef = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
295 T_ASSERT_NE(optionsRef, IO_OBJECT_NULL, "got options");
296 return optionsRef;
297 }
298
299 /**
300 * @brief Release option object passed in
301 */
302 void
ReleaseOptions(io_registry_entry_t optionsRef)303 ReleaseOptions(io_registry_entry_t optionsRef)
304 {
305 if (optionsRef != IO_OBJECT_NULL) {
306 IOObjectRelease(optionsRef);
307 }
308 }
309