1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <IOKit/IOLib.h>
24#include <libkern/c++/OSContainers.h>
25#include <IOKit/IODeviceTreeSupport.h>
26#include <IOKit/IOPlatformExpert.h>
27#include <IOKit/pci/IOPCIDevice.h>
28#include <IOKit/IOHibernatePrivate.h>
29
30#include "IONDRV.h"
31
32#include <IOKit/ndrvsupport/IONDRVFramebuffer.h>
33#include <IOKit/ndrvsupport/IONDRVSupport.h>
34#include <IOKit/ndrvsupport/IONDRVLibraries.h>
35#include <IOKit/graphics/IOGraphicsPrivate.h>
36
37#include <IOKit/assert.h>
38
39#include <pexpert/pexpert.h>
40#include <string.h>
41
42extern "C"
43{
44#include <kern/debug.h>
45
46extern void *kern_os_malloc(size_t size);
47extern void  kern_os_free(void * addr);
48
49#define LOG             if(1) IOLog
50#define LOGNAMEREG      0
51
52#define CHECK_INTERRUPT(s)                                      \
53if( ml_at_interrupt_context()) {                                \
54    /* IOLog("interrupt:%s(%s)\n", __FUNCTION__, s); */         \
55    return( nrLockedErr );                                      \
56}
57
58extern "C" IOReturn _IONDRVLibrariesMappingInitialize( IOService * provider );
59
60/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
61
62//#define EXP(s)        _e ## s
63#define EXP(s)  s
64
65
66/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
67
68OSStatus EXP(RegistryEntryIDCopy)( const RegEntryID * entryID, RegEntryID * to )
69{
70    bcopy( entryID, to, sizeof( RegEntryID) );
71    return (noErr);
72}
73
74OSStatus EXP(RegistryEntryIDInit)( RegEntryID * entryID )
75{
76    MAKE_REG_ENTRY( entryID, 0);
77    return (noErr);
78}
79
80OSStatus EXP(RegistryEntryIDDispose)
81    (RegEntryID * entryID)
82{
83    return (noErr);
84}
85
86/*
87    * Compare EntryID's for equality or if invalid
88    *
89    * If a NULL value is given for either id1 or id2, the other id
90    * is compared with an invalid ID.  If both are NULL, the id's
91    * are consided equal (result = true).
92    *   note: invalid != uninitialized
93    */
94Boolean EXP(RegistryEntryIDCompare)( const RegEntryID * entryID1, const RegEntryID * entryID2 )
95{
96    IORegistryEntry *   regEntry1;
97    IORegistryEntry *   regEntry2;
98
99    if (entryID1)
100    {
101        REG_ENTRY_TO_OBJ_RET( entryID1, regEntry1, false)
102    }
103    else
104        regEntry1 = 0;
105
106    if (entryID2)
107    {
108        REG_ENTRY_TO_OBJ_RET( entryID2, regEntry2, false)
109    }
110    else
111        regEntry2 = 0;
112
113    return (regEntry1 == regEntry2);
114}
115
116OSStatus EXP(RegistryPropertyGetSize)( const RegEntryID * entryID,
117                                       const RegPropertyName * propertyName,
118                                       RegPropertyValueSize * propertySize )
119{
120    OSStatus            err = noErr;
121    OSData *            prop;
122
123    CHECK_INTERRUPT(propertyName)
124    REG_ENTRY_TO_PT( entryID, regEntry)
125
126    if (!strcmp(kPropertyAAPLAddress, propertyName))
127        _IONDRVLibrariesMappingInitialize((IOService *) regEntry);
128
129    prop = OSDynamicCast( OSData, regEntry->getProperty( propertyName));
130    if (prop)
131        *propertySize = prop->getLength();
132    else
133        err = nrNotFoundErr;
134
135#if LOGNAMEREG
136    LOG("RegistryPropertyGetSize: %s : %d\n", propertyName, err);
137#endif
138    return (err);
139}
140
141OSStatus EXP(RegistryPropertyGet)(const RegEntryID * entryID,
142                                    const RegPropertyName * propertyName, void *propertyValue,
143                                    RegPropertyValueSize * propertySize)
144{
145    OSStatus            err = noErr;
146    OSData *            prop;
147    UInt32              len;
148
149    CHECK_INTERRUPT(propertyName)
150    REG_ENTRY_TO_PT( entryID, regEntry)
151
152    if (!strcmp(kPropertyAAPLAddress, propertyName))
153        _IONDRVLibrariesMappingInitialize((IOService *) regEntry);
154
155    prop = OSDynamicCast( OSData, regEntry->getProperty( propertyName));
156    if (prop)
157    {
158        len = *propertySize;
159        *propertySize = prop->getLength();
160        len = (len > prop->getLength()) ? prop->getLength() : len;
161        bcopy( prop->getBytesNoCopy(), propertyValue, len);
162#if LOGNAMEREG
163        LOG("value: %08x ", *propertyValue);
164#endif
165
166    }
167    else
168        err = nrNotFoundErr;
169
170#if LOGNAMEREG
171    LOG("RegistryPropertyGet: %s : %d\n", propertyName, err);
172#endif
173    return (err);
174}
175
176OSStatus EXP(RegistryPropertyCreate)( const RegEntryID * entryID, const RegPropertyName * propertyName,
177                                      const void * propertyValue, RegPropertyValueSize propertySize )
178{
179    OSStatus            err = noErr;
180    OSData *            prop;
181
182    CHECK_INTERRUPT(propertyName)
183    REG_ENTRY_TO_PT( entryID, regEntry)
184
185    prop = OSData::withBytes( propertyValue, propertySize );
186
187    if (prop)
188    {
189        regEntry->setProperty( propertyName, prop);
190        prop->release();
191    }
192    else
193        err = nrNotCreatedErr;
194
195#if LOGNAMEREG
196    LOG("RegistryPropertyCreate: %s : %d\n", propertyName, err);
197#endif
198    return (err);
199}
200
201OSStatus EXP(RegistryPropertyDelete)( const RegEntryID * entryID, const RegPropertyName * propertyName )
202{
203    OSStatus            err = noErr;
204    OSObject *          old;
205
206    CHECK_INTERRUPT(propertyName)
207    REG_ENTRY_TO_PT( entryID, regEntry)
208
209    old = regEntry->getProperty(propertyName);
210    if (old)
211        regEntry->removeProperty(propertyName);
212    else
213        err = nrNotFoundErr;
214
215#if LOGNAMEREG
216    LOG("RegistryPropertyDelete: %s : %d\n", propertyName, err);
217#endif
218    return (err);
219}
220
221void IONDRVSetNVRAMPropertyName( IORegistryEntry * regEntry,
222                                    const OSSymbol * sym )
223{
224    regEntry->setProperty( "IONVRAMProperty", /*(OSObject *)*/ sym );
225}
226
227static IOReturn IONDRVSetNVRAMPropertyValue( IORegistryEntry * regEntry,
228        const OSSymbol * name, OSData * value )
229{
230    IOReturn                    err;
231    IODTPlatformExpert *        platform;
232
233    if ((platform = OSDynamicCast(IODTPlatformExpert,
234                                  IOService::getPlatform())))
235        err = platform->writeNVRAMProperty( regEntry, name, value );
236    else
237        err = kIOReturnUnsupported;
238
239    return (err);
240}
241
242OSStatus EXP(RegistryPropertySet)( const RegEntryID * entryID,
243                                    const RegPropertyName * propertyName,
244                                    const void * propertyValue, RegPropertyValueSize propertySize )
245{
246    OSStatus                    err = noErr;
247    OSData *                    prop;
248    const OSSymbol *            sym;
249
250    CHECK_INTERRUPT(propertyName)
251    REG_ENTRY_TO_PT( entryID, regEntry)
252
253    sym = OSSymbol::withCString( propertyName );
254    if (!sym)
255        return (kIOReturnNoMemory);
256
257    prop = OSDynamicCast( OSData, regEntry->getProperty( sym ));
258    if (0 == prop)
259        err = nrNotFoundErr;
260
261    else if ((prop = OSData::withBytes(propertyValue, propertySize)))
262    {
263        regEntry->setProperty( sym, prop);
264
265        if (sym == (const OSSymbol *)
266                regEntry->getProperty("IONVRAMProperty"))
267            err = IONDRVSetNVRAMPropertyValue( regEntry, sym, prop );
268        prop->release();
269    }
270    else
271        err = nrNotCreatedErr;
272
273    sym->release();
274
275#if LOGNAMEREG
276    LOG("RegistryPropertySet: %s : %d\n", propertyName, err);
277#endif
278    return (err);
279}
280
281OSStatus EXP(RegistryPropertyGetMod)(const RegEntryID * entryID,
282                                     const RegPropertyName * propertyName,
283                                     RegPropertyModifiers * mod)
284{
285    const OSSymbol *    sym;
286
287    CHECK_INTERRUPT(propertyName)
288    REG_ENTRY_TO_PT( entryID, regEntry)
289
290    if ((sym = OSDynamicCast(OSSymbol,
291                                regEntry->getProperty("IONVRAMProperty")))
292            && (0 == strcmp(propertyName, sym->getCStringNoCopy())))
293
294        *mod = kNVRAMProperty;
295    else
296        *mod = 0;
297
298    return (noErr);
299}
300
301OSStatus EXP(RegistryPropertySetMod)(const RegEntryID * entryID,
302                                    const RegPropertyName * propertyName,
303                                    RegPropertyModifiers mod )
304{
305    OSStatus            err = noErr;
306    OSData *            data;
307    const OSSymbol *    sym;
308
309    CHECK_INTERRUPT(propertyName)
310    REG_ENTRY_TO_PT( entryID, regEntry)
311
312    if ((mod & kNVRAMProperty)
313            && (sym = OSSymbol::withCString(propertyName)))
314    {
315        if ((data = OSDynamicCast(OSData, regEntry->getProperty(sym))))
316        {
317            err = IONDRVSetNVRAMPropertyValue( regEntry, sym, data );
318            if (kIOReturnSuccess == err)
319                IONDRVSetNVRAMPropertyName( regEntry, sym );
320        }
321        sym->release();
322    }
323
324    return (err);
325}
326
327OSStatus EXP(RegistryPropertyIterateCreate)( const RegEntryID * entryID,
328        OSIterator ** cookie)
329{
330    CHECK_INTERRUPT("")
331    REG_ENTRY_TO_PT( entryID, regEntry)
332
333    // NB. unsynchronized. But should only happen on an owned nub!
334    // Should non OSData be filtered out?
335
336    OSDictionary * dict = regEntry->dictionaryWithProperties();
337    *cookie = OSCollectionIterator::withCollection(dict);
338    if (dict)
339        dict->release();
340
341    if (*cookie)
342        return (noErr);
343    else
344        return (nrNotEnoughMemoryErr);
345}
346
347OSStatus EXP(RegistryPropertyIterateDispose)( OSIterator ** cookie)
348{
349    if (*cookie)
350    {
351        (*cookie)->release();
352        *cookie = NULL;
353        return (noErr);
354    }
355    else
356        return (nrIterationDone);
357}
358
359OSStatus EXP(RegistryPropertyIterate)( OSIterator ** cookie,
360                                    char * name, Boolean * done )
361{
362    const OSSymbol *    key;
363
364    key = (const OSSymbol *) (*cookie)->getNextObject();
365    if (key)
366        strncpy( name, key->getCStringNoCopy(), kRegMaximumPropertyNameLength);
367
368    // Seems to be differences in handling "done".
369    // ATI assumes done = true when getting the last property.
370    // The Book says done is true after last property.
371    // ATI does check err, so this will work.
372    // Control ignores err and checks done.
373
374    *done = (key == 0);
375
376    if (0 != key)
377        return (noErr);
378    else
379        return (nrIterationDone);
380}
381
382OSStatus
383EXP(RegistryEntryIterateCreate)( IORegistryIterator ** cookie)
384{
385    *cookie = IORegistryIterator::iterateOver( gIODTPlane );
386    if (*cookie)
387        return (noErr);
388    else
389        return (nrNotEnoughMemoryErr);
390}
391
392OSStatus
393EXP(RegistryEntryIterateDispose)( IORegistryIterator ** cookie)
394{
395    if (*cookie)
396    {
397        (*cookie)->release();
398        *cookie = NULL;
399        return (noErr);
400    }
401    else
402        return (nrIterationDone);
403}
404
405OSStatus
406EXP(RegistryEntryIterate)( IORegistryIterator **        cookie,
407                        UInt32          /* relationship */,
408                        RegEntryID *    foundEntry,
409                        Boolean *       done)
410{
411    IORegistryEntry *   regEntry;
412
413    // TODO: check requested type of iteration
414    regEntry = (*cookie)->getNextObjectRecursive();
415
416    MAKE_REG_ENTRY( foundEntry, regEntry);
417    *done = (0 == regEntry);
418
419#if LOGNAMEREG
420    if (regEntry)
421        LOG("RegistryEntryIterate: %s\n", regEntry->getName( gIODTPlane ));
422#endif
423
424    if (regEntry)
425        return (noErr);
426    else
427        return (nrNotFoundErr);
428}
429
430OSStatus
431EXP(RegistryCStrEntryToName)( const RegEntryID *        entryID,
432                            RegEntryID *                parentEntry,
433                            char *                      nameComponent,
434                            Boolean *                   done )
435{
436    IORegistryEntry *   regEntry;
437
438    REG_ENTRY_TO_OBJ( entryID, regEntry)
439
440    strncpy( nameComponent, regEntry->getName( gIODTPlane ), kRegMaximumPropertyNameLength );
441    nameComponent[ kRegMaximumPropertyNameLength ] = 0;
442
443    regEntry = regEntry->getParentEntry( gIODTPlane );
444    if (regEntry)
445    {
446        MAKE_REG_ENTRY( parentEntry, regEntry);
447        *done = false;
448    }
449    else
450        *done = true;
451
452    return (noErr);
453}
454
455OSStatus
456EXP(RegistryCStrEntryLookup)( const RegEntryID *        parentEntry,
457                              const RegCStrPathName *   path,
458                              RegEntryID *              newEntry)
459{
460    IOReturn            err;
461    IORegistryEntry *   regEntry = 0;
462    char *              buf;
463    char *              cvtPath;
464    char                c;
465#define kDTRoot         "Devices:device-tree:"
466#define kMacIORoot      "Devices:device-tree:pci:mac-io:"
467
468    if (parentEntry)
469    {
470        REG_ENTRY_TO_OBJ( parentEntry, regEntry)
471    }
472    else
473        regEntry = 0;
474
475    buf = IONew( char, 512 );
476    if (!buf)
477        return (nrNotEnoughMemoryErr);
478
479    cvtPath = buf;
480    if (':' == path[0])
481        path++;
482    else if (0 == strncmp(path, kMacIORoot, strlen(kMacIORoot)))
483    {
484        path += strlen( kMacIORoot ) - 7;
485        regEntry = 0;
486    }
487    else if (0 == strncmp(path, kDTRoot, strlen(kDTRoot)))
488    {
489        path += strlen( kDTRoot ) - 1;
490        regEntry = 0;
491    }
492
493    do
494    {
495        c = *(path++);
496        if (':' == c)
497            c = '/';
498        *(cvtPath++) = c;
499    }
500    while (c != 0);
501
502    if (regEntry)
503        regEntry = regEntry->childFromPath( buf, gIODTPlane );
504    else
505        regEntry = IORegistryEntry::fromPath( buf, gIODTPlane );
506
507    if (regEntry)
508    {
509        MAKE_REG_ENTRY( newEntry, regEntry);
510        regEntry->release();
511        err = noErr;
512    }
513    else
514        err = nrNotFoundErr;
515
516    IODelete( buf, char, 512 );
517
518    return (err);
519}
520
521
522/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
523
524OSStatus EXP(VSLGestalt)( VSLGestaltType selector, UInt32 * response )
525{
526    IOReturn ret;
527
528    if (!response)
529        return (paramErr);
530
531    *response = 0;
532
533    switch (selector)
534    {
535        case kVSLClamshellStateGestaltType:
536            ret = IOGetHardwareClamshellState(response);
537            break;
538        default:
539            ret = gestaltUndefSelectorErr;
540            break;
541    }
542
543    return (ret);
544}
545
546OSErr
547EXP(VSLNewInterruptService)(
548  RegEntryID *            serviceDevice,
549  InterruptServiceType    serviceType,
550  InterruptServiceIDPtr   serviceID)
551{
552    return(IONDRVFramebuffer::VSLNewInterruptService(serviceDevice, serviceType, serviceID));
553}
554
555OSErr
556EXP(VSLDisposeInterruptService)(InterruptServiceIDType serviceID)
557{
558    return(IONDRVFramebuffer::VSLDisposeInterruptService(serviceID));
559}
560
561OSErr
562EXP(VSLDoInterruptService)(InterruptServiceIDType serviceID)
563{
564    return(IONDRVFramebuffer::VSLDoInterruptService(serviceID));
565}
566
567Boolean
568EXP(VSLPrepareCursorForHardwareCursor)(
569  void *                        cursorRef,
570  IOHardwareCursorDescriptor *  hardwareDescriptor,
571  IOHardwareCursorInfo *        hwCursorInfo)
572{
573    return(IONDRVFramebuffer::VSLPrepareCursorForHardwareCursor(cursorRef,
574                                hardwareDescriptor, hwCursorInfo));
575}
576
577/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
578
579}
580
581/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
582
583extern "C" IOReturn _IONDRVLibrariesFinalize( IOService * provider )
584{
585    provider->removeProperty(kPropertyAAPLAddress);
586    provider->removeProperty("AAPL,maps");
587
588    return (kIOReturnSuccess);
589}
590
591extern "C" IOReturn _IONDRVLibrariesMappingInitialize( IOService * provider )
592{
593    // map memory
594    OSData *     data;
595    unsigned int i;
596    IOItemCount  numMaps;
597    OSArray *    maps = 0;
598    data = 0;
599
600    if (provider->getProperty(kPropertyAAPLAddress))
601        return (kIOReturnSuccess);
602
603    numMaps = provider->getDeviceMemoryCount();
604    for (i = 0; i < numMaps; i++)
605    {
606        IODeviceMemory * mem;
607        IOMemoryMap *    map;
608        IOVirtualAddress virtAddress;
609
610        mem = provider->getDeviceMemoryWithIndex(i);
611        if (!mem)
612            continue;
613        if (!maps)
614            maps = OSArray::withCapacity(numMaps);
615        if (!maps)
616            continue;
617
618        map = mem->map();
619        if (!map)
620        {
621//          IOLog("%s: map[%ld] failed\n", provider->getName(), i);
622            maps->setObject(kOSBooleanFalse);           // placeholder
623            continue;
624        }
625
626        maps->setObject(map);
627        map->release();
628
629        virtAddress = map->getVirtualAddress();
630        mem->setMapping(kernel_task, virtAddress, kIOMapInhibitCache);
631        if (!data)
632            data = OSData::withCapacity( numMaps * sizeof( IOVirtualAddress));
633        if (!data)
634            continue;
635        data->appendBytes( &virtAddress, sizeof( IOVirtualAddress));
636    }
637
638    // NDRV aperture vectors
639    if (maps)
640    {
641        provider->setProperty( "AAPL,maps", maps );
642        maps->release();
643    }
644    if (data)
645    {
646        provider->setProperty(kPropertyAAPLAddress, data );
647        data->release();
648    }
649    return (kIOReturnSuccess);
650}
651
652extern "C" IOReturn _IONDRVLibrariesInitialize( IOService * provider )
653{
654    IODTPlatformExpert *        platform;
655    const OSSymbol *            sym;
656    OSData *                    data;
657
658#if NDRVLIBTEST
659    IONDRVLibrariesTest( provider );
660#endif
661
662    // copy nvram property
663
664    if ((platform = OSDynamicCast(IODTPlatformExpert,
665                                  IOService::getPlatform())))
666    {
667        //      IOService::waitForService( IOService::resourceMatching( "IONVRAM" ));
668
669        if (kIOReturnSuccess == platform->readNVRAMProperty(provider,
670                &sym, &data))
671        {
672            IONDRVSetNVRAMPropertyName( provider, sym );
673            provider->setProperty( sym, data);
674            data->release();
675            sym->release();
676        }
677    }
678
679#if VERSION_MAJOR < 9
680    _IONDRVLibrariesMappingInitialize(provider);
681#endif
682
683    return (kIOReturnSuccess);
684}
685
686/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
687