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