File:  [Apple Darwin 0.x] / driverkit / libDriver / IOMemoryContainer.m
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:37:50 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin02
Darwin 0.2 Driver Kit

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  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 1.1 (the "License").  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource 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 OR NON- INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/**
 * IOMemoryContainer.m
 * Copyright 1997-98 Apple Computer Inc. All Rights Reserved.
 *
 * IOMemoryContainer describes client memory. It is constructed and manipulated
 * by MemoryDescriptor classes. It is not "visible" to client and server
 * code. Note that IOMemoryContainer objects do not provide enumeration or
 * positioning services: they only describe a particular range of memory.
 *
 * IOMemoryContainer may be used in both user space and kernel space.
 * The memory described by a user-space IOMemoryContainer can be
 * accessed by the creating task as a collection of logical extents.
 * Kernel processes, after making the memory resident, can access
 * the container memory as either logical or physical extents.
 */
#import <driverkit/IOMemoryContainer.h>
#import <driverkit/generalFuncs.h>
#import <mach/vm_param.h>

/*
 * This is the information that is transferred by the serialization
 * methods. This fixed-length header will be followed by <rangeCount>
 * IORange values.
 */
typedef struct {
    vm_task_t		client;
    unsigned int	rangeCount;
} IOMemoryContainerSerialization;
#define kSerializationSize	(sizeof (IOMemoryContainerSerialization))

@interface IOMemoryContainer(Private)
- (id)			init;
- (id)			free;
@end /* IOMemoryContainer(Private) */

@implementation IOMemoryContainer(Private)
/**
 * Initialize an empty IOMemoryContainer object.
 */
- (id)			init
{
	/*
	 * Initialize all fields to zero (but with one reference).
	 */
	options			= 0;
	retainCount		= 1;
	residencyCount		= 0;
#ifdef KERNEL
	client			= IOVmTaskSelf();
#else
	client			= IO_NULL_VM_TASK;
#endif /* KERNEL */
	rangeCount		= 0;
	totalByteCount		= 0;
	range.logical.start	= 0;
	range.logical.size	= 0;
	return (self);
}

/**
 * Dispose of the object. Note that free ignores the retainCount and
 * referenceCount and always frees the IOMemoryContainer object.
 */
- (id)			free
{
	if (rangeCount > 1 && range.vector != NULL) {
	    if ((options & ioRangeByReference) == 0) {
		IOFree((void *) range.vector, rangeCount * sizeof (IORange));
	    }
	    rangeCount = 0;
	    range.vector = NULL;
	}
	return [super free];
}

@end /* IOMemoryContainer(Private) */


@implementation IOMemoryContainer

/**
 * Create an IOMemoryContainer object for a logical scatter-gather list in the
 * caller's address map. The scatter-gather list is provided in DriverKit
 * IORange format. If byReference is TRUE, the IOMemoryContainer will hold
 * a reference to the range vector (which must remain addressable during the
 * lifetime of the object). If FALSE, the range vector will be copied into
 * the IOMemoryContainer object.
 */
- (id)			initWithIORange
			: (const IORange *) ioRange
	count		: (unsigned int) thisRangeCount
	byReference	: (BOOL) initByReference
{
	unsigned int	size;
	unsigned int	i;
	
	switch (thisRangeCount) {
	case 0:
	    [self initWithAddress
	    			: NULL
	    	    length	: 0
	    	];
	    break;
	case 1:
	    [self initWithAddress
	    			: (void *) ioRange->start
	    	    length	: ioRange->size
	    	];
	    break;
	default:
	    [self init];
	    rangeCount		= thisRangeCount;
	    size		= thisRangeCount * sizeof (IORange);
	    if (initByReference) {
		options		|= ioRangeByReference;
		range.vector	= ioRange;
	    }
	    else {
		range.vector	= IOMalloc(size);
		IOCopyMemory(
		    (void *) ioRange, 		/* Copy from here	*/
		    (void *) range.vector,	/* Copy to here		*/
		    size,			/* Total byte count	*/
		    4				/* Bytes per transfer	*/
		);
	    }
	    for (i = 0; i < thisRangeCount; i++) {
		totalByteCount += ioRange[i].size;
	    }
	    break;
	}
	return (self);
}

/**
 * Create an IOMemoryContainer object for a single logical range.
 */
- (id)			initWithAddress
			: (void *) address
	length		: (unsigned int) length
{
	[self init];
	rangeCount		= 1;
	range.logical.start	= (unsigned int) address;
	range.logical.size	= length;
	totalByteCount		= length;
	return (self);
}


/**
 * Create an IOMemoryContainer object for a logical scatter-gather list in the
 * caller's address map. The scatter-gather list is provided in BSD
 * Unix iov format.
 */
- (id)			initWithIOV
			: (const struct iovec *) iov
	count		: (unsigned int) iovCount
{
	unsigned int	size;
	unsigned int	i;

	switch (iovCount) {
	case 0:
	    [self initWithAddress : NULL
	    	    length	: 0
	    	];
	    break;
	case 1:
	    [self initWithAddress : iov->iov_base
	    	    length	: iov->iov_len
	    	];
	    break;
	default:
	    [self init];
	    range.vector	= IOMalloc(size);
	    rangeCount		= iovCount;
	    size		= iovCount * sizeof (IORange);
	    for (i = 0; i < iovCount; i++, iov++) {
		((IORange *) range.vector)[i].start =
				(unsigned int) iov->iov_base;
		((IORange *) range.vector)[i].size = iov->iov_len;
		totalByteCount	+= iov->iov_len;
	    }
	    break;
	}
	return (self);
}

/**
 * Accessor methods
 */
- (unsigned int)	rangeCount
{
	return (rangeCount);
}

- (unsigned int)	totalByteCount
{
	return (totalByteCount);
}

/**
 * Manage the retain/release reference count. See NSObject for details.
 */
- (unsigned int)	retainCount
{
	return (retainCount);
}

- (id)			retain
{
	++retainCount;
	return (self);
}

- (oneway void)		release
{
	if (--retainCount == 0) {
	    [self free];
	}
}

/**
 * Return one range segment. These return errors if the parameter is
 * incorrect (calling logicalRange on physicalRanges, index out of bounds).
 */

/**
 * Return the logical address and length for the i'th logical range.
 * Return IO_R_INVALID_ARG if the index is outside the allocated range.
 */
- (IOReturn)		logicalRange
			: (IORange *) logicalRange
	index		: (unsigned int) thisIndex
{
	IOReturn	ioReturn;
	
	if (thisIndex >= rangeCount || logicalRange == NULL) {
	    ioReturn		= IO_R_INVALID_ARG;
	}
	else {
	    ioReturn		= IO_R_SUCCESS;
	    if (rangeCount == 1) {
	        *logicalRange = range.logical;
	    }
	    else {
		*logicalRange = range.vector[thisIndex];
	    }
	}
	return (ioReturn);
}
@end /* IOMemoryContainer : Object */

#ifdef KERNEL
@implementation IOMemoryContainer(Kernel)

/*
 * Kernel-specific methods. By default, non-kernel objects contain a NULL
 * vm_task_t value. Kernel tasks will set the client to the task that
 * provided this memory.
 */

- (vm_task_t)		client
{
	return (client);
}

- (void)		setClient
			: (vm_task_t) thisClient
{
	client = thisClient;
}


/**
 * Make the memory described by this IOMemoryContainer resident.
 * This is called by the virtual memory manager and/or file system before
 * starting an I/O request. Residency is an all-or-nothing process. The
 * IOMemoryContainer maintains a reference count: the first caller makes the
 * memory resident; the others just increment the count. This method returns
 * an error status if any range cannot be made resident and all memory will
 * be made pageable. This method may only be called by kernel servers.
 */
- (IOReturn)		wireMemory
			: (BOOL) forReading
{
	IOReturn	ioReturn = IO_R_SUCCESS;
	kern_return_t	status;
	unsigned int	i;
	unsigned int	successCount;
	IORange		thisRange;
	vm_offset_t	rangeStart;
	vm_offset_t	rangeEnd;
	
	/*
	 * Increment the residency counter. If it was not zero when we were
	 * called, we have already made this memory resident, so just exit.
	 * If it was zero, this is the first call and we must make all ranges
	 * resident.
	 */
	if (forReading)
	    options |=  ioWiredForRead;
	else
	    options &= ~ioWiredForRead;

	if (residencyCount++ == 0) {	/* NOTE: AtomicIncrement */
	    successCount = 0;
	    for (i = 0; i < rangeCount && ioReturn == IO_R_SUCCESS; i++) {
		ioReturn = [self logicalRange	: &thisRange
				index		: i
			    ];
		if (ioReturn == IO_R_SUCCESS) {
		    rangeStart	= trunc_page(thisRange.start);
		    rangeEnd	= round_page(thisRange.start + thisRange.size);
		    status	= vm_map_pageable(
					client,
					rangeStart,
					rangeEnd,
					FALSE		/* Wire range	*/
				    );
		    if (status != KERN_SUCCESS)
			ioReturn = IO_R_CANT_WIRE;	/* Wire failed	*/
		    else {
		    	if (forReading == FALSE) {
		    	    /*
		    	     * Is this really needed?
		    	     */
			    // flush_cache_v(rangeStart, rangeEnd - rangeStart);
			}
			++successCount;
		    }
		}
	    }
	}
	if (ioReturn != IO_R_SUCCESS) {
	    /*
	     * Unwire partial preparations.
	     */
	    for (i = 0; i < successCount; i++) {
		[self logicalRange : &thisRange index : i];
		rangeStart	= trunc_page(thisRange.start);
		rangeEnd	= round_page(thisRange.start + thisRange.size);
		(void) vm_map_pageable(
			client,
			rangeStart,
			rangeEnd,
			TRUE		/* Unwire range	*/
		    );
	    }
	    --residencyCount;
	}
	return (ioReturn);
}

/**
 * Make the memory described by this underlying IOMemoryContainer pageable.
 * This is called by the virtual memory manager and/or file system after
 * completing an I/O request.  The IOMemoryContainer maintains a reference
 * count: the last caller frees the memory the others just decrement the count.
 * This method returns an error status if any range could not be freed,
 * but always tries to free all ranges. Return IO_R_VM_FAILURE if any range
 * can't be unwired (but there is no indication as to which range).
 */
- (IOReturn)		unwireMemory
{
	IOReturn	ioReturn = IO_R_SUCCESS;
	IOReturn	finalResult = IO_R_SUCCESS;
	kern_return_t	status;
	unsigned int	i;
	IORange		thisRange;
	vm_offset_t	rangeStart;
	vm_offset_t	rangeEnd;
	
	/*
	 * Decrement the residency counter. If is zero after decrement, this
	 * is the last caller, so make the ranges pageable.
	 */
	if (--residencyCount == 0) {	/* NOTE: AtomicDecrement */
	    for (i = 0; i < rangeCount; i++) {
		ioReturn = [self logicalRange	: &thisRange
				index		: i
			    ];
		if (finalResult != IO_R_SUCCESS) {
		    finalResult = ioReturn;
		}
		else {
		    rangeStart	= trunc_page(thisRange.start);
		    rangeEnd	= round_page(thisRange.start + thisRange.size);
		    status = vm_map_pageable(
				client,
				rangeStart,
				rangeEnd,
				TRUE		/* Make range pageable	*/
			    );
		    if (status != KERN_SUCCESS && finalResult == IO_R_SUCCESS) {
			finalResult = IO_R_VM_FAILURE;
		    }
		}
	    }
	}
	return (finalResult);
}

/**
 * Normalize cache coherency (if needed by this particular hardware
 * architecture) before starting a DMA operation. This normalizes all
 * memory described by this IOMemoryContainer.  This is used as follows:
 *	mem = [[IOMemoryDescriptor alloc] initWithAddress
 *				: address
 *			length	: length];
 *	[mem makeResident];
 *	[mem checkpoint : ioCheckpointInput];
 *	... Extract physical ranges and do DMA I/O ...
 *	[mem checkpoint : ioCheckpointComplete];
 *	[mem makePageable];
 *	[mem free];
 * A checkpoint call may specify any combination of ioCheckpointInput,
 * ioCheckpointOutput, or ioCheckpointNoDirection. After DMA completes,
 * drivers must call checkpoint with ioCheckpointComplete as the only
 * parameter. The actual operation of checkpoint is processor-specific.
 * Note that wireMemory and unwireMemory will call checkpoint with appropriate
 * options: it need only be called explicitly if the same buffer is used
 * for multiple I/O requests, as might be the case for a video-to-disk
 * process.
 */
- (IOReturn)		checkpoint
			: (IOMemoryCheckpointOption) option
{
	/* Override if necessary */
	return (IO_R_SUCCESS);
}
			
@end /* IOMemoryContainer(Kernel) */
#endif /* KERNEL */


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.