Source to bsd/dev/ppc/drvATADisk/ATADiskKernel.m
/*
* 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.0 (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@
*/
/*
* Copyright 1997-1998 by Apple Computer, Inc., All rights reserved.
* Copyright 1994-1997 NeXT Software, Inc., All rights reserved.
*
* ATADiskKernel.m - UNIX front end for kernel IDE/ATA Disk driver.
*
* HISTORY
* 07-Jul-1994 Rakesh Dubey at NeXT
* Created from original driver written by David Somayajulu.
* 19-Jun-97 Dieter Siegmund at Apple
* Updated to use the BSD4.4 ioctl interface that copies user
* buffer in/out of kernel automatically.
* 21-Feb-98 Brent Knight at Apple
* Added support for HFS partitions. Radar 2204950, 2004660
*/
#if (IO_DRIVERKIT_VERSION == 400)
#define _POSIX_SOURCE
#endif
/*
* Note that this file builds with KERNEL_PRIVATE and !MACH_USER_API.
*/
#import <sys/types.h>
#import <sys/ttycom.h>
#import <sys/ucred.h>
#import <driverkit/kernelDiskMethods.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/kernelDriver.h>
#import <driverkit/IODiskPartition.h>
#import <sys/buf.h>
#import <sys/uio.h>
#import <bsd/dev/ldd.h>
#import <sys/errno.h>
#import <sys/proc.h>
#if (IO_DRIVERKIT_VERSION == 330)
#import <vm/vm_kern.h>
#endif
#import <sys/fcntl.h>
#import <sys/systm.h>
#import "ATADiskInternal.h"
#import "ATADisk.h"
#import "ATADiskKernel.h"
#if (IO_DRIVERKIT_VERSION != 330)
extern struct vm_map *kernel_map;
#endif
#ifdef ppc //bknight - 2/21/98 - Radar 2204950, 2004660
#define GROK_APPLE 1
#endif //bknight - 2/21/98 - Radar 2204950, 2004660
#ifdef GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
/*
* For consistency with bsd/dev/SCSIDiskKern.m, we use the most
* significant bit of the 8-bit minor device number to flag HFS
* partitions. For SCSI devices, this meant that there could be
* 16 (= 4 bits) SCSI disks, each with 8 (= 3 bits) partitions,
* either UFS or HFS (= 1 bit).
*
* In SCSIDiskKern.h, NUM_SD_DEV is defined to be 16, which is
* just the right constant for checking that high bit of the
* Unit number and that's what I used in SCSIDiskKern.m.
*
* Unfortunately, we can't just use NUM_IDE_DEV here, because
* it is defined in IdeDiskInternal.h to be only 4. And since
* it seems inappropriate to include SCSIDiskKern.h here in
* IdeKernel.m, I am instead making this definition, which might
* some day migrate out into IdeDiskInternal.h, or which might
* be made obsolete by the forthcoming IOKit design.
*/
#define MAX_IDE_UNIT 16
#endif GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
IONamedValue iderValues[] = {
{IDER_SUCCESS, "Success" },
{IDER_TIMEOUT, "Timeout occured" },
{IDER_MEMALLOC, "Couldn't allocate memory" },
{IDER_MEMFAIL, "Memory transfer error" },
{IDER_REJECT, "Bad field in ide_ioreq" },
{IDER_BADDRV, "Drive not present" },
{IDER_CMD_ERROR, "Command Failed" },
{IDER_VOLUNAVAIL, "Requested Volume not available"},
{IDER_SPURIOUS, "Spurious Interrupt" },
{IDER_CNTRL_REJECT, "Controller Reject" },
{0, NULL },
};
static int ideOpenCount = 0;
/*
* dev-to-id map array. Instances of DiskObject register their IDs in
* this array via registerUnixDisk:.
*/
IODevAndIdInfo IdeIdMap[NUM_IDE_DEV];
/*
* Private per-unit data.
*/
static Ide_dev_t ide_dev[NUM_IDE_DEV] = {
{NULL},
{NULL},
{NULL},
{NULL},
};
/*
* Indices of our entries in devsw's.
*/
static int ide_block_major;
static int ide_raw_major;
/*
* prototypes for internal functions
*/
static unsigned ideminphys(struct buf *bp);
static id ide_dev_to_id(dev_t dev);
/*
* Initialize id map and Ide_dev. Currently invoked by Ide probe:.
*/
__private_extern__ void ide_init_idmap(id self)
{
IODevAndIdInfo *idMap = IdeIdMap;
Ide_dev_t *ide_devp = ide_dev;
int unit;
/*
* figure out our major device numbers.
*/
ide_block_major = [self blockMajor];
ide_raw_major = [self characterMajor];
bzero((char *)idMap, sizeof(IODevAndIdInfo) * NUM_IDE_DEV);
for (unit = 0; unit < NUM_IDE_DEV; unit++) {
idMap->rawDev = makedev(ide_raw_major, (unit << 3));
idMap->blockDev = makedev(ide_block_major, (unit << 3));
idMap++;
ide_devp->physbuf = (struct buf *)IOMalloc(sizeof(struct buf));
ide_devp->physbuf->b_flags = 0;
ide_devp++;
}
}
__private_extern__ IODevAndIdInfo *ide_idmap()
{
return IdeIdMap;
}
__private_extern__ int
ideopen(dev_t dev, int flag, int devtype, struct proc * pp)
{
id diskObj = ide_dev_to_id(dev);
if(diskObj == nil)
return(ENXIO);
if([diskObj isDiskReady:NO])
return(ENXIO);
/*
* Register this 'Unix-level open' event for IODiskPartitions.
*/
if(IO_DISK_PART(dev) != IDE_LIVE_PART) {
if(major(dev) == ide_block_major) {
[diskObj setBlockDeviceOpen:YES];
ideOpenCount++;
}
else {
[diskObj setRawDeviceOpen:YES];
}
}
return(0);
} /* ideopen() */
__private_extern__ int
ideclose(dev_t dev, int flag, int devtype, struct proc * pp)
{
id diskObj = ide_dev_to_id(dev);
if(diskObj == nil)
return(ENXIO);
if(IO_DISK_PART(dev) == IDE_LIVE_PART) {
return 0;
} else {
if(![diskObj isInstanceOpen])
return(ENXIO);
}
/*
* Register this 'Unix-level close' event. We won't be called unless
* this is the last close.
*/
if(major(dev) == ide_block_major) {
[diskObj setBlockDeviceOpen:NO];
if ( ideOpenCount )
{
if ( !--ideOpenCount )
{
IOSleep(1000);
}
}
}
else {
[diskObj setRawDeviceOpen:NO];
}
return(0);
} /* ideclose() */
/*
* Raw I/O uses standard UNIX physio routine, resulting in async I/O requests
* via idestrategy().
*/
__private_extern__ int
ideread(dev_t dev, struct uio *uiop, int ioflag)
{
id diskObj = ide_dev_to_id(dev);
int unit = IO_DISK_UNIT(dev);
int rtn;
if(diskObj == nil) {
return(ENXIO);
}
rtn = physio(idestrategy,
ide_dev[unit].physbuf,
dev,
B_READ,
ideminphys,
uiop,
[diskObj blockSize]);
return rtn;
} /* ideread() */
__private_extern__ int
idewrite(dev_t dev, struct uio *uiop, int ioflag)
{
id diskObj = ide_dev_to_id(dev);
int unit = IO_DISK_UNIT(dev);
int rtn;
if(diskObj == nil)
return(ENXIO);
rtn = physio(idestrategy,
ide_dev[unit].physbuf,
dev,
B_WRITE,
ideminphys,
uiop,
[diskObj blockSize]);
return rtn;
} /* idewrite() */
__private_extern__ void
idestrategy(struct buf *bp)
{
id diskObj = ide_dev_to_id(bp->b_dev);
u_int offset;
u_int bytes_req;
void *bufp;
u_int block_size;
IOReturn rtn;
vm_task_t client;
if(diskObj == nil) {
bp->b_error = ENXIO;
goto bad;
}
if((bp->b_flags & (B_PHYS|B_KERNSPACE)) == B_PHYS) {
/*
* Physical I/O to user space.
*/
client = IOVmTaskForBuf(bp);
}
else {
/*
* Either block I/O (always kernel space) or physical I/O
* to kernel space (e.g., loadable file system).
*/
client = IOVmTaskSelf();
}
block_size = [diskObj blockSize];
if (block_size == 0) {
bp->b_error = ENXIO;
goto bad;
}
offset = bp->b_blkno;
bytes_req = bp->b_bcount;
bufp = bp->b_un.b_addr;
if (bp->b_flags & B_READ) {
rtn = [diskObj readAsyncAt:offset
length:bytes_req
buffer:bufp
pending:bp
client:client];
}
else {
rtn = [diskObj writeAsyncAt:offset
length:bytes_req
buffer:bufp
pending:bp
client:client];
}
if (rtn) {
bp->b_error = [diskObj errnoFromReturn:rtn];
goto bad;
}
return(0);
bad:
bp->b_flags |= B_ERROR;
rtn = -1;
biodone(bp);
return(rtn);
} /* fdstrategy() */
/*
* Ops which are common to all IODiskPartitions are done on the raw device;
* Ide-specific ioctls are done directly to the live Ide object.
*/
__private_extern__ int
ideioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc * pp)
{
int unit = IO_DISK_UNIT(dev);
#ifdef GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
int part = IO_DISK_PART(dev);
#endif GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
id diskObj;
IODevAndIdInfo *idmap;
int rtn = 0;
IOReturn irtn = IO_R_SUCCESS;
struct ucred cred;
u_short acflags;
// user src/dest
int i;
int nblk;
ideIoReq_t *ideIoReq;
int error;
void *userPtr;
// in ideIoReq_t
BOOL wrFlag = NO;
unsigned bSize = 0;
unsigned char *alignedPtr;
#ifdef GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
if ( ! ( unit < MAX_IDE_UNIT ) )
{
unit -= MAX_IDE_UNIT;
part += NUM_IDE_PART;
}
#endif GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
if (unit > NUM_IDE_DEV)
return (ENXIO);
#if 0
#ifdef GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
if ((major(dev) != ide_raw_major) || (part >= 2*NUM_IDE_PART)) {
#else GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
if ((major(dev) != ide_raw_major) || (part >= NUM_IDE_PART)) {
#endif GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
return (ENXIO);
}
#endif
idmap = &IdeIdMap[unit];
#ifdef GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
/*
* Special cases for HFS partitions.
* DKIOCNUMBLKS - return # blocks on partition, not the whole device
* DKIOCSFORMAT - not permitted
* DKIOCGFORMAT - not permitted
* DKIOCGLABEL - not permitted
* DKIOCSLABEL - not permitted
*/
/* Is it an HFS partition ? */
if ( ! ( part < NPART ) )
{
switch ( cmd )
{
case DKIOCNUMBLKS:
/*
* For an HFS partition, return the # of blocks in the
* partition, rather than # of blocks on the whole device.
*/
diskObj = idmap->partitionId[part];
*(int *)data = [diskObj diskSize];
return (0);
break;
case DKIOCSFORMAT:
case DKIOCGFORMAT:
case DKIOCGLABEL:
case DKIOCSLABEL:
return(EINVAL);
break;
}
}
#endif GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
/*
* First verify valid device.
*/
switch (cmd) {
case DKIOCSFORMAT:
case DKIOCGFORMAT:
case DKIOCGLABEL:
case DKIOCSLABEL:
/*
* Raw device, whatever the caller asked for.
*/
diskObj = idmap->partitionId[0];
break;
case DKIOCGLOCATION:
diskObj = idmap->partitionId[part];
break;
case IDEDIOCREQ:
case IDEDIOCINFO:
case DKIOCINFO:
case DKIOCBLKSIZE:
case DKIOCNUMBLKS:
diskObj = idmap->liveId;
break;
default:
return (EINVAL);
}
if (diskObj == nil)
goto nodev;
switch (cmd) {
case DKIOCSFORMAT:
/*
* This can fail if block devices attached to this disk are open.
*/
irtn = [diskObj setFormatted:(*(u_int *) data)];
break;
case DKIOCGFORMAT:
{
*(int *)data = [diskObj isFormatted];
break;
}
case DKIOCGLABEL:
{
struct disk_label *labelp;
labelp = (struct disk_label *)IOMalloc(sizeof(*labelp));
irtn = [diskObj readLabel:labelp];
if (irtn == IO_R_SUCCESS) {
*(struct disk_label *)data = *labelp;
}
IOFree(labelp, sizeof(*labelp));
break;
}
case DKIOCSLABEL:
{
struct disk_label *labelp;
labelp = (struct disk_label *)IOMalloc(sizeof(*labelp));
*labelp = *(struct disk_label *)data;
irtn = [diskObj writeLabel:labelp];
IOFree(labelp, sizeof(*labelp));
break;
}
case DKIOCINFO:
{
struct drive_info info;
bzero(&info, sizeof(info));
strcpy(info.di_name,[diskObj driveName]);
info.di_devblklen = [diskObj blockSize];
info.di_maxbcount = IDE_MAX_PHYS_IO;
if (info.di_devblklen) {
nblk = howmany(sizeof(struct disk_label),
info.di_devblklen);
} else {
nblk = 0;
}
for (i = 0; i < NLABELS; i++)
info.di_label_blkno[i] = nblk * i;
*(struct drive_info *) data = info;
break;
}
case DKIOCBLKSIZE:
*(int *)data = [diskObj blockSize];
break;
case DKIOCNUMBLKS:
*(int *)data = [diskObj diskSize];
break;
case DKIOCGLOCATION:
if( nil == [diskObj getDevicePath:((struct drive_location *)data)->location
maxLength:sizeof(((struct drive_location *)data)->location)
useAlias:YES] )
irtn = IO_R_NO_MEMORY;
break;
case IDEDIOCREQ:
/*
* Perform specified I/O.
*/
ideIoReq = (ideIoReq_t *) data;
// if (!suser() && (ideIoReq->cmd != IDE_IDENTIFY_DRIVE))
if (suser(pp->p_ucred, &pp->p_acflag) &&
(ideIoReq->cmd != IDE_IDENTIFY_DRIVE)) {
// return (u.u_error);
return (EINVAL);
}
if ((ideIoReq->cmd == IDE_WRITE_DMA) ||
(ideIoReq->cmd == IDE_READ_DMA)) {
if ([[diskObj cntrlr] isDmaSupported:[diskObj driveNum]] != TRUE)
return (EINVAL);
}
if (ideIoReq->cmd == IDE_IDENTIFY_DRIVE)
ideIoReq->blkcnt = 1;
userPtr = (void *)ideIoReq->addr;
alignedPtr = ideIoReq->addr;
if ((ideIoReq->cmd == IDE_WRITE) ||
(ideIoReq->cmd == IDE_READ) ||
(ideIoReq->cmd == IDE_READ_MULTIPLE) ||
(ideIoReq->cmd == IDE_WRITE_MULTIPLE) ||
(ideIoReq->cmd == IDE_READ_DMA) ||
(ideIoReq->cmd == IDE_WRITE_DMA) ||
(ideIoReq->cmd == IDE_IDENTIFY_DRIVE)) {
if (ideIoReq->blkcnt != 0) {
bSize = [diskObj blockSize];
wrFlag = ((ideIoReq->cmd == IDE_WRITE) ||
(ideIoReq->cmd == IDE_WRITE_MULTIPLE) ||
(ideIoReq->cmd == IDE_WRITE_DMA));
alignedPtr = (unsigned char *)
IOMalloc(ideIoReq->blkcnt * bSize);
if (alignedPtr == 0) {
ideIoReq->status = IDER_MEMALLOC;
return (ENOMEM);
}
if (wrFlag) {
error = copyin(ideIoReq->addr,
alignedPtr,
ideIoReq->blkcnt * bSize);
if (error) {
ideIoReq->status = IDER_MEMFAIL;
goto err_exit;
}
}
}
ideIoReq->addr = (caddr_t) alignedPtr;
ideIoReq->map = (struct vm_map *)IOVmTaskSelf();
}
[diskObj ideXfrIoReq:ideIoReq];
/*
* Note if we got this far, we'll return 0; any errors are in
* ideIoReq->status.
*/
if ((ideIoReq->cmd == IDE_WRITE) ||
(ideIoReq->cmd == IDE_READ) ||
(ideIoReq->cmd == IDE_READ_MULTIPLE) ||
(ideIoReq->cmd == IDE_WRITE_MULTIPLE) ||
(ideIoReq->cmd == IDE_READ_DMA) ||
(ideIoReq->cmd == IDE_WRITE_DMA) ||
(ideIoReq->cmd == IDE_IDENTIFY_DRIVE)) {
ideIoReq->addr = userPtr;
if (((ideIoReq->cmd == IDE_READ) ||
(ideIoReq->cmd == IDE_READ_MULTIPLE) ||
(ideIoReq->cmd == IDE_IDENTIFY_DRIVE) ||
(ideIoReq->cmd == IDE_READ_DMA)) &&
(ideIoReq->blocks_xfered != 0)) {
error = copyout(alignedPtr,
userPtr,
ideIoReq->blocks_xfered * bSize);
if (error) {
ideIoReq->status = IDER_MEMFAIL;
}
}
err_exit:
/*
* if we malloc'd any memory, free it.
*/
if (ideIoReq->blkcnt != 0)
IOFree(alignedPtr, ideIoReq->blkcnt * bSize);
}
break;
case IDEDIOCINFO:
{
ideDriveInfo_t *idep = (ideDriveInfo_t *) data;
*idep = (ideDriveInfo_t)[diskObj ideGetDriveInfo];
break;
}
default:
return (EINVAL);
}
if (irtn)
rtn = [diskObj errnoFromReturn:irtn];
return (rtn);
nodev:
return (ENXIO);
} /* ideioctl() */
/*
* Obtain physical block size.
*/
__private_extern__ int
idesize(dev_t dev)
{
id diskObj = ide_dev_to_id(dev);
if(diskObj == nil) {
return -1;
}
return [diskObj blockSize];
}
/*
* Returns block and char major nums. This is used so that the PostLoad
* program can create block and character nodes for IDE.
*/
__private_extern__ void
ide_block_char_majors(int *blockmajor, int *charmajor)
{
*blockmajor = ide_block_major;
*charmajor = ide_raw_major;
}
static unsigned ideminphys(struct buf *bp)
{
if (bp->b_bcount > IDE_MAX_PHYS_IO)
bp->b_bcount = IDE_MAX_PHYS_IO;
return(bp->b_bcount);
}
/*
* Map dev_t to id. A nil return indicates ENXIO.
*/
static id ide_dev_to_id(dev_t dev)
{
id rtn;
int unit = IO_DISK_UNIT(dev);
int part = IO_DISK_PART(dev);
IODevAndIdInfo *idmap;
#ifdef GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
if ( ! ( unit < MAX_IDE_UNIT ) )
{
/* Modify the unit # under the assumption that it is an HFS device. */
unit -= MAX_IDE_UNIT;
/* Is it an HFS device? */
if ( ! ( unit < MAX_IDE_UNIT ) ) {
rtn = nil;
goto Return;
}
/* Yes, it is an HFS device. */
/* But it doesn't have a raw variant. */
if ( major(dev) == ide_raw_major ) {
rtn = nil;
goto Return;
}
/* Modify the partition # to indicate an HFS partition and then proceed. */
part += NUM_IDE_PART;
}
#else GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
if((unit >= NUM_IDE_DEV) || (part >= NUM_IDE_PART)) {
return nil;
}
#endif GROK_APPLE //bknight - 2/21/98 - Radar 2204950, 2004660
idmap = &IdeIdMap[unit];
if(part == IDE_LIVE_PART) {
if(major(dev) == ide_block_major) {
rtn = nil;
}
else {
rtn = idmap->liveId;
}
}
else {
rtn = idmap->partitionId[part];
}
Return:
return rtn;
}
/* end of IdeKern.m */