1/*
2 * Copyright (c) 2000 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/* OSObject.cpp created by gvdl on Fri 1998-11-17 */
29
30#include <libkern/c++/OSObject.h>
31#include <libkern/c++/OSString.h>
32#include <libkern/c++/OSArray.h>
33#include <libkern/c++/OSSerialize.h>
34#include <libkern/c++/OSLib.h>
35#include <libkern/OSDebug.h>
36#include <libkern/c++/OSCPPDebug.h>
37#include <IOKit/IOKitDebug.h>
38#include <libkern/OSAtomic.h>
39
40#include <libkern/c++/OSCollection.h>
41
42#include <kern/queue.h>
43
44__BEGIN_DECLS
45int debug_ivars_size;
46__END_DECLS
47
48#if OSALLOCDEBUG
49#define ACCUMSIZE(s) do { debug_ivars_size += (s); } while(0)
50#else
51#define ACCUMSIZE(s)
52#endif
53
54// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
55/* Class global data */
56OSObject::MetaClass OSObject::gMetaClass;
57const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
58const OSMetaClass * const OSObject::superClass = 0;
59
60/* Class member functions - Can't use defaults */
61OSObject::OSObject()			{ retainCount = 1; }
62OSObject::OSObject(const OSMetaClass *)	{ retainCount = 1; }
63OSObject::~OSObject()			{ }
64const OSMetaClass * OSObject::getMetaClass() const
65    { return &gMetaClass; }
66OSObject *OSObject::MetaClass::alloc() const { return 0; }
67
68/* The OSObject::MetaClass constructor */
69OSObject::MetaClass::MetaClass()
70    : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject))
71    { }
72
73// Virtual Padding
74OSMetaClassDefineReservedUnused(OSObject,  0);
75OSMetaClassDefineReservedUnused(OSObject,  1);
76OSMetaClassDefineReservedUnused(OSObject,  2);
77OSMetaClassDefineReservedUnused(OSObject,  3);
78OSMetaClassDefineReservedUnused(OSObject,  4);
79OSMetaClassDefineReservedUnused(OSObject,  5);
80OSMetaClassDefineReservedUnused(OSObject,  6);
81OSMetaClassDefineReservedUnused(OSObject,  7);
82OSMetaClassDefineReservedUnused(OSObject,  8);
83OSMetaClassDefineReservedUnused(OSObject,  9);
84OSMetaClassDefineReservedUnused(OSObject, 10);
85OSMetaClassDefineReservedUnused(OSObject, 11);
86OSMetaClassDefineReservedUnused(OSObject, 12);
87OSMetaClassDefineReservedUnused(OSObject, 13);
88OSMetaClassDefineReservedUnused(OSObject, 14);
89OSMetaClassDefineReservedUnused(OSObject, 15);
90
91static const char *getClassName(const OSObject *obj)
92{
93    const OSMetaClass *meta = obj->getMetaClass();
94    return (meta) ? meta->getClassName() : "unknown class?";
95}
96
97bool OSObject::init()
98    { return true; }
99
100void OSObject::free()
101{
102    const OSMetaClass *meta = getMetaClass();
103
104    if (meta)
105	meta->instanceDestructed();
106    delete this;
107}
108
109int OSObject::getRetainCount() const
110{
111    return (int) ((UInt16) retainCount);
112}
113
114void OSObject::taggedRetain(const void *tag) const
115{
116    volatile UInt32 *countP = (volatile UInt32 *) &retainCount;
117    UInt32 inc = 1;
118    UInt32 origCount;
119    UInt32 newCount;
120
121    // Increment the collection bucket.
122    if ((const void *) OSTypeID(OSCollection) == tag)
123	inc |= (1UL<<16);
124
125    do {
126	origCount = *countP;
127        if ( ((UInt16) origCount | 0x1) == 0xffff ) {
128            const char *msg;
129            if (origCount & 0x1) {
130                // If count == 0xffff that means we are freeing now so we can
131                // just return obviously somebody is cleaning up dangling
132                // references.
133                msg = "Attempting to retain a freed object";
134            }
135            else {
136                // If count == 0xfffe then we have wrapped our reference count.
137                // We should stop counting now as this reference must be
138                // leaked rather than accidently wrapping around the clock and
139                // freeing a very active object later.
140
141#if !DEBUG
142		break;	// Break out of update loop which pegs the reference
143#else /* DEBUG */
144                // @@@ gvdl: eventually need to make this panic optional
145                // based on a boot argument i.e. debug= boot flag
146                msg = "About to wrap the reference count, reference leak?";
147#endif /* !DEBUG */
148            }
149            panic("OSObject::refcount: %s", msg);
150        }
151
152	newCount = origCount + inc;
153    } while (!OSCompareAndSwap(origCount, newCount, const_cast<UInt32 *>(countP)));
154}
155
156void OSObject::taggedRelease(const void *tag) const
157{
158    taggedRelease(tag, 1);
159}
160
161void OSObject::taggedRelease(const void *tag, const int when) const
162{
163    volatile UInt32 *countP = (volatile UInt32 *) &retainCount;
164    UInt32 dec = 1;
165    UInt32 origCount;
166    UInt32 newCount;
167    UInt32 actualCount;
168
169    // Increment the collection bucket.
170    if ((const void *) OSTypeID(OSCollection) == tag)
171	dec |= (1UL<<16);
172
173    do {
174	origCount = *countP;
175
176        if ( ((UInt16) origCount | 0x1) == 0xffff ) {
177            if (origCount & 0x1) {
178                // If count == 0xffff that means we are freeing now so we can
179                // just return obviously somebody is cleaning up some dangling
180                // references.  So we blow out immediately.
181                return;
182            }
183            else {
184                // If count == 0xfffe then we have wrapped our reference
185                // count.  We should stop counting now as this reference must be
186                // leaked rather than accidently freeing an active object later.
187
188#if !DEBUG
189		return;	// return out of function which pegs the reference
190#else /* DEBUG */
191                // @@@ gvdl: eventually need to make this panic optional
192                // based on a boot argument i.e. debug= boot flag
193                panic("OSObject::refcount: %s",
194                      "About to unreference a pegged object, reference leak?");
195#endif /* !DEBUG */
196            }
197        }
198	actualCount = origCount - dec;
199        if ((UInt16) actualCount < when)
200            newCount = 0xffff;
201        else
202            newCount = actualCount;
203
204    } while (!OSCompareAndSwap(origCount, newCount, const_cast<UInt32 *>(countP)));
205
206    //
207    // This panic means that we have just attempted to release an object
208    // whose retain count has gone to less than the number of collections
209    // it is a member off.  Take a panic immediately.
210    // In fact the panic MAY not be a registry corruption but it is
211    // ALWAYS the wrong thing to do.  I call it a registry corruption 'cause
212    // the registry is the biggest single use of a network of collections.
213    //
214// xxx - this error message is overly-specific;
215// xxx - any code in the kernel could trip this,
216// xxx - and it applies as noted to all collections, not just the registry
217    if ((UInt16) actualCount < (actualCount >> 16)) {
218        panic("A kext releasing a(n) %s has corrupted the registry.",
219            getClassName(this));
220    }
221
222    // Check for a 'free' condition and that if we are first through
223    if (newCount == 0xffff) {
224        (const_cast<OSObject *>(this))->free();
225    }
226}
227
228void OSObject::release() const
229{
230    taggedRelease(0);
231}
232
233void OSObject::retain() const
234{
235    taggedRetain(0);
236}
237
238void OSObject::release(int when) const
239{
240    taggedRelease(0, when);
241}
242
243bool OSObject::serialize(OSSerialize *s) const
244{
245    char cstr[128];
246    bool ok;
247
248    snprintf(cstr, sizeof(cstr), "%s is not serializable", getClassName(this));
249
250    OSString * str;
251    str = OSString::withCStringNoCopy(cstr);
252    if (!str) return false;
253
254    ok = str->serialize(s);
255    str->release();
256
257    return (ok);
258}
259
260
261thread_t gOSObjectTrackThread;
262
263queue_head_t gOSObjectTrackList =
264    { (queue_t) &gOSObjectTrackList, (queue_t) &gOSObjectTrackList };
265
266lck_spin_t gOSObjectTrackLock;
267
268OSArray * OSFlushObjectTrackList(void)
269{
270    OSArray *     array;
271    queue_entry_t next;
272
273    array = OSArray::withCapacity(16);
274
275    lck_spin_lock(&gOSObjectTrackLock);
276    while (!queue_empty(&gOSObjectTrackList))
277    {
278	next = queue_first(&gOSObjectTrackList);
279	remque(next);
280	lck_spin_unlock(&gOSObjectTrackLock);
281	array->setObject((OSObject *) (next + 1));
282	lck_spin_lock(&gOSObjectTrackLock);
283    }
284    lck_spin_unlock(&gOSObjectTrackLock);
285
286    return (array);
287}
288
289struct OSObjectTracking
290{
291    queue_chain_t link;
292    void *	  bt[14];
293};
294
295void *OSObject::operator new(size_t size)
296{
297    size_t tracking        = (gIOKitDebug & kOSTraceObjectAlloc)
298			   ? sizeof(OSObjectTracking) : 0;
299    OSObjectTracking * mem = (OSObjectTracking *) kalloc(size + tracking);
300
301    assert(mem);
302
303    if (tracking)
304    {
305	if ((((thread_t) 1) == gOSObjectTrackThread) || (current_thread() == gOSObjectTrackThread))
306	{
307	    (void) OSBacktrace(&mem->bt[0], sizeof(mem->bt) / sizeof(mem->bt[0]));
308	    lck_spin_lock(&gOSObjectTrackLock);
309	    enqueue_tail(&gOSObjectTrackList, &mem->link);
310	    lck_spin_unlock(&gOSObjectTrackLock);
311	}
312	else
313	    mem->link.next = 0;
314	mem++;
315    }
316
317    bzero(mem, size);
318
319    ACCUMSIZE(size);
320
321    return (void *) mem;
322}
323
324void OSObject::operator delete(void *_mem, size_t size)
325{
326    size_t             tracking = (gIOKitDebug & kOSTraceObjectAlloc)
327				? sizeof(OSObjectTracking) : 0;
328    OSObjectTracking * mem      = (OSObjectTracking *) _mem;
329
330    if (!mem)
331	return;
332
333    if (tracking)
334    {
335	mem--;
336	if (mem->link.next)
337	{
338	    lck_spin_lock(&gOSObjectTrackLock);
339	    remque(&mem->link);
340	    lck_spin_unlock(&gOSObjectTrackLock);
341	}
342    }
343
344    kfree(mem, size + tracking);
345
346    ACCUMSIZE(-size);
347}
348