1/*
2 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
4 *
5 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. The rights granted to you under the License
11 * may not be used to create, or enable the creation or redistribution of,
12 * unlawful or unlicensed copies of an Apple operating system, or to
13 * circumvent, violate, or enable the circumvention or violation of, any
14 * terms of an Apple operating system software license agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 *
19 * The Original Code and all software distributed under the License are
20 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
24 * Please see the License for the specific language governing rights and
25 * limitations under the License.
26 *
27 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 */
29
30#include <IOKit/IOLib.h>
31#include <IOKit/IONVRAM.h>
32#include <IOKit/IOPlatformExpert.h>
33#include <IOKit/IOUserClient.h>
34#include <IOKit/IOKitKeys.h>
35#include <kern/debug.h>
36#include <pexpert/pexpert.h>
37
38#define super IOService
39
40#define kIONVRAMPrivilege	kIOClientPrivilegeAdministrator
41//#define kIONVRAMPrivilege	kIOClientPrivilegeLocalUser
42
43OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
44
45bool IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
46{
47  OSDictionary *dict;
48
49  if (!super::init(old, plane)) return false;
50
51  dict =  OSDictionary::withCapacity(1);
52  if (dict == 0) return false;
53  setPropertyTable(dict);
54
55  _nvramImage = IONew(UInt8, kIODTNVRAMImageSize);
56  if (_nvramImage == 0) return false;
57
58  _nvramPartitionOffsets = OSDictionary::withCapacity(1);
59  if (_nvramPartitionOffsets == 0) return false;
60
61  _nvramPartitionLengths = OSDictionary::withCapacity(1);
62  if (_nvramPartitionLengths == 0) return false;
63
64  _registryPropertiesKey = OSSymbol::withCStringNoCopy("aapl,pci");
65  if (_registryPropertiesKey == 0) return false;
66
67  // <rdar://problem/9529235> race condition possible between
68  // IODTNVRAM and IONVRAMController (restore loses boot-args)
69  initProxyData();
70
71  return true;
72}
73
74void IODTNVRAM::initProxyData(void)
75{
76  IORegistryEntry *entry;
77  const char *key = "nvram-proxy-data";
78  OSObject *prop;
79  OSData *data;
80  const void *bytes;
81
82  entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
83  if (entry != 0) {
84    prop = entry->getProperty(key);
85    if (prop != 0) {
86      data = OSDynamicCast(OSData, prop);
87      if (data != 0) {
88        bytes = data->getBytesNoCopy();
89        if (bytes != 0) {
90          bcopy(bytes, _nvramImage, data->getLength());
91          initNVRAMImage();
92          _isProxied = true;
93        }
94      }
95    }
96    entry->removeProperty(key);
97    entry->release();
98  }
99}
100
101void IODTNVRAM::registerNVRAMController(IONVRAMController *nvram)
102{
103  if (_nvramController != 0) return;
104
105  _nvramController = nvram;
106
107  // <rdar://problem/9529235> race condition possible between
108  // IODTNVRAM and IONVRAMController (restore loses boot-args)
109  if (!_isProxied) {
110    _nvramController->read(0, _nvramImage, kIODTNVRAMImageSize);
111    initNVRAMImage();
112  }
113}
114
115void IODTNVRAM::initNVRAMImage(void)
116{
117  char   partitionID[18];
118  UInt32 partitionOffset, partitionLength;
119  UInt32 freePartitionOffset, freePartitionSize;
120  UInt32 currentLength, currentOffset = 0;
121  OSNumber *partitionOffsetNumber, *partitionLengthNumber;
122
123  // Find the offsets for the OF, XPRAM, NameRegistry and PanicInfo partitions.
124  _ofPartitionOffset = 0xFFFFFFFF;
125  _xpramPartitionOffset = 0xFFFFFFFF;
126  _nrPartitionOffset = 0xFFFFFFFF;
127  _piPartitionOffset = 0xFFFFFFFF;
128  freePartitionOffset = 0xFFFFFFFF;
129  freePartitionSize = 0;
130  if (getPlatform()->getBootROMType()) {
131    // Look through the partitions to find the OF, MacOS partitions.
132    while (currentOffset < kIODTNVRAMImageSize) {
133      currentLength = ((UInt16 *)(_nvramImage + currentOffset))[1] * 16;
134
135      partitionOffset = currentOffset + 16;
136      partitionLength = currentLength - 16;
137
138      if (strncmp((const char *)_nvramImage + currentOffset + 4,
139		  kIODTNVRAMOFPartitionName, 12) == 0) {
140	_ofPartitionOffset = partitionOffset;
141	_ofPartitionSize = partitionLength;
142      } else if (strncmp((const char *)_nvramImage + currentOffset + 4,
143			 kIODTNVRAMXPRAMPartitionName, 12) == 0) {
144	_xpramPartitionOffset = partitionOffset;
145	_xpramPartitionSize = kIODTNVRAMXPRAMSize;
146	_nrPartitionOffset = _xpramPartitionOffset + _xpramPartitionSize;
147	_nrPartitionSize = partitionLength - _xpramPartitionSize;
148      } else if (strncmp((const char *)_nvramImage + currentOffset + 4,
149			 kIODTNVRAMPanicInfoPartitonName, 12) == 0) {
150	_piPartitionOffset = partitionOffset;
151	_piPartitionSize = partitionLength;
152      } else if (strncmp((const char *)_nvramImage + currentOffset + 4,
153			 kIODTNVRAMFreePartitionName, 12) == 0) {
154	freePartitionOffset = currentOffset;
155	freePartitionSize = currentLength;
156      } else {
157	// Construct the partition ID from the signature and name.
158	snprintf(partitionID, sizeof(partitionID), "0x%02x,",
159		*(UInt8 *)(_nvramImage + currentOffset));
160	strncpy(partitionID + 5,
161		(const char *)(_nvramImage + currentOffset + 4), 12);
162	partitionID[17] = '\0';
163
164	partitionOffsetNumber = OSNumber::withNumber(partitionOffset, 32);
165	partitionLengthNumber = OSNumber::withNumber(partitionLength, 32);
166
167	// Save the partition offset and length
168	_nvramPartitionOffsets->setObject(partitionID, partitionOffsetNumber);
169	_nvramPartitionLengths->setObject(partitionID, partitionLengthNumber);
170
171	partitionOffsetNumber->release();
172	partitionLengthNumber->release();
173      }
174      currentOffset += currentLength;
175    }
176  } else {
177    // Use the fixed address for old world machines.
178    _ofPartitionOffset    = 0x1800;
179    _ofPartitionSize      = 0x0800;
180    _xpramPartitionOffset = 0x1300;
181    _xpramPartitionSize   = 0x0100;
182    _nrPartitionOffset    = 0x1400;
183    _nrPartitionSize      = 0x0400;
184  }
185
186  if (_ofPartitionOffset != 0xFFFFFFFF)
187    _ofImage    = _nvramImage + _ofPartitionOffset;
188  if (_xpramPartitionOffset != 0xFFFFFFFF)
189    _xpramImage = _nvramImage + _xpramPartitionOffset;
190  if (_nrPartitionOffset != 0xFFFFFFFF)
191    _nrImage    = _nvramImage + _nrPartitionOffset;
192
193  if (_piPartitionOffset == 0xFFFFFFFF) {
194    if (freePartitionSize > 0x20) {
195      // Set the signature to 0xa1.
196      _nvramImage[freePartitionOffset] = 0xa1;
197      // Set the checksum to 0.
198      _nvramImage[freePartitionOffset + 1] = 0;
199      // Set the name for the Panic Info partition.
200      strncpy((char *)(_nvramImage + freePartitionOffset + 4),
201	      kIODTNVRAMPanicInfoPartitonName, 12);
202
203      // Calculate the partition offset and size.
204      _piPartitionOffset = freePartitionOffset + 0x10;
205      _piPartitionSize = 0x800;
206      if (_piPartitionSize + 0x20 > freePartitionSize)
207	_piPartitionSize = freePartitionSize - 0x20;
208
209      _piImage = _nvramImage + _piPartitionOffset;
210
211      // Zero the new partition.
212      bzero(_piImage, _piPartitionSize);
213
214      // Set the partition size.
215      *(UInt16 *)(_nvramImage + freePartitionOffset + 2) =
216	(_piPartitionSize / 0x10) + 1;
217
218      // Set the partition checksum.
219      _nvramImage[freePartitionOffset + 1] =
220	calculatePartitionChecksum(_nvramImage + freePartitionOffset);
221
222      // Calculate the free partition offset and size.
223      freePartitionOffset += _piPartitionSize + 0x10;
224      freePartitionSize -= _piPartitionSize + 0x10;
225
226      // Set the signature to 0x7f.
227      _nvramImage[freePartitionOffset] = 0x7f;
228      // Set the checksum to 0.
229      _nvramImage[freePartitionOffset + 1] = 0;
230      // Set the name for the free partition.
231      strncpy((char *)(_nvramImage + freePartitionOffset + 4),
232	      kIODTNVRAMFreePartitionName, 12);
233      // Set the partition size.
234      *(UInt16 *)(_nvramImage + freePartitionOffset + 2) =
235	freePartitionSize / 0x10;
236      // Set the partition checksum.
237      _nvramImage[freePartitionOffset + 1] =
238	calculatePartitionChecksum(_nvramImage + freePartitionOffset);
239
240      // Set the nvram image as dirty.
241      _nvramImageDirty = true;
242    }
243  } else {
244    _piImage = _nvramImage + _piPartitionOffset;
245  }
246
247  _lastDeviceSync = 0;
248  _freshInterval = TRUE;		// we will allow sync() even before the first 15 minutes have passed.
249
250  initOFVariables();
251}
252
253void IODTNVRAM::sync(void)
254{
255  if (!_nvramImageDirty && !_ofImageDirty) return;
256
257  // Don't try to sync OF Variables if the system has already paniced.
258  if (!_systemPaniced) syncOFVariables();
259
260  // Don't try to perform controller operations if none has been registered.
261  if (_nvramController == 0) return;
262
263  _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
264  _nvramController->sync();
265
266  _nvramImageDirty = false;
267}
268
269bool IODTNVRAM::serializeProperties(OSSerialize *s) const
270{
271  bool                 result, hasPrivilege;
272  UInt32               variablePerm;
273  const OSSymbol       *key;
274  OSDictionary         *dict;
275  OSCollectionIterator *iter = 0;
276
277  // Verify permissions.
278  hasPrivilege = (kIOReturnSuccess == IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege));
279
280  dict = OSDictionary::withCapacity(1);
281  if (dict == 0) return false;
282
283  if (_ofDict == 0) {
284    /* No nvram. Return an empty dictionary. */
285  } else {
286    /* Copy properties with client privilege. */
287    iter = OSCollectionIterator::withCollection(_ofDict);
288    if (iter == 0) {
289      dict->release();
290      return false;
291    }
292    while (1) {
293      key = OSDynamicCast(OSSymbol, iter->getNextObject());
294      if (key == 0) break;
295
296      variablePerm = getOFVariablePerm(key);
297      if ((hasPrivilege || (variablePerm != kOFVariablePermRootOnly)) &&
298	  ( ! (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) )) {
299	dict->setObject(key, _ofDict->getObject(key));
300      }
301    }
302  }
303
304  result = dict->serialize(s);
305
306  dict->release();
307  if (iter != 0) iter->release();
308
309  return result;
310}
311
312OSObject *IODTNVRAM::getProperty(const OSSymbol *aKey) const
313{
314  IOReturn result;
315  UInt32   variablePerm;
316
317  if (_ofDict == 0) return 0;
318
319  // Verify permissions.
320  variablePerm = getOFVariablePerm(aKey);
321  result = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege);
322  if (result != kIOReturnSuccess) {
323    if (variablePerm == kOFVariablePermRootOnly) return 0;
324  }
325  if (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return 0;
326
327  return _ofDict->getObject(aKey);
328}
329
330OSObject *IODTNVRAM::getProperty(const char *aKey) const
331{
332  const OSSymbol *keySymbol;
333  OSObject *theObject = 0;
334
335  keySymbol = OSSymbol::withCStringNoCopy(aKey);
336  if (keySymbol != 0) {
337    theObject = getProperty(keySymbol);
338    keySymbol->release();
339  }
340
341  return theObject;
342}
343
344bool IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
345{
346  bool     result;
347  UInt32   propType, propPerm;
348  OSString *tmpString;
349  OSObject *propObject = 0;
350
351  if (_ofDict == 0) return false;
352
353  // Verify permissions.
354  propPerm = getOFVariablePerm(aKey);
355  result = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege);
356  if (result != kIOReturnSuccess) {
357    if (propPerm != kOFVariablePermUserWrite) return false;
358  }
359  if (propPerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return 0;
360
361  // Don't allow creation of new properties on old world machines.
362  if (getPlatform()->getBootROMType() == 0) {
363    if (_ofDict->getObject(aKey) == 0) return false;
364  }
365
366  // Don't allow change of 'aapl,panic-info'.
367  if (aKey->isEqualTo(kIODTNVRAMPanicInfoKey)) return false;
368
369  // Make sure the object is of the correct type.
370  propType = getOFVariableType(aKey);
371  switch (propType) {
372  case kOFVariableTypeBoolean :
373    propObject = OSDynamicCast(OSBoolean, anObject);
374    break;
375
376  case kOFVariableTypeNumber :
377    propObject = OSDynamicCast(OSNumber, anObject);
378    break;
379
380  case kOFVariableTypeString :
381    propObject = OSDynamicCast(OSString, anObject);
382    break;
383
384  case kOFVariableTypeData :
385    propObject = OSDynamicCast(OSData, anObject);
386    if (propObject == 0) {
387      tmpString = OSDynamicCast(OSString, anObject);
388      if (tmpString != 0) {
389	propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
390				       tmpString->getLength());
391      }
392    }
393    break;
394  }
395
396  if (propObject == 0) return false;
397
398  result = _ofDict->setObject(aKey, propObject);
399
400  if (result) {
401    if (getPlatform()->getBootROMType() == 0) {
402      updateOWBootArgs(aKey, propObject);
403    }
404
405    _ofImageDirty = true;
406  }
407
408  return result;
409}
410
411void IODTNVRAM::removeProperty(const OSSymbol *aKey)
412{
413  bool     result;
414  UInt32   propPerm;
415
416  if (_ofDict == 0) return;
417
418  // Verify permissions.
419  propPerm = getOFVariablePerm(aKey);
420  result = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator);
421  if (result != kIOReturnSuccess) {
422    if (propPerm != kOFVariablePermUserWrite) return;
423  }
424  if (propPerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return;
425
426  // Don't allow removal of properties on old world machines.
427  if (getPlatform()->getBootROMType() == 0) return;
428
429  // Don't allow change of 'aapl,panic-info'.
430  if (aKey->isEqualTo(kIODTNVRAMPanicInfoKey)) return;
431
432  // If the object exists, remove it from the dictionary.
433  result = _ofDict->getObject(aKey) != 0;
434  if (result) {
435    _ofDict->removeObject(aKey);
436
437    _ofImageDirty = true;
438  }
439}
440
441IOReturn IODTNVRAM::setProperties(OSObject *properties)
442{
443  bool                 result = true;
444  OSObject             *object;
445  const OSSymbol       *key;
446  const OSString       *tmpStr;
447  OSDictionary         *dict;
448  OSCollectionIterator *iter;
449
450  dict = OSDynamicCast(OSDictionary, properties);
451  if (dict == 0) return kIOReturnBadArgument;
452
453  iter = OSCollectionIterator::withCollection(dict);
454  if (iter == 0) return kIOReturnBadArgument;
455
456  while (result) {
457    key = OSDynamicCast(OSSymbol, iter->getNextObject());
458    if (key == 0) break;
459
460    object = dict->getObject(key);
461    if (object == 0) continue;
462
463    if (key->isEqualTo(kIONVRAMDeletePropertyKey)) {
464		tmpStr = OSDynamicCast(OSString, object);
465		if (tmpStr != 0) {
466			key = OSSymbol::withString(tmpStr);
467			removeProperty(key);
468			key->release();
469			result = true;
470		} else {
471			result = false;
472		}
473    } else if(key->isEqualTo(kIONVRAMSyncNowPropertyKey)) {
474		tmpStr = OSDynamicCast(OSString, object);
475		if (tmpStr != 0) {
476
477			result = true; // We are not going to gaurantee sync, this is best effort
478
479			if(safeToSync())
480				sync();
481
482		} else {
483			result = false;
484		}
485	}
486	else {
487		result = setProperty(key, object);
488    }
489
490  }
491
492  iter->release();
493
494  if (result) return kIOReturnSuccess;
495  else return kIOReturnError;
496}
497
498IOReturn IODTNVRAM::readXPRAM(IOByteCount offset, UInt8 *buffer,
499			      IOByteCount length)
500{
501  if (_xpramImage == 0) return kIOReturnUnsupported;
502
503  if ((buffer == 0) || (length == 0) ||
504      (offset + length > kIODTNVRAMXPRAMSize))
505    return kIOReturnBadArgument;
506
507  bcopy(_nvramImage + _xpramPartitionOffset + offset, buffer, length);
508
509  return kIOReturnSuccess;
510}
511
512IOReturn IODTNVRAM::writeXPRAM(IOByteCount offset, UInt8 *buffer,
513			       IOByteCount length)
514{
515  if (_xpramImage == 0) return kIOReturnUnsupported;
516
517  if ((buffer == 0) || (length == 0) ||
518      (offset + length > kIODTNVRAMXPRAMSize))
519    return kIOReturnBadArgument;
520
521  bcopy(buffer, _nvramImage + _xpramPartitionOffset + offset, length);
522
523  _nvramImageDirty = true;
524
525  return kIOReturnSuccess;
526}
527
528IOReturn IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
529				      const OSSymbol **name,
530				      OSData **value)
531{
532  IOReturn err;
533
534  if (getPlatform()->getBootROMType())
535    err = readNVRAMPropertyType1(entry, name, value);
536  else
537    err = readNVRAMPropertyType0(entry, name, value);
538
539  return err;
540}
541
542IOReturn IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
543				       const OSSymbol *name,
544				       OSData *value)
545{
546  IOReturn err;
547
548  if (getPlatform()->getBootROMType())
549    err = writeNVRAMPropertyType1(entry, name, value);
550  else
551    err = writeNVRAMPropertyType0(entry, name, value);
552
553  return err;
554}
555
556OSDictionary *IODTNVRAM::getNVRAMPartitions(void)
557{
558  return _nvramPartitionLengths;
559}
560
561IOReturn IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
562				       IOByteCount offset, UInt8 *buffer,
563				       IOByteCount length)
564{
565  OSNumber *partitionOffsetNumber, *partitionLengthNumber;
566  UInt32   partitionOffset, partitionLength;
567
568  partitionOffsetNumber =
569    (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
570  partitionLengthNumber =
571    (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
572
573  if ((partitionOffsetNumber == 0) || (partitionLengthNumber == 0))
574    return kIOReturnNotFound;
575
576  partitionOffset = partitionOffsetNumber->unsigned32BitValue();
577  partitionLength = partitionLengthNumber->unsigned32BitValue();
578
579  if ((buffer == 0) || (length == 0) ||
580      (offset + length > partitionLength))
581    return kIOReturnBadArgument;
582
583  bcopy(_nvramImage + partitionOffset + offset, buffer, length);
584
585  return kIOReturnSuccess;
586}
587
588IOReturn IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
589					IOByteCount offset, UInt8 *buffer,
590					IOByteCount length)
591{
592  OSNumber *partitionOffsetNumber, *partitionLengthNumber;
593  UInt32   partitionOffset, partitionLength;
594
595  partitionOffsetNumber =
596    (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
597  partitionLengthNumber =
598    (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
599
600  if ((partitionOffsetNumber == 0) || (partitionLengthNumber == 0))
601    return kIOReturnNotFound;
602
603  partitionOffset = partitionOffsetNumber->unsigned32BitValue();
604  partitionLength = partitionLengthNumber->unsigned32BitValue();
605
606  if ((buffer == 0) || (length == 0) ||
607      (offset + length > partitionLength))
608    return kIOReturnBadArgument;
609
610  bcopy(buffer, _nvramImage + partitionOffset + offset, length);
611
612  _nvramImageDirty = true;
613
614  return kIOReturnSuccess;
615}
616
617IOByteCount IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length)
618{
619  if ((_piImage == 0) || (length <= 0)) return 0;
620
621  if (length > (_piPartitionSize - 4))
622    length = _piPartitionSize - 4;
623
624  // Save the Panic Info.
625  bcopy(buffer, _piImage + 4, length);
626
627  // Save the Panic Info length.
628  *(UInt32 *)_piImage = length;
629
630  _nvramImageDirty = true;
631  /*
632   * This prevents OF variables from being committed if the system has panicked
633   */
634  _systemPaniced = true;
635  /* The call to sync() forces the NVRAM controller to write the panic info
636   * partition to NVRAM.
637   */
638  sync();
639
640  return length;
641}
642
643// Private methods
644
645UInt8 IODTNVRAM::calculatePartitionChecksum(UInt8 *partitionHeader)
646{
647  UInt8 cnt, isum, csum = 0;
648
649  for (cnt = 0; cnt < 0x10; cnt++) {
650    isum = csum + partitionHeader[cnt];
651    if (isum < csum) isum++;
652    csum = isum;
653  }
654
655  return csum;
656}
657
658struct OWVariablesHeader {
659  UInt16   owMagic;
660  UInt8    owVersion;
661  UInt8    owPages;
662  UInt16   owChecksum;
663  UInt16   owHere;
664  UInt16   owTop;
665  UInt16   owNext;
666  UInt32   owFlags;
667  UInt32   owNumbers[9];
668  struct {
669    UInt16 offset;
670    UInt16 length;
671  }        owStrings[10];
672};
673typedef struct OWVariablesHeader OWVariablesHeader;
674
675IOReturn IODTNVRAM::initOFVariables(void)
676{
677  UInt32            cnt, propOffset, propType;
678  UInt8             *propName, *propData;
679  UInt32            propNameLength, propDataLength;
680  const OSSymbol    *propSymbol;
681  OSObject          *propObject;
682  OWVariablesHeader *owHeader;
683
684  if (_ofImage == 0) return kIOReturnNotReady;
685
686  _ofDict =  OSDictionary::withCapacity(1);
687  if (_ofDict == 0) return kIOReturnNoMemory;
688
689  if (getPlatform()->getBootROMType()) {
690    cnt = 0;
691    while (cnt < _ofPartitionSize) {
692      // Break if there is no name.
693      if (_ofImage[cnt] == '\0') break;
694
695      // Find the length of the name.
696      propName = _ofImage + cnt;
697      for (propNameLength = 0; (cnt + propNameLength) < _ofPartitionSize;
698	   propNameLength++) {
699	if (_ofImage[cnt + propNameLength] == '=') break;
700      }
701
702      // Break if the name goes past the end of the partition.
703      if ((cnt + propNameLength) >= _ofPartitionSize) break;
704      cnt += propNameLength + 1;
705
706      propData = _ofImage + cnt;
707      for (propDataLength = 0; (cnt + propDataLength) < _ofPartitionSize;
708	   propDataLength++) {
709	if (_ofImage[cnt + propDataLength] == '\0') break;
710      }
711
712      // Break if the data goes past the end of the partition.
713      if ((cnt + propDataLength) >= _ofPartitionSize) break;
714      cnt += propDataLength + 1;
715
716      if (convertPropToObject(propName, propNameLength,
717			      propData, propDataLength,
718			      &propSymbol, &propObject)) {
719	_ofDict->setObject(propSymbol, propObject);
720	propSymbol->release();
721	propObject->release();
722      }
723    }
724
725    // Create the boot-args property if it is not in the dictionary.
726    if (_ofDict->getObject("boot-args") == 0) {
727      propObject = OSString::withCStringNoCopy("");
728      if (propObject != 0) {
729	_ofDict->setObject("boot-args", propObject);
730	propObject->release();
731      }
732    }
733
734    // Create the 'aapl,panic-info' property if needed.
735    if (_piImage != 0) {
736      propDataLength = *(UInt32 *)_piImage;
737      if ((propDataLength != 0) && (propDataLength <= (_piPartitionSize - 4))) {
738	propObject = OSData::withBytes(_piImage + 4, propDataLength);
739	_ofDict->setObject(kIODTNVRAMPanicInfoKey, propObject);
740	propObject->release();
741
742	// Clear the length from _piImage and mark dirty.
743	*(UInt32 *)_piImage = 0;
744	_nvramImageDirty = true;
745      }
746    }
747  } else {
748    owHeader = (OWVariablesHeader *)_ofImage;
749    if (!validateOWChecksum(_ofImage)) {
750      _ofDict->release();
751      _ofDict = 0;
752      return kIOReturnBadMedia;
753    }
754
755    cnt = 0;
756    while (1) {
757      if (!getOWVariableInfo(cnt++, &propSymbol, &propType, &propOffset))
758	break;
759
760      switch (propType) {
761      case kOFVariableTypeBoolean :
762	propObject = OSBoolean::withBoolean(owHeader->owFlags & propOffset);
763	break;
764
765      case kOFVariableTypeNumber :
766	propObject = OSNumber::withNumber(owHeader->owNumbers[propOffset], 32);
767	break;
768
769      case kOFVariableTypeString :
770	propData = _ofImage + owHeader->owStrings[propOffset].offset -
771	  _ofPartitionOffset;
772	propDataLength = owHeader->owStrings[propOffset].length;
773	propName = IONew(UInt8, propDataLength + 1);
774	if (propName != 0) {
775	  strncpy((char *)propName, (const char *)propData, propDataLength);
776	  propName[propDataLength] = '\0';
777	  propObject = OSString::withCString((const char *)propName);
778	  IODelete(propName, UInt8, propDataLength + 1);
779	}
780	break;
781      }
782
783      if (propObject == 0) break;
784
785      _ofDict->setObject(propSymbol, propObject);
786      propSymbol->release();
787      propObject->release();
788    }
789
790    // Create the boot-args property.
791    propSymbol = OSSymbol::withCString("boot-command");
792    if (propSymbol != 0) {
793      propObject = _ofDict->getObject(propSymbol);
794      if (propObject != 0) {
795	updateOWBootArgs(propSymbol, propObject);
796      }
797      propSymbol->release();
798    }
799  }
800
801  return kIOReturnSuccess;
802}
803
804IOReturn IODTNVRAM::syncOFVariables(void)
805{
806  bool                 ok;
807  UInt32               cnt, length, maxLength;
808  UInt32               curOffset, tmpOffset, tmpType, tmpDataLength;
809  UInt8                *buffer, *tmpBuffer;
810  const UInt8          *tmpData;
811  const OSSymbol       *tmpSymbol;
812  OSObject             *tmpObject;
813  OSBoolean            *tmpBoolean;
814  OSNumber             *tmpNumber;
815  OSString             *tmpString;
816  OSCollectionIterator *iter;
817  OWVariablesHeader    *owHeader, *owHeaderOld;
818
819  if ((_ofImage == 0) || (_ofDict == 0)) return kIOReturnNotReady;
820
821  if (!_ofImageDirty) return kIOReturnSuccess;
822
823  if (getPlatform()->getBootROMType()) {
824    buffer = tmpBuffer = IONew(UInt8, _ofPartitionSize);
825    if (buffer == 0) return kIOReturnNoMemory;
826    bzero(buffer, _ofPartitionSize);
827
828    ok = true;
829    maxLength = _ofPartitionSize;
830
831    iter = OSCollectionIterator::withCollection(_ofDict);
832    if (iter == 0) ok = false;
833
834    while (ok) {
835      tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
836      if (tmpSymbol == 0) break;
837
838      // Don't save 'aapl,panic-info'.
839      if (tmpSymbol->isEqualTo(kIODTNVRAMPanicInfoKey)) continue;
840
841      tmpObject = _ofDict->getObject(tmpSymbol);
842
843      length = maxLength;
844      ok = convertObjectToProp(tmpBuffer, &length, tmpSymbol, tmpObject);
845      if (ok) {
846	tmpBuffer += length;
847	maxLength -= length;
848      }
849    }
850    iter->release();
851
852    if (ok) {
853      bcopy(buffer, _ofImage, _ofPartitionSize);
854    }
855
856    IODelete(buffer, UInt8, _ofPartitionSize);
857
858    if (!ok) return kIOReturnBadArgument;
859  } else {
860    buffer = IONew(UInt8, _ofPartitionSize);
861    if (buffer == 0) return kIOReturnNoMemory;
862    bzero(buffer, _ofPartitionSize);
863
864    owHeader    = (OWVariablesHeader *)buffer;
865    owHeaderOld = (OWVariablesHeader *)_ofImage;
866
867    owHeader->owMagic = owHeaderOld->owMagic;
868    owHeader->owVersion = owHeaderOld->owVersion;
869    owHeader->owPages = owHeaderOld->owPages;
870
871    curOffset = _ofPartitionSize;
872
873    ok = true;
874    cnt = 0;
875    while (ok) {
876      if (!getOWVariableInfo(cnt++, &tmpSymbol, &tmpType, &tmpOffset))
877	break;
878
879      tmpObject = _ofDict->getObject(tmpSymbol);
880
881      switch (tmpType) {
882      case kOFVariableTypeBoolean :
883	tmpBoolean = OSDynamicCast(OSBoolean, tmpObject);
884	if (tmpBoolean->getValue()) owHeader->owFlags |= tmpOffset;
885	break;
886
887      case kOFVariableTypeNumber :
888	tmpNumber = OSDynamicCast(OSNumber, tmpObject);
889	owHeader->owNumbers[tmpOffset] = tmpNumber->unsigned32BitValue();
890        break;
891
892      case kOFVariableTypeString :
893	tmpString = OSDynamicCast(OSString, tmpObject);
894	tmpData = (const UInt8 *)tmpString->getCStringNoCopy();
895	tmpDataLength = tmpString->getLength();
896
897	if ((curOffset - tmpDataLength) < sizeof(OWVariablesHeader)) {
898	  ok = false;
899	  break;
900	}
901
902	owHeader->owStrings[tmpOffset].length = tmpDataLength;
903	curOffset -= tmpDataLength;
904	owHeader->owStrings[tmpOffset].offset = curOffset + _ofPartitionOffset;
905	if (tmpDataLength != 0)
906	  bcopy(tmpData, buffer + curOffset, tmpDataLength);
907	break;
908      }
909    }
910
911    if (ok) {
912      owHeader->owHere = _ofPartitionOffset + sizeof(OWVariablesHeader);
913      owHeader->owTop = _ofPartitionOffset + curOffset;
914      owHeader->owNext = 0;
915
916      owHeader->owChecksum = 0;
917      owHeader->owChecksum = ~generateOWChecksum(buffer);
918
919      bcopy(buffer, _ofImage, _ofPartitionSize);
920    }
921
922    IODelete(buffer, UInt8, _ofPartitionSize);
923
924    if (!ok) return kIOReturnBadArgument;
925  }
926
927  _ofImageDirty = false;
928  _nvramImageDirty = true;
929
930  return kIOReturnSuccess;
931}
932
933struct OFVariable {
934  const char *variableName;
935  UInt32     variableType;
936  UInt32     variablePerm;
937  SInt32     variableOffset;
938};
939typedef struct OFVariable OFVariable;
940
941enum {
942  kOWVariableOffsetNumber = 8,
943  kOWVariableOffsetString = 17
944};
945
946OFVariable gOFVariables[] = {
947  {"little-endian?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 0},
948  {"real-mode?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 1},
949  {"auto-boot?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 2},
950  {"diag-switch?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 3},
951  {"fcode-debug?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 4},
952  {"oem-banner?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 5},
953  {"oem-logo?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 6},
954  {"use-nvramrc?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 7},
955  {"use-generic?", kOFVariableTypeBoolean, kOFVariablePermUserRead, -1},
956  {"default-mac-address?", kOFVariableTypeBoolean, kOFVariablePermUserRead,-1},
957  {"real-base", kOFVariableTypeNumber, kOFVariablePermUserRead, 8},
958  {"real-size", kOFVariableTypeNumber, kOFVariablePermUserRead, 9},
959  {"virt-base", kOFVariableTypeNumber, kOFVariablePermUserRead, 10},
960  {"virt-size", kOFVariableTypeNumber, kOFVariablePermUserRead, 11},
961  {"load-base", kOFVariableTypeNumber, kOFVariablePermUserRead, 12},
962  {"pci-probe-list", kOFVariableTypeNumber, kOFVariablePermUserRead, 13},
963  {"pci-probe-mask", kOFVariableTypeNumber, kOFVariablePermUserRead, -1},
964  {"screen-#columns", kOFVariableTypeNumber, kOFVariablePermUserRead, 14},
965  {"screen-#rows", kOFVariableTypeNumber, kOFVariablePermUserRead, 15},
966  {"selftest-#megs", kOFVariableTypeNumber, kOFVariablePermUserRead, 16},
967  {"boot-device", kOFVariableTypeString, kOFVariablePermUserRead, 17},
968  {"boot-file", kOFVariableTypeString, kOFVariablePermUserRead, 18},
969  {"boot-screen", kOFVariableTypeString, kOFVariablePermUserRead, -1},
970  {"console-screen", kOFVariableTypeString, kOFVariablePermUserRead, -1},
971  {"diag-device", kOFVariableTypeString, kOFVariablePermUserRead, 19},
972  {"diag-file", kOFVariableTypeString, kOFVariablePermUserRead, 20},
973  {"input-device", kOFVariableTypeString, kOFVariablePermUserRead, 21},
974  {"output-device", kOFVariableTypeString, kOFVariablePermUserRead, 22},
975  {"input-device-1", kOFVariableTypeString, kOFVariablePermUserRead, -1},
976  {"output-device-1", kOFVariableTypeString, kOFVariablePermUserRead, -1},
977  {"mouse-device", kOFVariableTypeString, kOFVariablePermUserRead, -1},
978  {"oem-banner", kOFVariableTypeString, kOFVariablePermUserRead, 23},
979  {"oem-logo", kOFVariableTypeString, kOFVariablePermUserRead, 24},
980  {"nvramrc", kOFVariableTypeString, kOFVariablePermUserRead, 25},
981  {"boot-command", kOFVariableTypeString, kOFVariablePermUserRead, 26},
982  {"default-client-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1},
983  {"default-server-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1},
984  {"default-gateway-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1},
985  {"default-subnet-mask", kOFVariableTypeString, kOFVariablePermUserRead, -1},
986  {"default-router-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1},
987  {"boot-script", kOFVariableTypeString, kOFVariablePermUserRead, -1},
988  {"boot-args", kOFVariableTypeString, kOFVariablePermUserRead, -1},
989  {"aapl,pci", kOFVariableTypeData, kOFVariablePermRootOnly, -1},
990  {"security-mode", kOFVariableTypeString, kOFVariablePermUserRead, -1},
991  {"security-password", kOFVariableTypeData, kOFVariablePermRootOnly, -1},
992  {"boot-image", kOFVariableTypeData, kOFVariablePermUserWrite, -1},
993  {"com.apple.System.fp-state", kOFVariableTypeData, kOFVariablePermKernelOnly, -1},
994  {0, kOFVariableTypeData, kOFVariablePermUserRead, -1}
995};
996
997UInt32 IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
998{
999  OFVariable *ofVar;
1000
1001  ofVar = gOFVariables;
1002  while (1) {
1003    if ((ofVar->variableName == 0) ||
1004	propSymbol->isEqualTo(ofVar->variableName)) break;
1005    ofVar++;
1006  }
1007
1008  return ofVar->variableType;
1009}
1010
1011UInt32 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
1012{
1013  OFVariable *ofVar;
1014
1015  ofVar = gOFVariables;
1016  while (1) {
1017    if ((ofVar->variableName == 0) ||
1018	propSymbol->isEqualTo(ofVar->variableName)) break;
1019    ofVar++;
1020  }
1021
1022  return ofVar->variablePerm;
1023}
1024
1025bool IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
1026				  UInt32 *propType, UInt32 *propOffset)
1027{
1028  OFVariable *ofVar;
1029
1030  ofVar = gOFVariables;
1031  while (1) {
1032    if (ofVar->variableName == 0) return false;
1033
1034    if (ofVar->variableOffset == (SInt32) variableNumber) break;
1035
1036    ofVar++;
1037  }
1038
1039  *propSymbol = OSSymbol::withCStringNoCopy(ofVar->variableName);
1040  *propType = ofVar->variableType;
1041
1042  switch (*propType) {
1043  case kOFVariableTypeBoolean :
1044    *propOffset = 1 << (31 - variableNumber);
1045    break;
1046
1047  case kOFVariableTypeNumber :
1048    *propOffset = variableNumber - kOWVariableOffsetNumber;
1049    break;
1050
1051  case kOFVariableTypeString :
1052    *propOffset = variableNumber - kOWVariableOffsetString;
1053    break;
1054  }
1055
1056  return true;
1057}
1058
1059bool IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
1060				    UInt8 *propData, UInt32 propDataLength,
1061				    const OSSymbol **propSymbol,
1062				    OSObject **propObject)
1063{
1064  UInt32         propType;
1065  const OSSymbol *tmpSymbol;
1066  OSObject       *tmpObject;
1067  OSNumber       *tmpNumber;
1068  OSString       *tmpString;
1069
1070  // Create the symbol.
1071  propName[propNameLength] = '\0';
1072  tmpSymbol = OSSymbol::withCString((const char *)propName);
1073  propName[propNameLength] = '=';
1074  if (tmpSymbol == 0) {
1075    return false;
1076  }
1077
1078  propType = getOFVariableType(tmpSymbol);
1079
1080  // Create the object.
1081  tmpObject = 0;
1082  switch (propType) {
1083  case kOFVariableTypeBoolean :
1084    if (!strncmp("true", (const char *)propData, propDataLength)) {
1085      tmpObject = kOSBooleanTrue;
1086    } else if (!strncmp("false", (const char *)propData, propDataLength)) {
1087      tmpObject = kOSBooleanFalse;
1088    }
1089    break;
1090
1091  case kOFVariableTypeNumber :
1092    tmpNumber = OSNumber::withNumber(strtol((const char *)propData, 0, 0), 32);
1093    if (tmpNumber != 0) tmpObject = tmpNumber;
1094    break;
1095
1096  case kOFVariableTypeString :
1097    tmpString = OSString::withCString((const char *)propData);
1098    if (tmpString != 0) tmpObject = tmpString;
1099    break;
1100
1101  case kOFVariableTypeData :
1102    tmpObject = unescapeBytesToData(propData, propDataLength);
1103    break;
1104  }
1105
1106  if (tmpObject == 0) {
1107    tmpSymbol->release();
1108    return false;
1109  }
1110
1111  *propSymbol = tmpSymbol;
1112  *propObject = tmpObject;
1113
1114  return true;
1115}
1116
1117bool IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
1118				    const OSSymbol *propSymbol, OSObject *propObject)
1119{
1120  const UInt8    *propName;
1121  UInt32         propNameLength, propDataLength;
1122  UInt32         propType, tmpValue;
1123  OSBoolean      *tmpBoolean = 0;
1124  OSNumber       *tmpNumber = 0;
1125  OSString       *tmpString = 0;
1126  OSData         *tmpData = 0;
1127
1128  propName = (const UInt8 *)propSymbol->getCStringNoCopy();
1129  propNameLength = propSymbol->getLength();
1130  propType = getOFVariableType(propSymbol);
1131
1132  // Get the size of the data.
1133  propDataLength = 0xFFFFFFFF;
1134  switch (propType) {
1135  case kOFVariableTypeBoolean :
1136    tmpBoolean = OSDynamicCast(OSBoolean, propObject);
1137    if (tmpBoolean != 0) propDataLength = 5;
1138    break;
1139
1140  case kOFVariableTypeNumber :
1141    tmpNumber = OSDynamicCast(OSNumber, propObject);
1142    if (tmpNumber != 0) propDataLength = 10;
1143    break;
1144
1145  case kOFVariableTypeString :
1146    tmpString = OSDynamicCast(OSString, propObject);
1147    if (tmpString != 0) propDataLength = tmpString->getLength();
1148    break;
1149
1150  case kOFVariableTypeData :
1151    tmpData = OSDynamicCast(OSData, propObject);
1152    if (tmpData != 0) {
1153      tmpData = escapeDataToData(tmpData);
1154      propDataLength = tmpData->getLength();
1155    }
1156    break;
1157  }
1158
1159  // Make sure the propertySize is known and will fit.
1160  if (propDataLength == 0xFFFFFFFF) return false;
1161  if ((propNameLength + propDataLength + 2) > *length) return false;
1162
1163  // Copy the property name equal sign.
1164  buffer += snprintf((char *)buffer, *length, "%s=", propName);
1165
1166  switch (propType) {
1167  case kOFVariableTypeBoolean :
1168    if (tmpBoolean->getValue()) {
1169      strlcpy((char *)buffer, "true", *length - propNameLength);
1170    } else {
1171      strlcpy((char *)buffer, "false", *length - propNameLength);
1172    }
1173    break;
1174
1175  case kOFVariableTypeNumber :
1176    tmpValue = tmpNumber->unsigned32BitValue();
1177    if (tmpValue == 0xFFFFFFFF) {
1178      strlcpy((char *)buffer, "-1", *length - propNameLength);
1179    } else if (tmpValue < 1000) {
1180      snprintf((char *)buffer, *length - propNameLength, "%d", (uint32_t)tmpValue);
1181    } else {
1182      snprintf((char *)buffer, *length - propNameLength, "0x%x", (uint32_t)tmpValue);
1183    }
1184    break;
1185
1186  case kOFVariableTypeString :
1187    strlcpy((char *)buffer, tmpString->getCStringNoCopy(), *length - propNameLength);
1188    break;
1189
1190  case kOFVariableTypeData :
1191    bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
1192    tmpData->release();
1193    break;
1194  }
1195
1196  propDataLength = strlen((const char *)buffer);
1197
1198  *length = propNameLength + propDataLength + 2;
1199
1200  return true;
1201}
1202
1203
1204UInt16 IODTNVRAM::generateOWChecksum(UInt8 *buffer)
1205{
1206  UInt32 cnt, checksum = 0;
1207  UInt16 *tmpBuffer = (UInt16 *)buffer;
1208
1209  for (cnt = 0; cnt < _ofPartitionSize / 2; cnt++)
1210    checksum += tmpBuffer[cnt];
1211
1212  return checksum % 0x0000FFFF;
1213}
1214
1215bool IODTNVRAM::validateOWChecksum(UInt8 *buffer)
1216{
1217  UInt32 cnt, checksum, sum = 0;
1218  UInt16 *tmpBuffer = (UInt16 *)buffer;
1219
1220  for (cnt = 0; cnt < _ofPartitionSize / 2; cnt++)
1221    sum += tmpBuffer[cnt];
1222
1223  checksum = (sum >> 16) + (sum & 0x0000FFFF);
1224  if (checksum == 0x10000) checksum--;
1225  checksum = (checksum ^ 0x0000FFFF) & 0x0000FFFF;
1226
1227  return checksum == 0;
1228}
1229
1230void IODTNVRAM::updateOWBootArgs(const OSSymbol *key, OSObject *value)
1231{
1232  bool        wasBootArgs, bootr = false;
1233  UInt32      cnt;
1234  OSString    *tmpString, *bootCommand, *bootArgs = 0;
1235  const UInt8 *bootCommandData, *bootArgsData;
1236  UInt8       *tmpData;
1237  UInt32      bootCommandDataLength, bootArgsDataLength, tmpDataLength;
1238
1239  tmpString = OSDynamicCast(OSString, value);
1240  if (tmpString == 0) return;
1241
1242  if (key->isEqualTo("boot-command")) {
1243    wasBootArgs = false;
1244    bootCommand = tmpString;
1245  } else if (key->isEqualTo("boot-args")) {
1246    wasBootArgs = true;
1247    bootArgs = tmpString;
1248    bootCommand = OSDynamicCast(OSString, _ofDict->getObject("boot-command"));
1249    if (bootCommand == 0) return;
1250  } else return;
1251
1252  bootCommandData = (const UInt8 *)bootCommand->getCStringNoCopy();
1253  bootCommandDataLength = bootCommand->getLength();
1254
1255  if (bootCommandData == 0) return;
1256
1257  for (cnt = 0; cnt < bootCommandDataLength; cnt++) {
1258    if ((bootCommandData[cnt] == 'b') &&
1259	!strncmp("bootr", (const char *)bootCommandData + cnt, 5)) {
1260      cnt += 5;
1261      while (bootCommandData[cnt] == ' ') cnt++;
1262      bootr = true;
1263      break;
1264    }
1265  }
1266  if (!bootr) {
1267    _ofDict->removeObject("boot-args");
1268    return;
1269  }
1270
1271  if (wasBootArgs) {
1272    bootArgsData = (const UInt8 *)bootArgs->getCStringNoCopy();
1273    bootArgsDataLength = bootArgs->getLength();
1274    if (bootArgsData == 0) return;
1275
1276    tmpDataLength = cnt + bootArgsDataLength;
1277    tmpData = IONew(UInt8, tmpDataLength + 1);
1278    if (tmpData == 0) return;
1279
1280    cnt -= strlcpy((char *)tmpData, (const char *)bootCommandData, cnt);
1281    strlcat((char *)tmpData, (const char *)bootArgsData, cnt);
1282
1283    bootCommand = OSString::withCString((const char *)tmpData);
1284    if (bootCommand != 0) {
1285      _ofDict->setObject("boot-command", bootCommand);
1286      bootCommand->release();
1287    }
1288
1289    IODelete(tmpData, UInt8, tmpDataLength + 1);
1290  } else {
1291    bootArgs = OSString::withCString((const char *)(bootCommandData + cnt));
1292    if (bootArgs != 0) {
1293      _ofDict->setObject("boot-args", bootArgs);
1294      bootArgs->release();
1295    }
1296  }
1297}
1298
1299
1300// Private methods for Name Registry access.
1301
1302enum {
1303  kMaxNVNameLength = 4,
1304  kMaxNVDataLength = 8
1305};
1306
1307struct NVRAMProperty
1308{
1309  IONVRAMDescriptor   header;
1310  UInt8               nameLength;
1311  UInt8               name[ kMaxNVNameLength ];
1312  UInt8               dataLength;
1313  UInt8               data[ kMaxNVDataLength ];
1314};
1315
1316bool IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor *hdr, UInt32 *where)
1317{
1318  UInt32 offset;
1319  SInt32 nvEnd;
1320
1321  nvEnd = *((UInt16 *)_nrImage);
1322  if(getPlatform()->getBootROMType()) {
1323    // on NewWorld, offset to partition start
1324    nvEnd -= 0x100;
1325  } else {
1326    // on old world, absolute
1327    nvEnd -= _nrPartitionOffset;
1328  }
1329  if((nvEnd < 0) || (nvEnd >= kIODTNVRAMNameRegistrySize))
1330    nvEnd = 2;
1331
1332  offset = 2;
1333  while ((offset + sizeof(NVRAMProperty)) <= (UInt32)nvEnd) {
1334    if (bcmp(_nrImage + offset, hdr, sizeof(*hdr)) == 0) {
1335      *where = offset;
1336      return true;
1337    }
1338    offset += sizeof(NVRAMProperty);
1339  }
1340
1341  if ((nvEnd + sizeof(NVRAMProperty)) <= kIODTNVRAMNameRegistrySize)
1342    *where = nvEnd;
1343  else
1344    *where = 0;
1345
1346  return false;
1347}
1348
1349IOReturn IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry *entry,
1350					   const OSSymbol **name,
1351					   OSData **value)
1352{
1353  IONVRAMDescriptor hdr;
1354  NVRAMProperty     *prop;
1355  IOByteCount       length;
1356  UInt32            offset;
1357  IOReturn          err;
1358  char              nameBuf[kMaxNVNameLength + 1];
1359
1360  if (_nrImage == 0) return kIOReturnUnsupported;
1361  if ((entry == 0) || (name == 0) || (value == 0)) return kIOReturnBadArgument;
1362
1363  err = IODTMakeNVDescriptor(entry, &hdr);
1364  if (err != kIOReturnSuccess) return err;
1365
1366  if (searchNVRAMProperty(&hdr, &offset)) {
1367    prop = (NVRAMProperty *)(_nrImage + offset);
1368
1369    length = prop->nameLength;
1370    if (length > kMaxNVNameLength) length = kMaxNVNameLength;
1371    strncpy(nameBuf, (const char *)prop->name, length);
1372    nameBuf[length] = 0;
1373    *name = OSSymbol::withCString(nameBuf);
1374
1375    length = prop->dataLength;
1376    if (length > kMaxNVDataLength) length = kMaxNVDataLength;
1377    *value = OSData::withBytes(prop->data, length);
1378
1379    if ((*name != 0) && (*value != 0)) return kIOReturnSuccess;
1380    else return kIOReturnNoMemory;
1381  }
1382
1383  return kIOReturnNoResources;
1384}
1385
1386IOReturn IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry *entry,
1387					    const OSSymbol *name,
1388					    OSData *value)
1389{
1390  IONVRAMDescriptor hdr;
1391  NVRAMProperty     *prop;
1392  IOByteCount       nameLength;
1393  IOByteCount       dataLength;
1394  UInt32            offset;
1395  IOReturn          err;
1396  UInt16            nvLength;
1397  bool              exists;
1398
1399  if (_nrImage == 0) return kIOReturnUnsupported;
1400  if ((entry == 0) || (name == 0) || (value == 0)) return kIOReturnBadArgument;
1401
1402  nameLength = name->getLength();
1403  dataLength = value->getLength();
1404  if (nameLength > kMaxNVNameLength) return kIOReturnNoSpace;
1405  if (dataLength > kMaxNVDataLength) return kIOReturnNoSpace;
1406
1407  err = IODTMakeNVDescriptor(entry, &hdr);
1408  if (err != kIOReturnSuccess) return err;
1409
1410  exists = searchNVRAMProperty(&hdr, &offset);
1411  if (offset == 0) return kIOReturnNoMemory;
1412
1413  prop = (NVRAMProperty *)(_nrImage + offset);
1414  if (!exists) bcopy(&hdr, &prop->header, sizeof(hdr));
1415
1416  prop->nameLength = nameLength;
1417  bcopy(name->getCStringNoCopy(), prop->name, nameLength);
1418  prop->dataLength = dataLength;
1419  bcopy(value->getBytesNoCopy(), prop->data, dataLength);
1420
1421  if (!exists) {
1422    nvLength = offset + sizeof(NVRAMProperty);
1423    if (getPlatform()->getBootROMType())
1424      nvLength += 0x100;
1425    else
1426      nvLength += _nrPartitionOffset;
1427    *((UInt16 *)_nrImage) = nvLength;
1428  }
1429
1430  _nvramImageDirty = true;
1431
1432  return err;
1433}
1434
1435OSData *IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length)
1436{
1437  OSData *data = 0;
1438  UInt32 totalLength = 0;
1439  UInt32 cnt, cnt2;
1440  UInt8  byte;
1441  bool   ok;
1442
1443  // Calculate the actual length of the data.
1444  ok = true;
1445  totalLength = 0;
1446  for (cnt = 0; cnt < length;) {
1447    byte = bytes[cnt++];
1448    if (byte == 0xFF) {
1449      byte = bytes[cnt++];
1450      if (byte == 0x00) {
1451        ok = false;
1452        break;
1453      }
1454      cnt2 = byte & 0x7F;
1455    } else
1456      cnt2 = 1;
1457    totalLength += cnt2;
1458  }
1459
1460  if (ok) {
1461    // Create an empty OSData of the correct size.
1462    data = OSData::withCapacity(totalLength);
1463    if (data != 0) {
1464      for (cnt = 0; cnt < length;) {
1465        byte = bytes[cnt++];
1466        if (byte == 0xFF) {
1467          byte = bytes[cnt++];
1468          cnt2 = byte & 0x7F;
1469          byte = (byte & 0x80) ? 0xFF : 0x00;
1470        } else
1471          cnt2 = 1;
1472        data->appendByte(byte, cnt2);
1473      }
1474    }
1475  }
1476
1477  return data;
1478}
1479
1480OSData * IODTNVRAM::escapeDataToData(OSData * value)
1481{
1482  OSData *       result;
1483  const UInt8 *  startPtr;
1484  const UInt8 *  endPtr;
1485  const UInt8 *  wherePtr;
1486  UInt8          byte;
1487  bool	         ok = true;
1488
1489  wherePtr = (const UInt8 *) value->getBytesNoCopy();
1490  endPtr = wherePtr + value->getLength();
1491
1492  result = OSData::withCapacity(endPtr - wherePtr);
1493  if (!result)
1494    return result;
1495
1496  while (wherePtr < endPtr) {
1497    startPtr = wherePtr;
1498    byte = *wherePtr++;
1499    if ((byte == 0x00) || (byte == 0xFF)) {
1500      for (;
1501            ((wherePtr - startPtr) < 0x80) && (wherePtr < endPtr) && (byte == *wherePtr);
1502            wherePtr++)	{}
1503      ok &= result->appendByte(0xff, 1);
1504      byte = (byte & 0x80) | (wherePtr - startPtr);
1505    }
1506    ok &= result->appendByte(byte, 1);
1507  }
1508  ok &= result->appendByte(0, 1);
1509
1510  if (!ok) {
1511    result->release();
1512    result = 0;
1513  }
1514
1515  return result;
1516}
1517
1518static bool IsApplePropertyName(const char * propName)
1519{
1520  char c;
1521  while ((c = *propName++)) {
1522    if ((c >= 'A') && (c <= 'Z'))
1523      break;
1524  }
1525
1526  return (c == 0);
1527}
1528
1529IOReturn IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry *entry,
1530					   const OSSymbol **name,
1531					   OSData **value)
1532{
1533  IOReturn    err = kIOReturnNoResources;
1534  OSData      *data;
1535  const UInt8 *startPtr;
1536  const UInt8 *endPtr;
1537  const UInt8 *wherePtr;
1538  const UInt8 *nvPath = 0;
1539  const char  *nvName = 0;
1540  const char  *resultName = 0;
1541  const UInt8 *resultValue = 0;
1542  UInt32       resultValueLen = 0;
1543  UInt8       byte;
1544
1545  if (_ofDict == 0) return err;
1546  data = OSDynamicCast(OSData, _ofDict->getObject(_registryPropertiesKey));
1547  if (data == 0) return err;
1548
1549  startPtr = (const UInt8 *) data->getBytesNoCopy();
1550  endPtr = startPtr + data->getLength();
1551
1552  wherePtr = startPtr;
1553  while (wherePtr < endPtr) {
1554    byte = *(wherePtr++);
1555    if (byte)
1556      continue;
1557
1558    if (nvPath == 0)
1559      nvPath = startPtr;
1560    else if (nvName == 0)
1561      nvName = (const char *) startPtr;
1562    else {
1563      IORegistryEntry * compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
1564      if (compareEntry)
1565        compareEntry->release();
1566      if (entry == compareEntry) {
1567        bool appleProp = IsApplePropertyName(nvName);
1568        if (!appleProp || !resultName) {
1569          resultName     = nvName;
1570          resultValue    = startPtr;
1571          resultValueLen = wherePtr - startPtr - 1;
1572        }
1573        if (!appleProp)
1574          break;
1575      }
1576      nvPath = 0;
1577      nvName = 0;
1578    }
1579    startPtr = wherePtr;
1580  }
1581  if (resultName) {
1582    *name = OSSymbol::withCString(resultName);
1583    *value = unescapeBytesToData(resultValue, resultValueLen);
1584    if ((*name != 0) && (*value != 0))
1585      err = kIOReturnSuccess;
1586    else
1587      err = kIOReturnNoMemory;
1588  }
1589  return err;
1590}
1591
1592IOReturn IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
1593					    const OSSymbol *propName,
1594					    OSData *value)
1595{
1596  OSData       *oldData;
1597  OSData       *data = 0;
1598  const UInt8  *startPtr;
1599  const UInt8  *propStart;
1600  const UInt8  *endPtr;
1601  const UInt8  *wherePtr;
1602  const UInt8  *nvPath = 0;
1603  const char   *nvName = 0;
1604  const char * comp;
1605  const char * name;
1606  UInt8        byte;
1607  bool         ok = true;
1608  bool         settingAppleProp;
1609
1610  if (_ofDict == 0) return kIOReturnNoResources;
1611
1612  settingAppleProp = IsApplePropertyName(propName->getCStringNoCopy());
1613
1614  // copy over existing properties for other entries
1615
1616  oldData = OSDynamicCast(OSData, _ofDict->getObject(_registryPropertiesKey));
1617  if (oldData) {
1618    startPtr = (const UInt8 *) oldData->getBytesNoCopy();
1619    endPtr = startPtr + oldData->getLength();
1620
1621    propStart = startPtr;
1622    wherePtr = startPtr;
1623    while (wherePtr < endPtr) {
1624      byte = *(wherePtr++);
1625      if (byte)
1626        continue;
1627      if (nvPath == 0)
1628        nvPath = startPtr;
1629      else if (nvName == 0)
1630        nvName = (const char *) startPtr;
1631      else {
1632        IORegistryEntry * compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
1633        if (compareEntry)
1634          compareEntry->release();
1635        if (entry == compareEntry) {
1636          if ((settingAppleProp && propName->isEqualTo(nvName))
1637           || (!settingAppleProp && !IsApplePropertyName(nvName))) {
1638             // delete old property (nvPath -> wherePtr)
1639             data = OSData::withBytes(propStart, nvPath - propStart);
1640             if (data)
1641               ok &= data->appendBytes(wherePtr, endPtr - wherePtr);
1642             break;
1643          }
1644        }
1645        nvPath = 0;
1646        nvName = 0;
1647      }
1648
1649      startPtr = wherePtr;
1650    }
1651  }
1652
1653  // make the new property
1654
1655  if (!data) {
1656    if (oldData)
1657      data = OSData::withData(oldData);
1658    else
1659      data = OSData::withCapacity(16);
1660    if (!data)
1661      return kIOReturnNoMemory;
1662  }
1663
1664  if (value && value->getLength()) {
1665		// get entries in path
1666		OSArray *array = OSArray::withCapacity(5);
1667		if (!array) {
1668			data->release();
1669			return kIOReturnNoMemory;
1670		}
1671		do
1672			array->setObject(entry);
1673		while ((entry = entry->getParentEntry(gIODTPlane)));
1674
1675		// append path
1676		for (int i = array->getCount() - 3;
1677					(entry = (IORegistryEntry *) array->getObject(i));
1678					i--) {
1679
1680			name = entry->getName(gIODTPlane);
1681			comp = entry->getLocation(gIODTPlane);
1682			if( comp && (0 == strncmp("pci", name, sizeof("pci")))
1683			 && (0 == strncmp("80000000", comp, sizeof("80000000")))) {
1684				// yosemite hack
1685				comp = "/pci@80000000";
1686			} else {
1687				if (comp)
1688					ok &= data->appendBytes("/@", 2);
1689				else {
1690					if (!name)
1691						continue;
1692					ok &= data->appendByte('/', 1);
1693					comp = name;
1694				}
1695			}
1696			ok &= data->appendBytes(comp, strlen(comp));
1697		}
1698		ok &= data->appendByte(0, 1);
1699		array->release();
1700
1701		// append prop name
1702		ok &= data->appendBytes(propName->getCStringNoCopy(), propName->getLength() + 1);
1703
1704		// append escaped data
1705		oldData = escapeDataToData(value);
1706		ok &= (oldData != 0);
1707		if (ok)
1708			ok &= data->appendBytes(oldData);
1709	}
1710  if (ok) {
1711    ok = _ofDict->setObject(_registryPropertiesKey, data);
1712    if (ok)
1713      _ofImageDirty = true;
1714  }
1715  data->release();
1716
1717  return ok ? kIOReturnSuccess : kIOReturnNoMemory;
1718}
1719
1720bool IODTNVRAM::safeToSync(void)
1721{
1722    AbsoluteTime delta;
1723    UInt64       delta_ns;
1724    SInt32       delta_secs;
1725
1726	// delta interval went by
1727	clock_get_uptime(&delta);
1728
1729    // Figure it in seconds.
1730    absolutetime_to_nanoseconds(delta, &delta_ns);
1731    delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
1732
1733	if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval)
1734	{
1735		_lastDeviceSync = delta_secs;
1736		_freshInterval = FALSE;
1737		return TRUE;
1738	}
1739
1740	return FALSE;
1741}
1742