IOMediaBSDClient.cpp [plain text]
#include <sys/types.h> // (miscfs/devfs/devfs.h, ...)
#include <miscfs/devfs/devfs.h> // (devfs_make_node, ...)
#include <sys/buf.h> // (struct buf, ...)
#include <sys/conf.h> // (bdevsw_add, ...)
#include <sys/fcntl.h> // (FWRITE, ...)
#include <sys/ioccom.h> // (IOCGROUP, ...)
#include <sys/stat.h> // (S_ISBLK, ...)
#include <sys/systm.h> // (kernel_flock, ...)
#include <sys/uio.h> // (struct uio, ...)
#include <IOKit/assert.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOMediaBSDClient.h>
#define super IOService
OSDefineMetaClassAndStructors(IOMediaBSDClient, IOService)
const SInt32 kMajor = 14;
const UInt32 kMinorsAddCountBits = 6;
const UInt32 kMinorsAddCountMask = (1 << kMinorsAddCountBits) - 1;
const UInt32 kMinorsAddCount = (1 << kMinorsAddCountBits);
const UInt32 kMinorsMaxCountBits = 16;
const UInt32 kMinorsMaxCountMask = (1 << kMinorsMaxCountBits) - 1;
const UInt32 kMinorsMaxCount = (1 << kMinorsMaxCountBits);
const UInt32 kMinorsBucketCount = kMinorsMaxCount / kMinorsAddCount;
const UInt32 kAnchorsAddCount = 2;
const UInt32 kAnchorsMaxCount = kMinorsMaxCount;
#define kMsgNoWhole "%s: No whole media found for media \"%s\".\n", getName()
#define kMsgNoLocation "%s: No location is found for media \"%s\".\n", getName()
extern "C"
{
int dkclose(dev_t dev, int flags, int devtype, struct proc *);
int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *);
int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int, struct proc *);
int dkopen(dev_t dev, int flags, int devtype, struct proc *);
int dkread(dev_t dev, struct uio * uio, int flags);
int dksize(dev_t dev);
void dkstrategy(struct buf * bp);
int dkwrite(dev_t dev, struct uio * uio, int flags);
}
static struct bdevsw bdevswFunctions =
{
dkopen,
dkclose,
dkstrategy,
dkioctl_bdev,
eno_dump,
dksize,
D_DISK
};
struct cdevsw cdevswFunctions =
{
dkopen,
dkclose,
dkread,
dkwrite,
dkioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
D_DISK
};
struct dio { dev_t dev; struct uio * uio; void * drvdata; };
typedef void * dkr_t;
typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
static int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype);
static void dkreadwritecompletion(void *, void *, IOReturn, UInt64);
#define get_kernel_task() kernel_task
#define get_user_task() current_task()
const UInt32 kInvalidAnchorID = (UInt32) (-1);
struct AnchorSlot
{
UInt32 isAssigned:1; UInt32 isObsolete:1;
IOService * anchor; void * key; IONotifier * notifier; };
class AnchorTable
{
protected:
AnchorSlot * _table;
UInt32 _tableCount;
static IOReturn anchorWasNotified( void * target,
void * parameter,
UInt32 messageType,
IOService * provider,
void * messageArgument,
vm_size_t messageArgumentSize );
public:
AnchorTable();
~AnchorTable();
UInt32 insert(IOService * anchor, void * key);
UInt32 locate(IOService * anchor);
UInt32 locate(IOService * anchor, void * key);
void obsolete(UInt32 anchorID);
void remove(UInt32 anchorID);
UInt32 update(IOService * anchor, void * key);
bool isObsolete(UInt32 anchorID);
};
const UInt32 kInvalidMinorID = (UInt32) (-1);
struct MinorSlot
{
UInt32 isAssigned:1; UInt32 isEjecting:1; UInt32 isObsolete:1;
UInt32 anchorID; IOMediaBSDClient * client; IOMedia * media; char * name;
UInt64 bdevBlockSize; void * bdevNode; UInt32 bdevOpen:16; UInt32 bdevWriter:1;
void * cdevNode; UInt32 cdevOpen:16; UInt32 cdevWriter:1; };
class MinorTable
{
protected:
class
{
public:
MinorSlot ** buckets;
inline MinorSlot & operator[](const int i)
{
return (buckets[i >> kMinorsAddCountBits])[i & kMinorsAddCountMask];
}
} _table;
UInt32 _tableCount;
public:
MinorTable();
~MinorTable();
UInt32 insert( IOMedia * media,
UInt32 anchorID,
IOMediaBSDClient * client,
char * slicePath );
UInt32 locate(IOMedia * media);
void obsolete(UInt32 minorID);
void remove(UInt32 minorID);
bool isObsolete(UInt32 minorID);
MinorSlot * getMinor(UInt32 minorID);
UInt32 getOpenCountForAnchorID(UInt32 anchorID);
IOMedia * getWholeMediaAtAnchorID(UInt32 anchorID);
bool hasReferencesToAnchorID(UInt32 anchorID);
};
class IOMediaBSDClientGlobals
{
protected:
AnchorTable * _anchors; MinorTable * _minors;
UInt32 _bdevswInstalled:1; UInt32 _cdevswInstalled:1;
IORecursiveLock * _lock;
public:
IOMediaBSDClientGlobals();
~IOMediaBSDClientGlobals();
AnchorTable * getAnchors();
MinorTable * getMinors();
MinorSlot * getMinor(UInt32 minorID);
bool isValid();
void lock();
void unlock();
};
static IOMediaBSDClientGlobals gIOMediaBSDClientGlobals;
bool IOMediaBSDClient::init(OSDictionary * properties = 0)
{
if ( super::init(properties) == false ) return false;
if ( gIOMediaBSDClientGlobals.isValid() == false ) return false;
_anchors = gIOMediaBSDClientGlobals.getAnchors();
_minors = gIOMediaBSDClientGlobals.getMinors();
return true;
}
void IOMediaBSDClient::free()
{
super::free();
}
bool IOMediaBSDClient::start(IOService * provider)
{
IOMedia * media = (IOMedia *) provider;
if ( super::start(provider) == false ) return false;
gIOMediaBSDClientGlobals.lock();
createNodes(media);
gIOMediaBSDClientGlobals.unlock();
registerService();
return true;
}
void IOMediaBSDClient::stop(IOService * provider)
{
IOMedia * media = (IOMedia *) provider;
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
UInt32 minorID = 0;
gIOMediaBSDClientGlobals.lock();
minorID = minors->locate(media);
assert(minorID != kInvalidMinorID);
assert(media->isOpen() == false);
if ( minors->getMinor(minorID)->isEjecting ) {
assert(minors->isObsolete(minorID) == false);
minors->obsolete(minorID);
}
else
{
assert(minors->getMinor(minorID)->bdevOpen == 0);
assert(minors->getMinor(minorID)->cdevOpen == 0);
minors->remove(minorID);
}
gIOMediaBSDClientGlobals.unlock();
super::stop(media);
}
IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media,
UInt32 * slicePathSize = 0,
char * slicePath = 0 )
{
UInt32 depth = 1;
UInt32 position = sizeof('\0');
IOService * service = 0;
assert(slicePath == 0 || slicePathSize != 0);
for ( service = media; service; service = service->getProvider() )
{
if ( OSDynamicCast(IOMedia, service) ) {
if ( ((IOMedia *)service)->isWhole() ) {
if ( slicePath ) {
slicePath[*slicePathSize - 1] = 0;
if ( position < *slicePathSize ) {
memmove( slicePath, slicePath + (*slicePathSize - position),
position );
}
}
else if ( slicePathSize ) {
*slicePathSize = position;
}
return (IOMedia *)service; }
const char * location = service->getLocation();
if ( location == 0 ) {
if ( service == media ) IOLog(kMsgNoLocation, media->getName());
return 0;
}
position += sizeof('s') + strlen(location);
if ( slicePath ) {
char * path = slicePath + *slicePathSize - position;
if ( position > *slicePathSize ) { assert(0); return 0; }
*path = 's';
strncpy(path + sizeof('s'), location, strlen(location));
}
depth += 1;
}
}
if ( depth == 1 ) IOLog(kMsgNoWhole, media->getName());
return 0;
}
bool IOMediaBSDClient::createNodes(IOMedia * media)
{
IOService * anchor;
AnchorTable * anchors = gIOMediaBSDClientGlobals.getAnchors();
UInt32 anchorID;
bool anchorNew = false;
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
UInt32 minorID;
char * slicePath = 0;
UInt32 slicePathSize;
IOMedia * whole;
whole = getWholeMedia(media, &slicePathSize);
if ( whole == 0 ) return false;
anchor = whole->getProvider();
if ( anchor == 0 ) return false;
anchorID = anchors->locate(anchor, whole);
if ( anchorID == kInvalidAnchorID )
{
anchorID = anchors->update(anchor, whole);
}
if ( anchorID == kInvalidAnchorID )
{
anchorID = anchors->insert(anchor, whole); if ( anchorID == kInvalidAnchorID ) return false;
anchorNew = true;
}
slicePath = (char *) IOMalloc(slicePathSize);
if ( slicePath == 0 ) goto createNodesErr;
whole = getWholeMedia(media, &slicePathSize, slicePath);
assert(whole);
minorID = minors->insert(media, anchorID, this, slicePath);
if ( minorID == kInvalidMinorID ) goto createNodesErr;
media->setProperty(kIOBSDNameKey, minors->getMinor(minorID)->name);
media->setProperty(kIOBSDUnitKey, anchorID, 32); media->setProperty(kIOBSDMajorKey, kMajor, 32); media->setProperty(kIOBSDMinorKey, minorID, 32);
IOFree(slicePath, slicePathSize);
return true;
createNodesErr:
if (anchorNew) anchors->remove(anchorID);
if (slicePath) IOFree(slicePath, slicePathSize);
return false; }
AnchorTable * IOMediaBSDClient::getAnchors()
{
return _anchors;
}
MinorTable * IOMediaBSDClient::getMinors()
{
return _minors;
}
MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID)
{
return _minors->getMinor(minorID);
}
IOMedia * IOMediaBSDClient::getProvider() const
{
return (IOMedia *) IOService::getProvider();
}
int IOMediaBSDClient::ioctl( dev_t dev,
u_long cmd,
caddr_t data,
int flags,
struct proc * proc )
{
int error = 0;
switch ( cmd )
{
default:
{
IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n",
gIOMediaBSDClientGlobals.getMinor(minor(dev))->name,
((cmd & IOC_INOUT) == IOC_INOUT) ? ("_IOWR,") :
( ((cmd & IOC_OUT) == IOC_OUT) ? ("_IOR,") :
( ((cmd & IOC_IN) == IOC_IN) ? ("_IOW,") :
( ((cmd & IOC_VOID) == IOC_VOID) ? ("_IO,") : "" ) ) ),
(char) IOCGROUP(cmd),
(int) (cmd & 0xff),
(int) IOCPARM_LEN(cmd) );
error = ENOTTY;
} break;
}
return error; }
OSMetaClassDefineReservedUsed(IOMediaBSDClient, 0);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 1);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 2);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 3);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 4);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 5);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 6);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 7);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 8);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 9);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 10);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 11);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 12);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 13);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 14);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 15);
int dkopen(dev_t dev, int flags, int devtype, struct proc *)
{
int error;
IOStorageAccess level;
MinorSlot * minor;
assert(S_ISBLK(devtype) || S_ISCHR(devtype));
gIOMediaBSDClientGlobals.lock();
error = 0;
level = (flags & FWRITE) ? kIOStorageAccessReaderWriter
: kIOStorageAccessReader;
minor = gIOMediaBSDClientGlobals.getMinor(minor(dev));
if ( minor == 0 ) {
error = ENXIO;
}
else if ( minor->isEjecting ) {
error = EBUSY;
}
else if ( (flags & FWRITE) ) {
if ( minor->media->isWritable() == false )
error = EACCES;
if ( minor->bdevWriter || minor->cdevWriter )
level = kIOStorageAccessNone;
}
else {
if ( minor->bdevOpen || minor->cdevOpen )
level = kIOStorageAccessNone;
}
if ( error == 0 && level != kIOStorageAccessNone ) {
if ( minor->media->open(minor->client, 0, level) == false ) {
error = EBUSY;
}
}
if ( error == 0 ) {
if ( S_ISBLK(devtype) )
{
minor->bdevOpen++;
if ( (flags & FWRITE) ) minor->bdevWriter = true;
}
else
{
minor->cdevOpen++;
if ( (flags & FWRITE) ) minor->cdevWriter = true;
}
}
gIOMediaBSDClientGlobals.unlock();
return error;
}
int dkclose(dev_t dev, int , int devtype, struct proc *)
{
IOMedia * media;
MinorSlot * minor;
bool wasWriter;
assert(S_ISBLK(devtype) || S_ISCHR(devtype));
gIOMediaBSDClientGlobals.lock();
media = 0;
minor = gIOMediaBSDClientGlobals.getMinor(minor(dev));
wasWriter = (minor->bdevWriter || minor->cdevWriter);
if ( S_ISBLK(devtype) ) {
minor->bdevBlockSize = minor->media->getPreferredBlockSize();
minor->bdevOpen = 0;
minor->bdevWriter = false;
}
else
{
minor->cdevOpen = 0;
minor->cdevWriter = false;
}
if ( minor->isEjecting ) {
minor->isEjecting = false;
assert(minor->bdevOpen == 0);
assert(minor->cdevOpen == 0);
if ( minor->isObsolete )
gIOMediaBSDClientGlobals.getMinors()->remove(minor(dev));
}
else if ( !minor->bdevOpen && !minor->cdevOpen )
{
media = minor->media;
minor->media->retain();
minor->media->close(minor->client); }
else if ( !minor->bdevWriter && !minor->cdevWriter && wasWriter )
{
bool success;
media = minor->media;
minor->media->retain();
success = minor->media->open(minor->client, 0, kIOStorageAccessReader);
assert(success); }
gIOMediaBSDClientGlobals.unlock();
if ( media )
{
media->waitQuiet();
media->release();
}
return 0;
}
int dkread(dev_t dev, struct uio * uio, int )
{
struct dio dio = { dev, uio };
return dkreadwrite(&dio, DKRTYPE_DIO);
}
int dkwrite(dev_t dev, struct uio * uio, int )
{
struct dio dio = { dev, uio };
return dkreadwrite(&dio, DKRTYPE_DIO);
}
void dkstrategy(struct buf * bp)
{
dkreadwrite(bp, DKRTYPE_BUF);
}
int dkioctl(dev_t dev, u_long cmd, caddr_t data, int f, struct proc * p)
{
int error = 0;
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(minor(dev));
if ( minor->isEjecting ) return EBADF;
switch ( cmd )
{
case DKIOCGETBLOCKSIZE: {
*(u_int32_t *)data = minor->media->getPreferredBlockSize();
} break;
case DKIOCGETBLOCKCOUNT32: {
if ( minor->media->getPreferredBlockSize() )
*(u_int32_t *)data = ( minor->media->getSize() /
minor->media->getPreferredBlockSize() );
else
*(u_int32_t *)data = 0;
} break;
case DKIOCGETBLOCKCOUNT: {
if ( minor->media->getPreferredBlockSize() )
*(u_int64_t *)data = ( minor->media->getSize() /
minor->media->getPreferredBlockSize() );
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXBLOCKCOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumBlockCountReadKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXBLOCKCOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumBlockCountWriteKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXBYTECOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumByteCountReadKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXBYTECOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumByteCountWriteKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTCOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentCountReadKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTCOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentCountWriteKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTBYTECOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentByteCountReadKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTBYTECOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentByteCountWriteKey,
gIOServicePlane ) );
if ( number )
*(u_int64_t *)data = number->unsigned64BitValue();
else
*(u_int64_t *)data = 0;
} break;
case DKIOCISFORMATTED: {
*(u_int32_t *)data = minor->media->isFormatted();
} break;
case DKIOCISWRITABLE: {
*(u_int32_t *)data = minor->media->isWritable();
} break;
case DKIOCGETFIRMWAREPATH: {
int l = sizeof(((dk_firmware_path_t *)data)->path);
char * p = ((dk_firmware_path_t *)data)->path;
if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
strcpy(p, strchr(p, ':') + 1); else
error = EINVAL;
} break;
case DKIOCEJECT: {
IOMediaBSDClient * client;
IOBlockStorageDriver * driver;
MinorTable * minors;
IOReturn status;
client = minor->client;
driver = (IOBlockStorageDriver *) minor->media->getProvider();
driver = OSDynamicCast(IOBlockStorageDriver, driver);
minors = gIOMediaBSDClientGlobals.getMinors();
if ( driver == 0 ) { error = ENOTTY; break; }
gIOMediaBSDClientGlobals.lock();
if ( minors->getOpenCountForAnchorID(minor->anchorID) > 1 ||
driver->open(client, 0, kIOStorageAccessReader) == false )
{
error = EBUSY;
gIOMediaBSDClientGlobals.unlock();
}
else
{
minor->isEjecting = true;
gIOMediaBSDClientGlobals.unlock();
minor->media->close(client);
client->retain();
status = driver->ejectMedia();
error = driver->errnoFromReturn(status);
driver->close(client);
client->release();
}
} break;
case DKIOCSYNCHRONIZECACHE: {
IOReturn status;
status = minor->media->synchronizeCache(minor->client);
error = minor->media->errnoFromReturn(status);
} break;
default:
{
error = minor->client->ioctl(dev, cmd, data, f, p);
} break;
}
return error; }
int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int f, struct proc * p)
{
int error = 0;
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(minor(dev));
if ( minor->isEjecting ) return EBADF;
switch ( cmd )
{
case DKIOCGETBLOCKSIZE: {
*(u_int32_t *)data = minor->bdevBlockSize;
} break;
case DKIOCSETBLOCKSIZE: {
if ( *(u_int32_t *)data > 0 )
minor->bdevBlockSize = (UInt64) (*(u_int32_t *)data);
else
error = EINVAL;
} break;
case DKIOCGETBLOCKCOUNT32: {
if ( minor->bdevBlockSize )
*(u_int32_t *)data = ( minor->media->getSize() /
minor->bdevBlockSize );
else
*(u_int32_t *)data = 0;
} break;
case DKIOCGETBLOCKCOUNT: {
if ( minor->bdevBlockSize )
*(u_int64_t *)data = ( minor->media->getSize() /
minor->bdevBlockSize );
else
*(u_int64_t *)data = 0;
} break;
default:
{
error = dkioctl(dev, cmd, data, f, p);
} break;
}
return error; }
int dksize(dev_t dev)
{
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(minor(dev));
if ( minor->isEjecting ) return 0;
return (int) minor->bdevBlockSize; }
inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((struct buf *)dkr)->b_dev
: ((struct dio *)dkr)->dev;
}
inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((struct buf *)dkr)->b_bcount
: ((struct dio *)dkr)->uio->uio_resid;
}
inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
{
if (dkrtype == DKRTYPE_BUF)
{
struct buf * bp = (struct buf *)dkr;
MinorSlot * minor;
minor = gIOMediaBSDClientGlobals.getMinor(minor(bp->b_dev));
return bp->b_blkno * minor->bdevBlockSize;
}
return ((struct dio *)dkr)->uio->uio_offset;
}
inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((((struct buf *)dkr)->b_flags & B_READ) == B_READ)
: ((((struct dio *)dkr)->uio->uio_rw) == UIO_READ);
}
inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? true
: false;
}
inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? false
: true;
}
inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
{
if (dkrtype == DKRTYPE_BUF)
((struct buf *)dkr)->b_resid = ((struct buf *)dkr)->b_bcount - bcount;
else
((struct dio *)dkr)->uio->uio_resid -= bcount;
}
inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
{
if (dkrtype == DKRTYPE_BUF)
{
struct buf * bp = (struct buf *)dkr;
MinorSlot * minor;
minor = gIOMediaBSDClientGlobals.getMinor(minor(bp->b_dev));
bp->b_error = minor->media->errnoFromReturn(status); bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0; biodone(bp); }
}
inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
{
if (dkrtype == DKRTYPE_BUF)
{
struct buf * bp = (struct buf *)dkr;
if ( (bp->b_flags & B_VECTORLIST) )
{
assert(sizeof(IOPhysicalRange ) == sizeof(iovec ));
assert(sizeof(IOPhysicalRange::address) == sizeof(iovec::iov_base));
assert(sizeof(IOPhysicalRange::length ) == sizeof(iovec::iov_len ));
return IOMemoryDescriptor::withPhysicalRanges( (IOPhysicalRange *) bp->b_vectorlist,
(UInt32) bp->b_vectorcount,
(bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
true );
}
return IOMemoryDescriptor::withAddress( (vm_address_t) bp->b_data,
(vm_size_t) bp->b_bcount,
(bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
(bp->b_flags & B_PHYS) ? get_user_task() : get_kernel_task() );
}
else
{
struct uio * uio = ((struct dio *)dkr)->uio;
assert(sizeof(IOVirtualRange ) == sizeof(iovec ));
assert(sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base));
assert(sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ));
return IOMemoryDescriptor::withRanges( (IOVirtualRange *) uio->uio_iov,
(UInt32) uio->uio_iovcnt,
(uio->uio_rw == UIO_READ ) ? kIODirectionIn : kIODirectionOut,
(uio->uio_segflg != UIO_SYSSPACE) ? get_user_task() : get_kernel_task(),
true );
}
}
inline void * DKR_GET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((struct buf *)dkr)->b_drvdata
: ((struct dio *)dkr)->drvdata;
}
inline void DKR_SET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype, void * drvdata)
{
if (dkrtype == DKRTYPE_BUF)
((struct buf *)dkr)->b_drvdata = drvdata;
else
((struct dio *)dkr)->drvdata = drvdata;
}
int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
{
IOMemoryDescriptor * buffer;
register UInt64 byteCount;
register UInt64 byteStart;
UInt64 mediaSize;
MinorSlot * minor;
IOReturn status;
DKR_SET_DRIVER_DATA(dkr, dkrtype, 0);
minor = gIOMediaBSDClientGlobals.getMinor(minor(DKR_GET_DEV(dkr, dkrtype)));
if ( minor->isEjecting ) {
status = kIOReturnNoMedia;
goto dkreadwriteErr;
}
if ( minor->media->isFormatted() == false ) {
status = kIOReturnUnformattedMedia;
goto dkreadwriteErr;
}
byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype); byteStart = DKR_GET_BYTE_START(dkr, dkrtype); mediaSize = minor->media->getSize();
if ( byteStart >= mediaSize ) {
status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;
goto dkreadwriteErr;
}
if ( DKR_IS_RAW(dkr, dkrtype) )
{
UInt64 mediaBlockSize = minor->media->getPreferredBlockSize();
if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
{
status = kIOReturnNotAligned;
goto dkreadwriteErr;
}
}
buffer = DKR_GET_BUFFER(dkr, dkrtype);
if ( buffer == 0 ) {
status = kIOReturnNoMemory;
goto dkreadwriteErr;
}
if ( byteCount > mediaSize - byteStart ) {
IOMemoryDescriptor * originalBuffer = buffer;
buffer = IOMemoryDescriptor::withSubRange(
originalBuffer,
0,
mediaSize - byteStart,
originalBuffer->getDirection() );
originalBuffer->release();
if ( buffer == 0 ) {
status = kIOReturnNoMemory;
goto dkreadwriteErr;
}
}
if ( buffer->prepare() != kIOReturnSuccess ) {
buffer->release();
status = kIOReturnVMError; goto dkreadwriteErr;
}
DKR_SET_DRIVER_DATA(dkr, dkrtype, buffer);
if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) ) {
IOStorageCompletion completion;
completion.target = dkr;
completion.action = dkreadwritecompletion;
completion.parameter = (void *) dkrtype;
if ( DKR_IS_READ(dkr, dkrtype) ) {
minor->media->read( minor->client,
byteStart,
buffer,
completion ); }
else {
minor->media->write( minor->client,
byteStart,
buffer,
completion ); }
status = kIOReturnSuccess;
}
else {
if ( DKR_IS_READ(dkr, dkrtype) ) {
status = minor->media->read(
minor->client,
byteStart,
buffer,
&byteCount ); }
else {
status = minor->media->write(
minor->client,
byteStart,
buffer,
&byteCount ); }
dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
}
return minor->media->errnoFromReturn(status);
dkreadwriteErr:
dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
return minor->media->errnoFromReturn(status); }
void dkreadwritecompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
dkr_t dkr = (dkr_t) target;
dkrtype_t dkrtype = (dkrtype_t) (int) parameter;
dev_t dev = DKR_GET_DEV(dkr, dkrtype);
void * drvdata = DKR_GET_DRIVER_DATA(dkr, dkrtype);
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(minor(dev));
if ( drvdata ) {
IOMemoryDescriptor * buffer = (IOMemoryDescriptor *) drvdata;
buffer->complete(); buffer->release(); }
if ( status != kIOReturnSuccess ) {
IOLog("%s: %s.\n", minor->name, minor->media->stringFromReturn(status));
}
if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) ) {
boolean_t funnel_state;
funnel_state = thread_funnel_set(kernel_flock, TRUE);
DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); DKR_RUN_COMPLETION(dkr, dkrtype, status);
thread_funnel_set(kernel_flock, funnel_state);
}
else
{
DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); }
}
AnchorTable::AnchorTable()
{
_table = 0;
_tableCount = 0;
}
AnchorTable::~AnchorTable()
{
for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ )
if ( _table[anchorID].isAssigned ) remove(anchorID);
if ( _table ) IODelete(_table, AnchorSlot, _tableCount);
}
UInt32 AnchorTable::insert(IOService * anchor, void * key)
{
UInt32 anchorID;
IONotifier * notifier;
for ( anchorID = 0; anchorID < _tableCount; anchorID++ )
if ( _table[anchorID].isAssigned == false ) break;
if ( anchorID == _tableCount )
{
AnchorSlot * newTable;
UInt32 newTableCount;
if ( _tableCount >= kAnchorsMaxCount ) return kInvalidAnchorID;
newTableCount = min(kAnchorsAddCount + _tableCount, kAnchorsMaxCount);
newTable = IONew(AnchorSlot, newTableCount);
if ( newTable == 0 ) return kInvalidAnchorID;
bzero(newTable, newTableCount * sizeof(AnchorSlot));
if ( _table )
{
bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot));
IODelete(_table, AnchorSlot, _tableCount);
}
anchorID = _tableCount;
_table = newTable;
_tableCount = newTableCount;
}
notifier = anchor->registerInterest(
gIOGeneralInterest,
anchorWasNotified,
this,
0 );
if ( notifier == 0 ) return kInvalidAnchorID;
bzero(&_table[anchorID], sizeof(AnchorSlot));
_table[anchorID].isAssigned = true; _table[anchorID].isObsolete = false;
_table[anchorID].anchor = anchor;
_table[anchorID].key = key;
_table[anchorID].notifier = notifier;
_table[anchorID].anchor->retain();
return anchorID;
}
void AnchorTable::remove(UInt32 anchorID)
{
assert(anchorID < _tableCount);
assert(_table[anchorID].isAssigned);
_table[anchorID].notifier->remove();
_table[anchorID].anchor->release();
bzero(&_table[anchorID], sizeof(AnchorSlot)); }
void AnchorTable::obsolete(UInt32 anchorID)
{
assert(anchorID < _tableCount);
assert(_table[anchorID].isAssigned);
_table[anchorID].isObsolete = true;
}
UInt32 AnchorTable::locate(IOService * anchor)
{
for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
{
if ( _table[anchorID].isAssigned != false &&
_table[anchorID].isObsolete == false &&
_table[anchorID].anchor == anchor ) return anchorID;
}
return kInvalidAnchorID;
}
UInt32 AnchorTable::locate(IOService * anchor, void * key)
{
for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
{
if ( _table[anchorID].isAssigned != false &&
_table[anchorID].isObsolete == false &&
_table[anchorID].anchor == anchor &&
_table[anchorID].key == key ) return anchorID;
}
return kInvalidAnchorID;
}
UInt32 AnchorTable::update(IOService * anchor, void * key)
{
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
{
if ( _table[anchorID].isAssigned != false &&
_table[anchorID].isObsolete == false &&
_table[anchorID].anchor == anchor )
{
if ( minors->hasReferencesToAnchorID(anchorID) == false )
{
_table[anchorID].key = key;
return anchorID;
}
}
}
return kInvalidAnchorID;
}
bool AnchorTable::isObsolete(UInt32 anchorID)
{
assert(anchorID < _tableCount);
assert(_table[anchorID].isAssigned);
return _table[anchorID].isObsolete ? true : false;
}
IOReturn AnchorTable::anchorWasNotified( void * ,
void * ,
UInt32 messageType,
IOService * anchor,
void * ,
vm_size_t )
{
AnchorTable * anchors = gIOMediaBSDClientGlobals.getAnchors();
UInt32 anchorID;
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
if ( messageType != kIOMessageServiceIsTerminated )
return kIOReturnSuccess;
gIOMediaBSDClientGlobals.lock();
while ( (anchorID = anchors->locate(anchor)) != kInvalidAnchorID )
{
if ( minors->hasReferencesToAnchorID(anchorID) )
anchors->obsolete(anchorID);
else
anchors->remove(anchorID);
}
gIOMediaBSDClientGlobals.unlock();
return kIOReturnSuccess;
}
MinorTable::MinorTable()
{
_table.buckets = IONew(MinorSlot *, kMinorsBucketCount);
_tableCount = 0;
if ( _table.buckets )
bzero(_table.buckets, kMinorsBucketCount * sizeof(MinorSlot *));
}
MinorTable::~MinorTable()
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
if ( _table[minorID].isAssigned ) remove(minorID);
if ( _table.buckets )
{
for ( UInt32 bucketID = 0; _table.buckets[bucketID]; bucketID++ )
IODelete(_table.buckets[bucketID], MinorSlot, kMinorsAddCount);
IODelete(_table.buckets, MinorSlot *, kMinorsBucketCount);
}
}
UInt32 MinorTable::insert( IOMedia * media,
UInt32 anchorID,
IOMediaBSDClient * client,
char * slicePath )
{
void * bdevNode;
void * cdevNode;
UInt32 minorID;
char * minorName;
UInt32 minorNameSize;
if ( _table.buckets == 0 ) return kInvalidMinorID;
for ( minorID = 0; minorID < _tableCount; minorID++ )
if ( _table[minorID].isAssigned == false ) break;
if ( minorID == _tableCount )
{
UInt32 bucketID = _tableCount / kMinorsAddCount;
if ( bucketID >= kMinorsBucketCount ) return kInvalidMinorID;
_table.buckets[bucketID] = IONew(MinorSlot, kMinorsAddCount);
if ( _table.buckets[bucketID] == 0 ) return kInvalidMinorID;
bzero(_table.buckets[bucketID], kMinorsAddCount * sizeof(MinorSlot));
_tableCount += kMinorsAddCount;
}
minorNameSize = strlen("disk#");
for (unsigned temp = anchorID; temp >= 10; temp /= 10) minorNameSize++;
minorNameSize += strlen(slicePath);
minorNameSize += 1;
minorName = IONew(char, minorNameSize);
bdevNode = devfs_make_node( makedev(kMajor, minorID),
DEVFS_BLOCK,
UID_ROOT,
GID_OPERATOR,
media->isWritable()?0640:0440,
"disk%d%s",
anchorID,
slicePath );
cdevNode = devfs_make_node( makedev(kMajor, minorID),
DEVFS_CHAR,
UID_ROOT,
GID_OPERATOR,
media->isWritable()?0640:0440,
"rdisk%d%s",
anchorID,
slicePath );
if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 )
{
if ( cdevNode ) devfs_remove(cdevNode);
if ( bdevNode ) devfs_remove(bdevNode);
if ( minorName ) IODelete(minorName, char, minorNameSize);
return kInvalidMinorID;
}
sprintf(minorName, "disk%ld%s", anchorID, slicePath);
assert(strlen(minorName) + 1 == minorNameSize);
bzero(&_table[minorID], sizeof(MinorSlot));
_table[minorID].isAssigned = true; _table[minorID].isEjecting = false;
_table[minorID].isObsolete = false;
_table[minorID].anchorID = anchorID;
_table[minorID].client = client;
_table[minorID].media = media;
_table[minorID].name = minorName;
_table[minorID].bdevBlockSize = media->getPreferredBlockSize();
_table[minorID].bdevNode = bdevNode;
_table[minorID].bdevOpen = 0;
_table[minorID].cdevNode = cdevNode;
_table[minorID].cdevOpen = 0;
_table[minorID].client->retain(); _table[minorID].media->retain();
return minorID;
}
void MinorTable::remove(UInt32 minorID)
{
UInt32 anchorID;
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
assert(_table[minorID].isEjecting == false);
assert(_table[minorID].bdevOpen == 0);
assert(_table[minorID].cdevOpen == 0);
anchorID = _table[minorID].anchorID;
devfs_remove(_table[minorID].cdevNode);
devfs_remove(_table[minorID].bdevNode);
IODelete(_table[minorID].name, char, strlen(_table[minorID].name) + 1);
_table[minorID].client->release(); _table[minorID].media->release();
bzero(&_table[minorID], sizeof(MinorSlot));
if ( gIOMediaBSDClientGlobals.getAnchors()->isObsolete(anchorID) )
{
if ( hasReferencesToAnchorID(anchorID) == false )
gIOMediaBSDClientGlobals.getAnchors()->remove(anchorID);
}
}
UInt32 MinorTable::locate(IOMedia * media)
{
for (UInt32 minorID = 0; minorID < _tableCount; minorID++)
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].isObsolete == false &&
_table[minorID].media == media ) return minorID;
}
return kInvalidMinorID;
}
UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID)
{
UInt32 opens = 0;
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID )
{
opens += _table[minorID].bdevOpen;
opens += _table[minorID].cdevOpen;
}
}
return opens;
}
IOMedia * MinorTable::getWholeMediaAtAnchorID(UInt32 anchorID)
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID &&
_table[minorID].media->isWhole() ) return _table[minorID].media;
}
return 0;
}
bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID)
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID ) return true;
}
return false;
}
MinorSlot * MinorTable::getMinor(UInt32 minorID)
{
if ( minorID < _tableCount && _table[minorID].isAssigned )
return &_table[minorID];
else
return 0;
}
void MinorTable::obsolete(UInt32 minorID)
{
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
_table[minorID].isObsolete = true;
}
bool MinorTable::isObsolete(UInt32 minorID)
{
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
return _table[minorID].isObsolete ? true : false;
}
IOMediaBSDClientGlobals::IOMediaBSDClientGlobals()
{
_anchors = new AnchorTable();
_minors = new MinorTable();
_bdevswInstalled = (bdevsw_add(kMajor, &bdevswFunctions) == kMajor);
_cdevswInstalled = (cdevsw_add(kMajor, &cdevswFunctions) == kMajor);
_lock = IORecursiveLockAlloc();
}
IOMediaBSDClientGlobals::~IOMediaBSDClientGlobals()
{
if ( _lock ) IORecursiveLockFree(_lock);
if ( _cdevswInstalled ) cdevsw_remove(kMajor, &cdevswFunctions);
if ( _bdevswInstalled ) bdevsw_remove(kMajor, &bdevswFunctions);
if ( _minors ) delete _minors;
if ( _anchors ) delete _anchors;
}
AnchorTable * IOMediaBSDClientGlobals::getAnchors()
{
return _anchors;
}
MinorTable * IOMediaBSDClientGlobals::getMinors()
{
return _minors;
}
MinorSlot * IOMediaBSDClientGlobals::getMinor(UInt32 minorID)
{
return _minors->getMinor(minorID);
}
bool IOMediaBSDClientGlobals::isValid()
{
return _anchors && _minors && _bdevswInstalled && _cdevswInstalled && _lock; }
void IOMediaBSDClientGlobals::lock()
{
IORecursiveLockLock(_lock);
}
void IOMediaBSDClientGlobals::unlock()
{
IORecursiveLockUnlock(_lock);
}