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