/*
 * Copyright (c) 1998-2007 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#include <IOKit/IOSubMemoryDescriptor.h>
#include <IOKit/IOLib.h>

#include "IOKitKernelInternal.h"

#define super IOMemoryDescriptor

OSDefineMetaClassAndStructors(IOSubMemoryDescriptor, IOMemoryDescriptor)

IOReturn
IOSubMemoryDescriptor::redirect( task_t safeTask, bool doRedirect )
{
#ifdef __LP64__
	super::redirect( safeTask, doRedirect );
#endif /* __LP64__ */
	return _parent->redirect( safeTask, doRedirect );
}

IOSubMemoryDescriptor *
IOSubMemoryDescriptor::withSubRange(IOMemoryDescriptor *        of,
    IOByteCount             offset,
    IOByteCount             length,
    IOOptionBits            options)
{
	IOSubMemoryDescriptor *self = new IOSubMemoryDescriptor;

	if (self && !self->initSubRange(of, offset, length, (IODirection) options)) {
		self->release();
		self = NULL;
	}
	return self;
}

bool
IOSubMemoryDescriptor::initSubRange( IOMemoryDescriptor * parent,
    IOByteCount offset, IOByteCount length,
    IODirection direction )
{
	if (parent && ((offset + length) > parent->getLength())) {
		return false;
	}

	/*
	 * We can check the _parent instance variable before having ever set it
	 * to an initial value because I/O Kit guarantees that all our instance
	 * variables are zeroed on an object's allocation.
	 */

	if (!_parent) {
		if (!super::init()) {
			return false;
		}
	} else {
		/*
		 * An existing memory descriptor is being retargeted to
		 * point to somewhere else.  Clean up our present state.
		 */

		_parent->release();
	}

	if (parent) {
		parent->retain();
		_tag    = parent->getTag();
	} else {
		_tag    = 0;
	}
	_parent     = parent;
	_start      = offset;
	_length     = length;
	_flags      = direction;
	_flags |= kIOMemoryThreadSafe;

#ifndef __LP64__
	_direction  = (IODirection) (_flags & kIOMemoryDirectionMask);
#endif /* !__LP64__ */

	return true;
}

void
IOSubMemoryDescriptor::free( void )
{
	if (_parent) {
		_parent->release();
	}

	super::free();
}

addr64_t
IOSubMemoryDescriptor::getPhysicalSegment(IOByteCount offset, IOByteCount * length, IOOptionBits options)
{
	addr64_t    address;
	IOByteCount actualLength;

	assert(offset <= _length);

	if (length) {
		*length = 0;
	}

	if (offset >= _length) {
		return 0;
	}

	address = _parent->getPhysicalSegment( offset + _start, &actualLength, options );

	if (address && length) {
		*length = min( _length - offset, actualLength );
	}

	return address;
}

IOReturn
IOSubMemoryDescriptor::setPurgeable( IOOptionBits newState,
    IOOptionBits * oldState )
{
	IOReturn err;

	err = _parent->setPurgeable( newState, oldState );

	return err;
}

IOReturn
IOSubMemoryDescriptor::setOwnership( task_t newOwner,
    int newLedgerTag,
    IOOptionBits newLedgerOptions )
{
	IOReturn err;

	if (iokit_iomd_setownership_enabled == FALSE) {
		return kIOReturnUnsupported;
	}

	err = _parent->setOwnership( newOwner, newLedgerTag, newLedgerOptions );

	return err;
}

IOReturn
IOSubMemoryDescriptor::prepare(
	IODirection forDirection)
{
	IOReturn    err;

	err = _parent->prepare( forDirection);

	return err;
}

IOReturn
IOSubMemoryDescriptor::complete(
	IODirection forDirection)
{
	IOReturn    err;

	err = _parent->complete( forDirection);

	return err;
}

IOMemoryMap *
IOSubMemoryDescriptor::makeMapping(
	IOMemoryDescriptor *    owner,
	task_t                  intoTask,
	IOVirtualAddress        address,
	IOOptionBits            options,
	IOByteCount             offset,
	IOByteCount             length )
{
	IOMemoryMap * mapping = NULL;

#ifndef __LP64__
	if (!(kIOMap64Bit & options)) {
		panic("IOSubMemoryDescriptor::makeMapping !64bit");
	}
#endif /* !__LP64__ */

	mapping = (IOMemoryMap *) _parent->makeMapping(
		owner,
		intoTask,
		address,
		options, _start + offset, length );

	return mapping;
}

uint64_t
IOSubMemoryDescriptor::getPreparationID( void )
{
	uint64_t pID;

	if (!super::getKernelReserved()) {
		return kIOPreparationIDUnsupported;
	}

	pID = _parent->getPreparationID();
	if (reserved->kernReserved[0] != pID) {
		reserved->kernReserved[0] = pID;
		reserved->preparationID   = kIOPreparationIDUnprepared;
		super::setPreparationID();
	}

	return super::getPreparationID();
}

IOReturn
IOSubMemoryDescriptor::getPageCounts(IOByteCount * residentPageCount,
    IOByteCount * dirtyPageCount)
{
	return _parent->getPageCounts(residentPageCount, dirtyPageCount);
}
