1/*
2 * Copyright (c) 1998-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <IOKit/IOCPU.h>
30#include <IOKit/IODeviceTreeSupport.h>
31#include <IOKit/IOKitDebug.h>
32#include <IOKit/IOMapper.h>
33#include <IOKit/IOMessage.h>
34#include <IOKit/IONVRAM.h>
35#include <IOKit/IOPlatformExpert.h>
36#include <IOKit/IORangeAllocator.h>
37#include <IOKit/IOWorkLoop.h>
38#include <IOKit/pwr_mgt/RootDomain.h>
39#include <IOKit/IOKitKeys.h>
40#include <IOKit/IOTimeStamp.h>
41#include <IOKit/IOUserClient.h>
42
43#include <IOKit/system.h>
44
45#include <libkern/c++/OSContainers.h>
46#include <libkern/crypto/sha1.h>
47#include <libkern/OSAtomic.h>
48
49extern "C" {
50#include <machine/machine_routines.h>
51#include <pexpert/pexpert.h>
52#include <uuid/uuid.h>
53}
54
55void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg);
56static void getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen);
57
58/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
59
60#define super IOService
61
62OSDefineMetaClassAndStructors(IOPlatformExpert, IOService)
63
64OSMetaClassDefineReservedUsed(IOPlatformExpert,  0);
65
66OSMetaClassDefineReservedUsed(IOPlatformExpert,  1);
67OSMetaClassDefineReservedUnused(IOPlatformExpert,  2);
68OSMetaClassDefineReservedUnused(IOPlatformExpert,  3);
69OSMetaClassDefineReservedUnused(IOPlatformExpert,  4);
70OSMetaClassDefineReservedUnused(IOPlatformExpert,  5);
71OSMetaClassDefineReservedUnused(IOPlatformExpert,  6);
72OSMetaClassDefineReservedUnused(IOPlatformExpert,  7);
73OSMetaClassDefineReservedUnused(IOPlatformExpert,  8);
74OSMetaClassDefineReservedUnused(IOPlatformExpert,  9);
75OSMetaClassDefineReservedUnused(IOPlatformExpert, 10);
76OSMetaClassDefineReservedUnused(IOPlatformExpert, 11);
77
78static IOPlatformExpert * gIOPlatform;
79static OSDictionary * gIOInterruptControllers;
80static IOLock * gIOInterruptControllersLock;
81static IODTNVRAM *gIOOptionsEntry;
82
83OSSymbol * gPlatformInterruptControllerName;
84
85/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
86
87bool IOPlatformExpert::attach( IOService * provider )
88{
89
90    if( !super::attach( provider ))
91	return( false);
92
93    return( true);
94}
95
96bool IOPlatformExpert::start( IOService * provider )
97{
98    IORangeAllocator *	physicalRanges;
99    OSData *		busFrequency;
100    uint32_t		debugFlags;
101
102    if (!super::start(provider))
103      return false;
104
105    // Override the mapper present flag is requested by boot arguments.
106    if (PE_parse_boot_argn("dart", &debugFlags, sizeof (debugFlags)) && (debugFlags == 0))
107      removeProperty(kIOPlatformMapperPresentKey);
108    if (PE_parse_boot_argn("-x", &debugFlags, sizeof (debugFlags)))
109      removeProperty(kIOPlatformMapperPresentKey);
110
111    // Register the presence or lack thereof a system
112    // PCI address mapper with the IOMapper class
113    IOMapper::setMapperRequired(0 != getProperty(kIOPlatformMapperPresentKey));
114
115    gIOInterruptControllers = OSDictionary::withCapacity(1);
116    gIOInterruptControllersLock = IOLockAlloc();
117
118    // Correct the bus frequency in the device tree.
119    busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 4);
120    provider->setProperty("clock-frequency", busFrequency);
121    busFrequency->release();
122
123    gPlatformInterruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy("IOPlatformInterruptController");
124
125    physicalRanges = IORangeAllocator::withRange(0xffffffff, 1, 16,
126						 IORangeAllocator::kLocking);
127    assert(physicalRanges);
128    setProperty("Platform Memory Ranges", physicalRanges);
129
130    setPlatform( this );
131    gIOPlatform = this;
132
133    PMInstantiatePowerDomains();
134
135    // Parse the serial-number data and publish a user-readable string
136    OSData* mydata = (OSData*) (provider->getProperty("serial-number"));
137    if (mydata != NULL) {
138        OSString *serNoString = createSystemSerialNumberString(mydata);
139        if (serNoString != NULL) {
140            provider->setProperty(kIOPlatformSerialNumberKey, serNoString);
141            serNoString->release();
142        }
143    }
144
145    return( configure(provider) );
146}
147
148bool IOPlatformExpert::configure( IOService * provider )
149{
150    OSSet *		topLevel;
151    OSDictionary *	dict;
152    IOService * 	nub;
153
154    topLevel = OSDynamicCast( OSSet, getProperty("top-level"));
155
156    if( topLevel) {
157        while( (dict = OSDynamicCast( OSDictionary,
158				topLevel->getAnyObject()))) {
159            dict->retain();
160            topLevel->removeObject( dict );
161            nub = createNub( dict );
162            if( 0 == nub)
163                continue;
164            dict->release();
165            nub->attach( this );
166            nub->registerService();
167        }
168    }
169
170    return( true );
171}
172
173IOService * IOPlatformExpert::createNub( OSDictionary * from )
174{
175    IOService *		nub;
176
177    nub = new IOPlatformDevice;
178    if(nub) {
179	if( !nub->init( from )) {
180	    nub->release();
181	    nub = 0;
182	}
183    }
184    return( nub);
185}
186
187bool IOPlatformExpert::compareNubName( const IOService * nub,
188				OSString * name, OSString ** matched ) const
189{
190    return( nub->IORegistryEntry::compareName( name, matched ));
191}
192
193IOReturn IOPlatformExpert::getNubResources( IOService * nub )
194{
195    return( kIOReturnSuccess );
196}
197
198long IOPlatformExpert::getBootROMType(void)
199{
200  return _peBootROMType;
201}
202
203long IOPlatformExpert::getChipSetType(void)
204{
205  return _peChipSetType;
206}
207
208long IOPlatformExpert::getMachineType(void)
209{
210  return _peMachineType;
211}
212
213void IOPlatformExpert::setBootROMType(long peBootROMType)
214{
215  _peBootROMType = peBootROMType;
216}
217
218void IOPlatformExpert::setChipSetType(long peChipSetType)
219{
220  _peChipSetType = peChipSetType;
221}
222
223void IOPlatformExpert::setMachineType(long peMachineType)
224{
225  _peMachineType = peMachineType;
226}
227
228bool IOPlatformExpert::getMachineName( char * /*name*/, int /*maxLength*/)
229{
230    return( false );
231}
232
233bool IOPlatformExpert::getModelName( char * /*name*/, int /*maxLength*/)
234{
235    return( false );
236}
237
238OSString* IOPlatformExpert::createSystemSerialNumberString(OSData* myProperty)
239{
240    return NULL;
241}
242
243IORangeAllocator * IOPlatformExpert::getPhysicalRangeAllocator(void)
244{
245    return(OSDynamicCast(IORangeAllocator,
246			getProperty("Platform Memory Ranges")));
247}
248
249int (*PE_halt_restart)(unsigned int type) = 0;
250
251int IOPlatformExpert::haltRestart(unsigned int type)
252{
253  if (type == kPEPanicSync) return 0;
254
255  if (type == kPEHangCPU) while (true) {}
256
257  if (type == kPEUPSDelayHaltCPU) {
258    // RestartOnPowerLoss feature was turned on, proceed with shutdown.
259    type = kPEHaltCPU;
260  }
261
262  // On ARM kPEPanicRestartCPU is supported in the drivers
263  if (type == kPEPanicRestartCPU)
264	  type = kPERestartCPU;
265
266  if (PE_halt_restart) return (*PE_halt_restart)(type);
267  else return -1;
268}
269
270void IOPlatformExpert::sleepKernel(void)
271{
272#if 0
273  long cnt;
274  boolean_t intState;
275
276  intState = ml_set_interrupts_enabled(false);
277
278  for (cnt = 0; cnt < 10000; cnt++) {
279    IODelay(1000);
280  }
281
282  ml_set_interrupts_enabled(intState);
283#else
284//  PE_initialize_console(0, kPEDisableScreen);
285
286  IOCPUSleepKernel();
287
288//  PE_initialize_console(0, kPEEnableScreen);
289#endif
290}
291
292long IOPlatformExpert::getGMTTimeOfDay(void)
293{
294    return(0);
295}
296
297void IOPlatformExpert::setGMTTimeOfDay(long secs)
298{
299}
300
301
302IOReturn IOPlatformExpert::getConsoleInfo( PE_Video * consoleInfo )
303{
304    return( PE_current_console( consoleInfo));
305}
306
307IOReturn IOPlatformExpert::setConsoleInfo( PE_Video * consoleInfo,
308						unsigned int op)
309{
310    return( PE_initialize_console( consoleInfo, op ));
311}
312
313IOReturn IOPlatformExpert::registerInterruptController(OSSymbol *name, IOInterruptController *interruptController)
314{
315  IOLockLock(gIOInterruptControllersLock);
316
317  gIOInterruptControllers->setObject(name, interruptController);
318
319  IOLockWakeup(gIOInterruptControllersLock,
320		gIOInterruptControllers, /* one-thread */ false);
321
322  IOLockUnlock(gIOInterruptControllersLock);
323
324  return kIOReturnSuccess;
325}
326
327IOInterruptController *IOPlatformExpert::lookUpInterruptController(OSSymbol *name)
328{
329  OSObject              *object;
330
331  IOLockLock(gIOInterruptControllersLock);
332  while (1) {
333
334    object = gIOInterruptControllers->getObject(name);
335
336    if (object != 0)
337	break;
338
339    IOLockSleep(gIOInterruptControllersLock,
340		gIOInterruptControllers, THREAD_UNINT);
341  }
342
343  IOLockUnlock(gIOInterruptControllersLock);
344  return OSDynamicCast(IOInterruptController, object);
345}
346
347
348void IOPlatformExpert::setCPUInterruptProperties(IOService *service)
349{
350  IOCPUInterruptController *controller;
351
352  controller = OSDynamicCast(IOCPUInterruptController, waitForService(serviceMatching("IOCPUInterruptController")));
353  if (controller) controller->setCPUInterruptProperties(service);
354}
355
356bool IOPlatformExpert::atInterruptLevel(void)
357{
358  return ml_at_interrupt_context();
359}
360
361bool IOPlatformExpert::platformAdjustService(IOService */*service*/)
362{
363  return true;
364}
365
366
367//*********************************************************************************
368// PMLog
369//
370//*********************************************************************************
371
372void IOPlatformExpert::
373PMLog(const char *who, unsigned long event,
374      unsigned long param1, unsigned long param2)
375{
376	clock_sec_t nows;
377	clock_usec_t nowus;
378	clock_get_system_microtime(&nows, &nowus);
379	nowus += (nows % 1000) * 1000000;
380
381    kprintf("pm%u %p %.30s %d %lx %lx\n",
382		nowus, current_thread(), who,	// Identity
383		(int) event, (long) param1, (long) param2);			// Args
384}
385
386
387//*********************************************************************************
388// PMInstantiatePowerDomains
389//
390// In this vanilla implementation, a Root Power Domain is instantiated.
391// All other objects which register will be children of this Root.
392// Where this is inappropriate, PMInstantiatePowerDomains is overridden
393// in a platform-specific subclass.
394//*********************************************************************************
395
396void IOPlatformExpert::PMInstantiatePowerDomains ( void )
397{
398    root = new IOPMrootDomain;
399    root->init();
400    root->attach(this);
401    root->start(this);
402}
403
404
405//*********************************************************************************
406// PMRegisterDevice
407//
408// In this vanilla implementation, all callers are made children of the root power domain.
409// Where this is inappropriate, PMRegisterDevice is overridden in a platform-specific subclass.
410//*********************************************************************************
411
412void IOPlatformExpert::PMRegisterDevice(IOService * theNub, IOService * theDevice)
413{
414    root->addPowerChild ( theDevice );
415}
416
417//*********************************************************************************
418// hasPMFeature
419//
420//*********************************************************************************
421
422bool IOPlatformExpert::hasPMFeature (unsigned long featureMask)
423{
424  return ((_pePMFeatures & featureMask) != 0);
425}
426
427//*********************************************************************************
428// hasPrivPMFeature
429//
430//*********************************************************************************
431
432bool IOPlatformExpert::hasPrivPMFeature (unsigned long privFeatureMask)
433{
434  return ((_pePrivPMFeatures & privFeatureMask) != 0);
435}
436
437//*********************************************************************************
438// numBatteriesSupported
439//
440//*********************************************************************************
441
442int IOPlatformExpert::numBatteriesSupported (void)
443{
444  return (_peNumBatteriesSupported);
445}
446
447//*********************************************************************************
448// CheckSubTree
449//
450// This method is called by the instantiated sublass of the platform expert to
451// determine how a device should be inserted into the Power Domain. The subclass
452// provides an XML power tree description against which a device is matched based
453// on class and provider. If a match is found this routine returns true in addition
454// to flagging the description tree at the appropriate node that a device has been
455// registered for the given service.
456//*********************************************************************************
457
458bool IOPlatformExpert::CheckSubTree (OSArray * inSubTree, IOService * theNub, IOService * theDevice, OSDictionary * theParent)
459{
460  unsigned int    i;
461  unsigned int    numPowerTreeNodes;
462  OSDictionary *  entry;
463  OSDictionary *  matchingDictionary;
464  OSDictionary *  providerDictionary;
465  OSDictionary *  deviceDictionary;
466  OSDictionary *  nubDictionary;
467  OSArray *       children;
468  bool            nodeFound            = false;
469  bool            continueSearch       = false;
470  bool            deviceMatch          = false;
471  bool            providerMatch        = false;
472  bool            multiParentMatch     = false;
473
474  if ( (NULL == theDevice) || (NULL == inSubTree) )
475    return false;
476
477  numPowerTreeNodes = inSubTree->getCount ();
478
479  // iterate through the power tree to find a home for this device
480
481  for ( i = 0; i < numPowerTreeNodes; i++ ) {
482
483    entry =  (OSDictionary *) inSubTree->getObject (i);
484
485    matchingDictionary = (OSDictionary *) entry->getObject ("device");
486    providerDictionary = (OSDictionary *) entry->getObject ("provider");
487
488    deviceMatch = true; // if no matching dictionary, this is not a criteria and so must match
489    if ( matchingDictionary ) {
490      deviceMatch = false;
491      if ( NULL != (deviceDictionary = theDevice->dictionaryWithProperties ())) {
492        deviceMatch = deviceDictionary->isEqualTo ( matchingDictionary, matchingDictionary );
493        deviceDictionary->release ();
494      }
495    }
496
497    providerMatch = true; // we indicate a match if there is no nub or provider
498    if ( theNub && providerDictionary ) {
499      providerMatch = false;
500      if ( NULL != (nubDictionary = theNub->dictionaryWithProperties ()) ) {
501        providerMatch = nubDictionary->isEqualTo ( providerDictionary,  providerDictionary );
502        nubDictionary->release ();
503      }
504    }
505
506    multiParentMatch = true; // again we indicate a match if there is no multi-parent node
507    if (deviceMatch && providerMatch) {
508      if (NULL != multipleParentKeyValue) {
509        OSNumber * aNumber = (OSNumber *) entry->getObject ("multiple-parent");
510        multiParentMatch   = (NULL != aNumber) ? multipleParentKeyValue->isEqualTo (aNumber) : false;
511      }
512    }
513
514    nodeFound = (deviceMatch && providerMatch && multiParentMatch);
515
516    // if the power tree specifies a provider dictionary but theNub is
517    // NULL then we cannot match with this entry.
518
519    if ( theNub == NULL && providerDictionary != NULL )
520      nodeFound = false;
521
522    // if this node is THE ONE...then register the device
523
524    if ( nodeFound ) {
525      if (RegisterServiceInTree (theDevice, entry, theParent, theNub) ) {
526
527        if ( kIOLogPower & gIOKitDebug)
528          IOLog ("PMRegisterDevice/CheckSubTree - service registered!\n");
529
530	numInstancesRegistered++;
531
532	// determine if we need to search for additional nodes for this item
533	multipleParentKeyValue = (OSNumber *) entry->getObject ("multiple-parent");
534      }
535      else
536	nodeFound = false;
537    }
538
539    continueSearch = ( (false == nodeFound) || (NULL != multipleParentKeyValue) );
540
541    if ( continueSearch && (NULL != (children = (OSArray *) entry->getObject ("children"))) ) {
542      nodeFound = CheckSubTree ( children, theNub, theDevice, entry );
543      continueSearch = ( (false == nodeFound) || (NULL != multipleParentKeyValue) );
544    }
545
546    if ( false == continueSearch )
547      break;
548  }
549
550  return ( nodeFound );
551}
552
553//*********************************************************************************
554// RegisterServiceInTree
555//
556// Register a device at the specified node of our power tree.
557//*********************************************************************************
558
559bool IOPlatformExpert::RegisterServiceInTree (IOService * theService, OSDictionary * theTreeNode, OSDictionary * theTreeParentNode, IOService * theProvider)
560{
561  IOService *    aService;
562  bool           registered = false;
563  OSArray *      children;
564  unsigned int   numChildren;
565  OSDictionary * child;
566
567  // make sure someone is not already registered here
568
569  if ( NULL == theTreeNode->getObject ("service") ) {
570
571    if ( theTreeNode->setObject ("service", OSDynamicCast ( OSObject, theService)) ) {
572
573      // 1. CHILDREN ------------------
574
575      // we registered the node in the tree...now if the node has children
576      // registered we must tell this service to add them.
577
578      if ( NULL != (children = (OSArray *) theTreeNode->getObject ("children")) ) {
579        numChildren = children->getCount ();
580        for ( unsigned int i = 0; i < numChildren; i++ ) {
581          if ( NULL != (child = (OSDictionary *) children->getObject (i)) ) {
582            if ( NULL != (aService = (IOService *) child->getObject ("service")) )
583              theService->addPowerChild (aService);
584          }
585        }
586      }
587
588      // 2. PARENT --------------------
589
590      // also we must notify the parent of this node (if a registered service
591      // exists there) of a new child.
592
593      if ( theTreeParentNode ) {
594        if ( NULL != (aService = (IOService *) theTreeParentNode->getObject ("service")) )
595          if (aService != theProvider)
596            aService->addPowerChild (theService);
597      }
598
599      registered = true;
600    }
601  }
602
603  return registered;
604}
605
606//*********************************************************************************
607// printDictionaryKeys
608//
609// Print the keys for the given dictionary and selected contents.
610//*********************************************************************************
611void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg)
612{
613  OSCollectionIterator * mcoll = OSCollectionIterator::withCollection (inDictionary);
614  OSSymbol * mkey;
615  OSString * ioClass;
616  unsigned int i = 0;
617
618  mcoll->reset ();
619
620  mkey = OSDynamicCast (OSSymbol, mcoll->getNextObject ());
621
622  while (mkey) {
623
624    // kprintf ("dictionary key #%d: %s\n", i, mkey->getCStringNoCopy () );
625
626    // if this is the IOClass key, print it's contents
627
628    if ( mkey->isEqualTo ("IOClass") ) {
629      ioClass = (OSString *) inDictionary->getObject ("IOClass");
630      if ( ioClass ) IOLog ("%s IOClass is %s\n", inMsg, ioClass->getCStringNoCopy () );
631    }
632
633    // if this is an IOProviderClass key print it
634
635    if ( mkey->isEqualTo ("IOProviderClass") ) {
636      ioClass = (OSString *) inDictionary->getObject ("IOProviderClass");
637      if ( ioClass ) IOLog ("%s IOProviderClass is %s\n", inMsg, ioClass->getCStringNoCopy () );
638
639    }
640
641    // also print IONameMatch keys
642    if ( mkey->isEqualTo ("IONameMatch") ) {
643      ioClass = (OSString *) inDictionary->getObject ("IONameMatch");
644      if ( ioClass ) IOLog ("%s IONameMatch is %s\n", inMsg, ioClass->getCStringNoCopy () );
645    }
646
647    // also print IONameMatched keys
648
649    if ( mkey->isEqualTo ("IONameMatched") ) {
650      ioClass = (OSString *) inDictionary->getObject ("IONameMatched");
651      if ( ioClass ) IOLog ("%s IONameMatched is %s\n", inMsg, ioClass->getCStringNoCopy () );
652    }
653
654#if 0
655    // print clock-id
656
657    if ( mkey->isEqualTo ("AAPL,clock-id") ) {
658      char * cstr;
659      cstr = getCStringForObject (inDictionary->getObject ("AAPL,clock-id"));
660      if (cstr)
661        kprintf (" ===> AAPL,clock-id is %s\n", cstr );
662    }
663#endif
664
665    // print name
666
667    if ( mkey->isEqualTo ("name") ) {
668      char nameStr[64];
669      nameStr[0] = 0;
670      getCStringForObject(inDictionary->getObject("name"), nameStr,
671		      sizeof(nameStr));
672      if (strlen(nameStr) > 0)
673        IOLog ("%s name is %s\n", inMsg, nameStr);
674    }
675
676    mkey = (OSSymbol *) mcoll->getNextObject ();
677
678    i++;
679  }
680
681  mcoll->release ();
682}
683
684static void
685getCStringForObject(OSObject *inObj, char *outStr, size_t outStrLen)
686{
687   char * buffer;
688   unsigned int    len, i;
689
690   if ( (NULL == inObj) || (NULL == outStr))
691     return;
692
693   char * objString = (char *) (inObj->getMetaClass())->getClassName();
694
695   if ((0 == strncmp(objString, "OSString", sizeof("OSString"))) ||
696		   (0 == strncmp(objString, "OSSymbol", sizeof("OSSymbol"))))
697     strlcpy(outStr, ((OSString *)inObj)->getCStringNoCopy(), outStrLen);
698
699   else if (0 == strncmp(objString, "OSData", sizeof("OSData"))) {
700     len = ((OSData *)inObj)->getLength();
701     buffer = (char *)((OSData *)inObj)->getBytesNoCopy();
702     if (buffer && (len > 0)) {
703       for (i=0; i < len; i++) {
704         outStr[i] = buffer[i];
705       }
706       outStr[len] = 0;
707     }
708   }
709}
710
711/* IOShutdownNotificationsTimedOut
712 * - Called from a timer installed by PEHaltRestart
713 */
714static void IOShutdownNotificationsTimedOut(
715    thread_call_param_t p0,
716    thread_call_param_t p1)
717{
718    int type = (int)(long)p0;
719
720    /* 30 seconds has elapsed - resume shutdown */
721    if(gIOPlatform) gIOPlatform->haltRestart(type);
722}
723
724
725extern "C" {
726
727/*
728 * Callouts from BSD for machine name & model
729 */
730
731boolean_t PEGetMachineName( char * name, int maxLength )
732{
733    if( gIOPlatform)
734	return( gIOPlatform->getMachineName( name, maxLength ));
735    else
736	return( false );
737}
738
739boolean_t PEGetModelName( char * name, int maxLength )
740{
741    if( gIOPlatform)
742	return( gIOPlatform->getModelName( name, maxLength ));
743    else
744	return( false );
745}
746
747int PEGetPlatformEpoch(void)
748{
749    if( gIOPlatform)
750	return( gIOPlatform->getBootROMType());
751    else
752	return( -1 );
753}
754
755int PEHaltRestart(unsigned int type)
756{
757  IOPMrootDomain    *pmRootDomain;
758  AbsoluteTime      deadline;
759  thread_call_t     shutdown_hang;
760
761  if(type == kPEHaltCPU || type == kPERestartCPU || type == kPEUPSDelayHaltCPU)
762  {
763    pmRootDomain = IOService::getPMRootDomain();
764    /* Notify IOKit PM clients of shutdown/restart
765       Clients subscribe to this message with a call to
766       IOService::registerInterest()
767    */
768
769    /* Spawn a thread that will panic in 30 seconds.
770       If all goes well the machine will be off by the time
771       the timer expires.
772     */
773    shutdown_hang = thread_call_allocate( &IOShutdownNotificationsTimedOut,
774                        (thread_call_param_t)(uintptr_t) type);
775    clock_interval_to_deadline( 30, kSecondScale, &deadline );
776    thread_call_enter1_delayed( shutdown_hang, 0, deadline );
777
778    pmRootDomain->handlePlatformHaltRestart(type);
779    /* This notification should have few clients who all do
780       their work synchronously.
781
782       In this "shutdown notification" context we don't give
783       drivers the option of working asynchronously and responding
784       later. PM internals make it very hard to wait for asynchronous
785       replies.
786     */
787   }
788
789  if (gIOPlatform) return gIOPlatform->haltRestart(type);
790  else return -1;
791}
792
793UInt32 PESavePanicInfo(UInt8 *buffer, UInt32 length)
794{
795  if (gIOPlatform != 0) return gIOPlatform->savePanicInfo(buffer, length);
796  else return 0;
797}
798
799
800
801inline static int init_gIOOptionsEntry(void)
802{
803    IORegistryEntry *entry;
804    void *nvram_entry;
805    volatile void **options;
806    int ret = -1;
807
808    if (gIOOptionsEntry)
809        return 0;
810
811    entry = IORegistryEntry::fromPath( "/options", gIODTPlane );
812    if (!entry)
813        return -1;
814
815    nvram_entry = (void *) OSDynamicCast(IODTNVRAM, entry);
816    if (!nvram_entry)
817        goto release;
818
819    options = (volatile void **) &gIOOptionsEntry;
820    if (!OSCompareAndSwapPtr(NULL, nvram_entry, options)) {
821        ret = 0;
822        goto release;
823    }
824
825    return 0;
826
827release:
828    entry->release();
829    return ret;
830
831}
832
833/* pass in a NULL value if you just want to figure out the len */
834boolean_t PEReadNVRAMProperty(const char *symbol, void *value,
835                              unsigned int *len)
836{
837    OSObject  *obj;
838    OSData *data;
839    unsigned int vlen;
840
841    if (!symbol || !len)
842        goto err;
843
844    if (init_gIOOptionsEntry() < 0)
845        goto err;
846
847    vlen = *len;
848    *len = 0;
849
850    obj = gIOOptionsEntry->getProperty(symbol);
851    if (!obj)
852        goto err;
853
854    /* convert to data */
855    data = OSDynamicCast(OSData, obj);
856    if (!data)
857        goto err;
858
859    *len  = data->getLength();
860    vlen  = min(vlen, *len);
861    if (value && vlen)
862        memcpy((void *) value, data->getBytesNoCopy(), vlen);
863
864    return TRUE;
865
866err:
867    return FALSE;
868}
869
870
871boolean_t PEWriteNVRAMProperty(const char *symbol, const void *value,
872                               const unsigned int len)
873{
874    const OSSymbol *sym;
875    OSData *data;
876    bool ret = false;
877
878    if (!symbol || !value || !len)
879        goto err;
880
881    if (init_gIOOptionsEntry() < 0)
882        goto err;
883
884    sym = OSSymbol::withCStringNoCopy(symbol);
885    if (!sym)
886        goto err;
887
888    data = OSData::withBytes((void *) value, len);
889    if (!data)
890        goto sym_done;
891
892    ret = gIOOptionsEntry->setProperty(sym, data);
893    data->release();
894
895sym_done:
896    sym->release();
897
898    if (ret == true) {
899        gIOOptionsEntry->sync();
900        return TRUE;
901    }
902
903err:
904    return FALSE;
905}
906
907
908boolean_t PERemoveNVRAMProperty(const char *symbol)
909{
910    const OSSymbol *sym;
911
912    if (!symbol)
913        goto err;
914
915    if (init_gIOOptionsEntry() < 0)
916        goto err;
917
918    sym = OSSymbol::withCStringNoCopy(symbol);
919    if (!sym)
920        goto err;
921
922    gIOOptionsEntry->removeProperty(sym);
923
924    sym->release();
925
926    gIOOptionsEntry->sync();
927    return TRUE;
928
929err:
930    return FALSE;
931
932}
933
934long PEGetGMTTimeOfDay(void)
935{
936	long	result = 0;
937
938	if( gIOPlatform)		result = gIOPlatform->getGMTTimeOfDay();
939
940	return (result);
941}
942
943void PESetGMTTimeOfDay(long secs)
944{
945    if( gIOPlatform)		gIOPlatform->setGMTTimeOfDay(secs);
946}
947
948} /* extern "C" */
949
950void IOPlatformExpert::registerNVRAMController(IONVRAMController * caller)
951{
952    OSData *          data;
953    IORegistryEntry * entry;
954    OSString *        string = 0;
955    uuid_string_t     uuid;
956
957    entry = IORegistryEntry::fromPath( "/efi/platform", gIODTPlane );
958    if ( entry )
959    {
960        data = OSDynamicCast( OSData, entry->getProperty( "system-id" ) );
961        if ( data && data->getLength( ) == 16 )
962        {
963            SHA1_CTX     context;
964            uint8_t      digest[ SHA_DIGEST_LENGTH ];
965            const uuid_t space = { 0x2A, 0x06, 0x19, 0x90, 0xD3, 0x8D, 0x44, 0x40, 0xA1, 0x39, 0xC4, 0x97, 0x70, 0x37, 0x65, 0xAC };
966
967            SHA1Init( &context );
968            SHA1Update( &context, space, sizeof( space ) );
969            SHA1Update( &context, data->getBytesNoCopy( ), data->getLength( ) );
970            SHA1Final( digest, &context );
971
972            digest[ 6 ] = ( digest[ 6 ] & 0x0F ) | 0x50;
973            digest[ 8 ] = ( digest[ 8 ] & 0x3F ) | 0x80;
974
975            uuid_unparse( digest, uuid );
976            string = OSString::withCString( uuid );
977        }
978
979        entry->release( );
980    }
981
982    if ( string == 0 )
983    {
984        entry = IORegistryEntry::fromPath( "/options", gIODTPlane );
985        if ( entry )
986        {
987            data = OSDynamicCast( OSData, entry->getProperty( "platform-uuid" ) );
988            if ( data && data->getLength( ) == sizeof( uuid_t ) )
989            {
990                uuid_unparse( ( uint8_t * ) data->getBytesNoCopy( ), uuid );
991                string = OSString::withCString( uuid );
992            }
993
994            entry->release( );
995        }
996    }
997
998    if ( string )
999    {
1000        getProvider( )->setProperty( kIOPlatformUUIDKey, string );
1001        publishResource( kIOPlatformUUIDKey, string );
1002
1003        string->release( );
1004    }
1005
1006    publishResource("IONVRAM");
1007}
1008
1009IOReturn IOPlatformExpert::callPlatformFunction(const OSSymbol *functionName,
1010						bool waitForFunction,
1011						void *param1, void *param2,
1012						void *param3, void *param4)
1013{
1014  IOService *service, *_resources;
1015
1016  if (waitForFunction) {
1017    _resources = waitForService(resourceMatching(functionName));
1018  } else {
1019    _resources = getResourceService();
1020  }
1021  if (_resources == 0) return kIOReturnUnsupported;
1022
1023  service = OSDynamicCast(IOService, _resources->getProperty(functionName));
1024  if (service == 0) return kIOReturnUnsupported;
1025
1026  return service->callPlatformFunction(functionName, waitForFunction,
1027				       param1, param2, param3, param4);
1028}
1029
1030IOByteCount IOPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length)
1031{
1032  return 0;
1033}
1034
1035/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1036
1037#undef super
1038#define super IOPlatformExpert
1039
1040OSDefineMetaClassAndAbstractStructors( IODTPlatformExpert, IOPlatformExpert )
1041
1042OSMetaClassDefineReservedUnused(IODTPlatformExpert,  0);
1043OSMetaClassDefineReservedUnused(IODTPlatformExpert,  1);
1044OSMetaClassDefineReservedUnused(IODTPlatformExpert,  2);
1045OSMetaClassDefineReservedUnused(IODTPlatformExpert,  3);
1046OSMetaClassDefineReservedUnused(IODTPlatformExpert,  4);
1047OSMetaClassDefineReservedUnused(IODTPlatformExpert,  5);
1048OSMetaClassDefineReservedUnused(IODTPlatformExpert,  6);
1049OSMetaClassDefineReservedUnused(IODTPlatformExpert,  7);
1050
1051/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1052
1053IOService * IODTPlatformExpert::probe( IOService * provider,
1054			       		SInt32 * score )
1055{
1056    if( !super::probe( provider, score))
1057	return( 0 );
1058
1059    // check machine types
1060    if( !provider->compareNames( getProperty( gIONameMatchKey ) ))
1061        return( 0 );
1062
1063    return( this);
1064}
1065
1066bool IODTPlatformExpert::configure( IOService * provider )
1067{
1068    if( !super::configure( provider))
1069	return( false);
1070
1071    processTopLevel( provider );
1072
1073    return( true );
1074}
1075
1076IOService * IODTPlatformExpert::createNub( IORegistryEntry * from )
1077{
1078    IOService *		nub;
1079
1080    nub = new IOPlatformDevice;
1081    if( nub) {
1082	if( !nub->init( from, gIODTPlane )) {
1083	    nub->free();
1084	    nub = 0;
1085	}
1086    }
1087    return( nub);
1088}
1089
1090bool IODTPlatformExpert::createNubs( IOService * parent, OSIterator * iter )
1091{
1092    IORegistryEntry *	next;
1093    IOService *		nub;
1094    bool		ok = true;
1095
1096    if( iter) {
1097	while( (next = (IORegistryEntry *) iter->getNextObject())) {
1098
1099            if( 0 == (nub = createNub( next )))
1100                continue;
1101
1102            nub->attach( parent );
1103            nub->registerService();
1104        }
1105	iter->release();
1106    }
1107
1108    return( ok );
1109}
1110
1111void IODTPlatformExpert::processTopLevel( IORegistryEntry * rootEntry )
1112{
1113    OSIterator * 	kids;
1114    IORegistryEntry *	next;
1115    IORegistryEntry *	cpus;
1116    IORegistryEntry *	options;
1117
1118    // infanticide
1119    kids = IODTFindMatchingEntries( rootEntry, 0, deleteList() );
1120    if( kids) {
1121	while( (next = (IORegistryEntry *)kids->getNextObject())) {
1122	    next->detachAll( gIODTPlane);
1123	}
1124	kids->release();
1125    }
1126
1127    // Publish an IODTNVRAM class on /options.
1128    options = rootEntry->childFromPath("options", gIODTPlane);
1129    if (options) {
1130      dtNVRAM = new IODTNVRAM;
1131      if (dtNVRAM) {
1132        if (!dtNVRAM->init(options, gIODTPlane)) {
1133	  dtNVRAM->release();
1134	  dtNVRAM = 0;
1135        } else {
1136	  dtNVRAM->attach(this);
1137	  dtNVRAM->registerService();
1138	}
1139      }
1140    }
1141
1142    // Publish the cpus.
1143    cpus = rootEntry->childFromPath( "cpus", gIODTPlane);
1144    if ( cpus)
1145      createNubs( this, IODTFindMatchingEntries( cpus, kIODTExclusive, 0));
1146
1147    // publish top level, minus excludeList
1148    createNubs( this, IODTFindMatchingEntries( rootEntry, kIODTExclusive, excludeList()));
1149}
1150
1151IOReturn IODTPlatformExpert::getNubResources( IOService * nub )
1152{
1153  if( nub->getDeviceMemory())
1154    return( kIOReturnSuccess );
1155
1156  IODTResolveAddressing( nub, "reg", 0);
1157
1158  return( kIOReturnSuccess);
1159}
1160
1161bool IODTPlatformExpert::compareNubName( const IOService * nub,
1162				OSString * name, OSString ** matched ) const
1163{
1164    return( IODTCompareNubName( nub, name, matched )
1165	  || super::compareNubName( nub, name, matched) );
1166}
1167
1168bool IODTPlatformExpert::getModelName( char * name, int maxLength )
1169{
1170    OSData *		prop;
1171    const char *	str;
1172    int			len;
1173    char		c;
1174    bool		ok = false;
1175
1176    maxLength--;
1177
1178    prop = (OSData *) getProvider()->getProperty( gIODTCompatibleKey );
1179    if( prop ) {
1180	str = (const char *) prop->getBytesNoCopy();
1181
1182	if( 0 == strncmp( str, "AAPL,", strlen( "AAPL," ) ))
1183	    str += strlen( "AAPL," );
1184
1185	len = 0;
1186	while( (c = *str++)) {
1187	    if( (c == '/') || (c == ' '))
1188		c = '-';
1189
1190	    name[ len++ ] = c;
1191	    if( len >= maxLength)
1192		break;
1193	}
1194
1195	name[ len ] = 0;
1196	ok = true;
1197    }
1198    return( ok );
1199}
1200
1201bool IODTPlatformExpert::getMachineName( char * name, int maxLength )
1202{
1203    OSData *		prop;
1204    bool		ok = false;
1205
1206    maxLength--;
1207    prop = (OSData *) getProvider()->getProperty( gIODTModelKey );
1208    ok = (0 != prop);
1209
1210    if( ok )
1211	strlcpy( name, (const char *) prop->getBytesNoCopy(), maxLength );
1212
1213    return( ok );
1214}
1215
1216/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1217
1218void IODTPlatformExpert::registerNVRAMController( IONVRAMController * nvram )
1219{
1220  if (dtNVRAM) dtNVRAM->registerNVRAMController(nvram);
1221
1222  super::registerNVRAMController(nvram);
1223}
1224
1225int IODTPlatformExpert::haltRestart(unsigned int type)
1226{
1227  if (dtNVRAM) dtNVRAM->sync();
1228
1229  return super::haltRestart(type);
1230}
1231
1232IOReturn IODTPlatformExpert::readXPRAM(IOByteCount offset, UInt8 * buffer,
1233				       IOByteCount length)
1234{
1235  if (dtNVRAM) return dtNVRAM->readXPRAM(offset, buffer, length);
1236  else return kIOReturnNotReady;
1237}
1238
1239IOReturn IODTPlatformExpert::writeXPRAM(IOByteCount offset, UInt8 * buffer,
1240					IOByteCount length)
1241{
1242  if (dtNVRAM) return dtNVRAM->writeXPRAM(offset, buffer, length);
1243  else return kIOReturnNotReady;
1244}
1245
1246IOReturn IODTPlatformExpert::readNVRAMProperty(
1247	IORegistryEntry * entry,
1248	const OSSymbol ** name, OSData ** value )
1249{
1250  if (dtNVRAM) return dtNVRAM->readNVRAMProperty(entry, name, value);
1251  else return kIOReturnNotReady;
1252}
1253
1254IOReturn IODTPlatformExpert::writeNVRAMProperty(
1255	IORegistryEntry * entry,
1256	const OSSymbol * name, OSData * value )
1257{
1258  if (dtNVRAM) return dtNVRAM->writeNVRAMProperty(entry, name, value);
1259  else return kIOReturnNotReady;
1260}
1261
1262OSDictionary *IODTPlatformExpert::getNVRAMPartitions(void)
1263{
1264  if (dtNVRAM) return dtNVRAM->getNVRAMPartitions();
1265  else return 0;
1266}
1267
1268IOReturn IODTPlatformExpert::readNVRAMPartition(const OSSymbol * partitionID,
1269						IOByteCount offset, UInt8 * buffer,
1270						IOByteCount length)
1271{
1272  if (dtNVRAM) return dtNVRAM->readNVRAMPartition(partitionID, offset,
1273						  buffer, length);
1274  else return kIOReturnNotReady;
1275}
1276
1277IOReturn IODTPlatformExpert::writeNVRAMPartition(const OSSymbol * partitionID,
1278						 IOByteCount offset, UInt8 * buffer,
1279						 IOByteCount length)
1280{
1281  if (dtNVRAM) return dtNVRAM->writeNVRAMPartition(partitionID, offset,
1282						   buffer, length);
1283  else return kIOReturnNotReady;
1284}
1285
1286IOByteCount IODTPlatformExpert::savePanicInfo(UInt8 *buffer, IOByteCount length)
1287{
1288  IOByteCount lengthSaved = 0;
1289
1290  if (dtNVRAM) lengthSaved = dtNVRAM->savePanicInfo(buffer, length);
1291
1292  if (lengthSaved == 0) lengthSaved = super::savePanicInfo(buffer, length);
1293
1294  return lengthSaved;
1295}
1296
1297OSString* IODTPlatformExpert::createSystemSerialNumberString(OSData* myProperty) {
1298    UInt8* serialNumber;
1299    unsigned int serialNumberSize;
1300    unsigned short pos = 0;
1301    char* temp;
1302    char SerialNo[30];
1303
1304    if (myProperty != NULL) {
1305        serialNumberSize = myProperty->getLength();
1306        serialNumber = (UInt8*)(myProperty->getBytesNoCopy());
1307        temp = (char*)serialNumber;
1308        if (serialNumberSize > 0) {
1309            // check to see if this is a CTO serial number...
1310            while (pos < serialNumberSize && temp[pos] != '-') pos++;
1311
1312            if (pos < serialNumberSize) { // there was a hyphen, so it's a CTO serial number
1313                memcpy(SerialNo, serialNumber + 12, 8);
1314                memcpy(&SerialNo[8], serialNumber, 3);
1315                SerialNo[11] = '-';
1316                memcpy(&SerialNo[12], serialNumber + 3, 8);
1317                SerialNo[20] = 0;
1318            } else { // just a normal serial number
1319                memcpy(SerialNo, serialNumber + 13, 8);
1320                memcpy(&SerialNo[8], serialNumber, 3);
1321                SerialNo[11] = 0;
1322            }
1323            return OSString::withCString(SerialNo);
1324        }
1325    }
1326    return NULL;
1327}
1328
1329
1330/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1331
1332#undef super
1333#define super IOService
1334
1335OSDefineMetaClassAndStructors(IOPlatformExpertDevice, IOService)
1336
1337OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  0);
1338OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  1);
1339OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  2);
1340OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  3);
1341
1342/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1343
1344bool IOPlatformExpertDevice::compareName( OSString * name,
1345                                        OSString ** matched ) const
1346{
1347    return( IODTCompareNubName( this, name, matched ));
1348}
1349
1350bool
1351IOPlatformExpertDevice::initWithArgs(
1352                            void * dtTop, void * p2, void * p3, void * p4 )
1353{
1354    IORegistryEntry * 	dt = 0;
1355    bool		ok;
1356
1357    // dtTop may be zero on non- device tree systems
1358    if( dtTop && (dt = IODeviceTreeAlloc( dtTop )))
1359	ok = super::init( dt, gIODTPlane );
1360    else
1361	ok = super::init();
1362
1363    if( !ok)
1364	return( false);
1365
1366    reserved = NULL;
1367    workLoop = IOWorkLoop::workLoop();
1368    if (!workLoop)
1369        return false;
1370
1371    return( true);
1372}
1373
1374IOWorkLoop *IOPlatformExpertDevice::getWorkLoop() const
1375{
1376    return workLoop;
1377}
1378
1379IOReturn IOPlatformExpertDevice::setProperties( OSObject * properties )
1380{
1381    OSDictionary * dictionary;
1382    OSObject *     object;
1383    IOReturn       status;
1384
1385    status = super::setProperties( properties );
1386    if ( status != kIOReturnUnsupported ) return status;
1387
1388    status = IOUserClient::clientHasPrivilege( current_task( ), kIOClientPrivilegeAdministrator );
1389    if ( status != kIOReturnSuccess ) return status;
1390
1391    dictionary = OSDynamicCast( OSDictionary, properties );
1392    if ( dictionary == 0 ) return kIOReturnBadArgument;
1393
1394    object = dictionary->getObject( kIOPlatformUUIDKey );
1395    if ( object )
1396    {
1397        IORegistryEntry * entry;
1398        OSString *        string;
1399        uuid_t            uuid;
1400
1401        string = ( OSString * ) getProperty( kIOPlatformUUIDKey );
1402        if ( string ) return kIOReturnNotPermitted;
1403
1404        string = OSDynamicCast( OSString, object );
1405        if ( string == 0 ) return kIOReturnBadArgument;
1406
1407        status = uuid_parse( string->getCStringNoCopy( ), uuid );
1408        if ( status != 0 ) return kIOReturnBadArgument;
1409
1410        entry = IORegistryEntry::fromPath( "/options", gIODTPlane );
1411        if ( entry )
1412        {
1413            entry->setProperty( "platform-uuid", uuid, sizeof( uuid_t ) );
1414            entry->release( );
1415        }
1416
1417        setProperty( kIOPlatformUUIDKey, string );
1418        publishResource( kIOPlatformUUIDKey, string );
1419
1420        return kIOReturnSuccess;
1421    }
1422
1423    return kIOReturnUnsupported;
1424}
1425
1426void IOPlatformExpertDevice::free()
1427{
1428    if (workLoop)
1429        workLoop->release();
1430}
1431
1432/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1433
1434#undef super
1435#define super IOService
1436
1437OSDefineMetaClassAndStructors(IOPlatformDevice, IOService)
1438
1439OSMetaClassDefineReservedUnused(IOPlatformDevice,  0);
1440OSMetaClassDefineReservedUnused(IOPlatformDevice,  1);
1441OSMetaClassDefineReservedUnused(IOPlatformDevice,  2);
1442OSMetaClassDefineReservedUnused(IOPlatformDevice,  3);
1443
1444/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1445
1446bool IOPlatformDevice::compareName( OSString * name,
1447					OSString ** matched ) const
1448{
1449    return( ((IOPlatformExpert *)getProvider())->
1450		compareNubName( this, name, matched ));
1451}
1452
1453IOService * IOPlatformDevice::matchLocation( IOService * /* client */ )
1454{
1455    return( this );
1456}
1457
1458IOReturn IOPlatformDevice::getResources( void )
1459{
1460    return( ((IOPlatformExpert *)getProvider())->getNubResources( this ));
1461}
1462
1463/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1464
1465/*********************************************************************
1466* IOPanicPlatform class
1467*
1468* If no legitimate IOPlatformDevice matches, this one does and panics
1469* the kernel with a suitable message.
1470*********************************************************************/
1471
1472class IOPanicPlatform : IOPlatformExpert {
1473    OSDeclareDefaultStructors(IOPanicPlatform);
1474
1475public:
1476    bool start(IOService * provider);
1477};
1478
1479
1480OSDefineMetaClassAndStructors(IOPanicPlatform, IOPlatformExpert);
1481
1482
1483bool IOPanicPlatform::start(IOService * provider) {
1484    const char * platform_name = "(unknown platform name)";
1485
1486    if (provider) platform_name = provider->getName();
1487
1488    panic("Unable to find driver for this platform: \"%s\".\n",
1489        platform_name);
1490
1491    return false;
1492}
1493
1494