Coherent4.2.10/coh.386/lib/uio.c
/* $Header: $ */
#define _DDI_DKI 1
#define _SYSV3 1
/*
* Implementations of the functions which manipulate the uio(4) structure
* under the DDI/DKI. This function runs in _SYSV3 mode to interface with
* the iBCS2 Coherent kernel.
*
* $Log: $
*/
/*
*-IMPORTS:
* <common/ccompat.h>
* __CONST__
* __EXTERN_C_BEGIN__
* __EXTERN_C_END__
* __USE_PROTO__
* __ARGS ()
* __PROTO ()
* <sys/debug.h>
* ASSERT ()
* <sys/types.h>
* _VOID
* size_t
* <sys/errno.h>
* EFAULT
* <limits.h>
* CHAR_BIT
* UCHAR_MAX
* <string.h>
* memcpy ()
*/
#include <common/ccompat.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <limits.h>
#include <string.h>
#include <sys/uio.h>
#if __COHERENT__
/*
* Routines to perform transfers between the user and kernel address spaces.
*/
__EXTERN_C_BEGIN__
int kucopy __PROTO ((__CONST__ caddr_t ker, caddr_t usr,
size_t n));
int ukcopy __PROTO ((__CONST__ caddr_t usr, caddr_t ker,
size_t n));
int getubd __PROTO ((__CONST__ caddr_t usr));
void putubd __PROTO ((caddr_t usr, int ch));
__EXTERN_C_END__
#define TOUSER(usr,ker,n) kucopy ((caddr_t) ker, (caddr_t) usr, n)
#define FROMUSER(ker,usr,n) ukcopy ((caddr_t) usr, (caddr_t) ker, n)
#define PUTBYTE(c,usr) (putubd (usr, c), get_user_error ())
#define GETBYTE(usr) ((unsigned char) getubd (usr) - \
(get_user_error () > 0 ? UCHAR_MAX : 0))
#else /* if ! __COHERENT__ */
#define TOUSER(usr,ker,n) (memcpy ((usr), (ker), (n)), (n))
#define FROMUSER(ker,usr,n) (memcpy ((ker), (usr), (n)), (n))
#define PUTBYTE(c,usr) (* (usr) = (c), 0)
#define GETBYTE(usr) (* (unsigned char *) (usr))
#endif
/*
* For some reason the System V DDI/DKI uses "longs" and "ints" in many cases
* where a size_t is far more appropriate. We will only work for cases where
* the request does not exceed the half range of a size_t.
*/
#define SIZE_T_MAX ((1UL << (sizeof (size_t) * CHAR_BIT - 1)) - 1)
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* bcopy Copy data between address locations in the kernel.
*
*-SYNOPSIS:
* #include <sys/types.h>
*
* void bcopy (caddr_t from, caddr_t to, size_t bcount);
*
*-ARGUMENTS:
* from Source address from which the copy is made.
*
* to Destination address to which the copy is made.
*
* bcount Number of bytes to be copied.
*
*-DESCRIPTION:
* bcopy () copies "bcount" bytes from one kernel address to another. It
* chooses the best algorithm based on address alignment and number of
* bytes to copy. If the input and output addresses over, the function
* executes, but the results are undefined.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* The source and destination address ranges must both be within the
* kernel address space and must be memory resident. No range checking is
* done. Since there is no mechanism by which drivers that conform to the
* rules of the DDI/DKI can obtain and use a kernel address which is not
* memory resident (an address which is paged out), DDI/DKI conforming
* drivers can assume that any address to which they have access is
* memory resident and therefore a valid argument to bcopy (). Addresses
* within user space are not valid arguments, and specifying such an
* address may cause the driver to corrupt the system in an unpredictable
* way. For copying between kernel and user space, drivers must use an
* appropriate function defined for that purpose (for example, copyin (),
* copyout (), uiomove (), ureadc (), or uwritec ()).
*
*-SEE ALSO:
* bzero (), copyin (), copyout (), uiomove (), ureadc (), uwritec ()
*/
#if __USE_PROTO__
void (bcopy) (__CONST__ _VOID * from, _VOID * to, size_t bcount)
#else
void
bcopy __ARGS ((from, to, bcount))
__CONST__ _VOID
* from;
_VOID * to;
size_t bcount;
#endif
{
ASSERT (to != NULL);
(void) memcpy (to, from, bcount);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* bzero Clear memory for a given number of bytes.
*
*-SYNOPSIS:
* #include <sys/types.h>
*
* void bzero (caddr_t addr, size_t bytes);
*
*-ARGUMENTS:
* addr Starting virtual address of memory to be cleared.
*
* bytes Number of bytes to clear.
*
*-DESCRIPTION:
* The bzero () function clears a contiguous portion of memory by filling
* the memory with zeroes. It chooses the best algorithm based on address
* alignment and number of bytes to clear.
*
*-RETURN VALUE:
* None.
*
*-LEVEL:
* Base or interrupt.
*
*-NOTES:
* Does not sleep.
*
* Driver-defined basic locks, read/write locks, and sleep locks may be
* held across calls to this function.
*
* There are no alignment restrictions on "addr", and no length
* restrictions on "bytes", other than the address range specified must
* be within the kernel address space and must be memory resident. No
* range checking is done. Since there is no mechanism by which drivers
* that conform to the rules of the DDI/DKI can obtain and use a kernel
* address that is not memory resident (an address that is paged out),
* DDI/DKI conforming drivers can assume that any address to which they
* have access is memory resident and therefore a valid argument to
* bzero (). An address within user space is not a valid argument to
* bzero (), and specifying such an address may cause the driver to
* corrupt the system in an unpredictable way.
*
*-SEE ALSO:
* bcopy (), kmem_zalloc ()
*/
#if __USE_PROTO__
void (bzero) (_VOID * addr, size_t bytes)
#else
void
bzero __ARGS ((addr, bytes))
_VOID * addr;
size_t bytes;
#endif
{
ASSERT (addr != NULL);
(void) memset (addr, 0, bytes);
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* copyin Copy data from a user buffer to a driver buffer.
*
*-SYNOPSIS:
* #include <sys/types.h>
*
* int copyin (caddr_t userbuf, caddr_t driverbuf, size_t count);
*
*-ARGUMENTS:
* userbuf User source address from which copy is made.
*
* driverbuf Driver destination address to which copy is made.
*
* count Number of bytes to copy.
*
*-DESCRIPTION:
* copyin () copies "count" bytes of data from the user virtual address
* specified by "userbuf" to the kernel virtual address specified by
* "driverbuf". The driver must ensure that adequate space is allocated
* for the destination address.
*
* copyin () chooses the best algorithm based on address alignment and
* number of bytes to copy. Although the source and destination addresses
* are not required to be word aligned, word aligned addresses may result
* in a more efficient copy.
*
*-RETURN VALUE:
* If the copy is successful, 0 is returned. Otherwise, -1 is returned to
* indicate the specified user address range is invalid.
*
*-LEVEL:
* Base only.
*
*-NOTES:
* May sleep.
*
* Drivers usually convert a return value of -1 to an EFAULT error.
*
* Driver-defined basic locks and read/write locks may not be held across
* calls to this function.
*
* Driver-defined sleep locks may be held across calls to this function.
*
* When holding sleep locks across calls to this function, drivers must
* be careful to avoid creating a deadlock. During the data transfer,
* page fault resolution might result in another I/O to the same device.
* For example, this could occur if the driver controls the disk drive
* used as the swap device.
*
* The driver destination buffer must be completely within the kernel
* address space, or the system can panic.
*
*-SEE ALSO:
* copyout (), uiomove (), ureadc (), uwritec ()
*/
#if __USE_PROTO__
int (copyin) (_VOID * userbuf, _VOID * driverbuf, size_t count)
#else
int
copyin __ARGS ((userbuf, driverbuf, count))
_VOID * userbuf;
_VOID * driverbuf;
size_t count;
#endif
{
ASSERT (driverbuf != 0);
return FROMUSER (driverbuf, userbuf, count) != count ? -1 : 0;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* copyout Copy data from a driver buffer to a user buffer.
*
*-SYNOPSIS:
* #include <sys/types.h>
*
* int copyout (caddr_t driverbuf, caddr_t userbuf, size_t count);
*
*-ARGUMENTS:
* driverbuf Driver source address from which copy is made.
*
* userbuf User destination address to which copy is made.
*
* count Number of bytes to copy.
*
*-DESCRIPTION:
* copyout () copies "count" bytes of data from the kernel virtual
* address specified by "driverbuf" to the user virtual address specified
* by "userbuf".
*
* copyout () chooses the best algorithm based on address alignment and
* number of bytes to copy. Although the source and destination addresses
* are not required to be word aligned, word aligned addresses may result
* in a more efficient copy.
*
*-RETURN VALUE:
* If the copy is successful, 0 is returned. Otherwise, -1 is returned to
* indicate that the specified user address range is not valid.
*
*-LEVEL:
* Base only.
*
*-NOTES:
* May sleep.
*
* Drivers usually convert a return value of -1 into an EFAULT error.
*
* Driver-defined basic locks and read/write locks may not be held across
* calls to this function.
*
* Driver-defined sleep locks may be held across calls to this function.
*
* When holding sleep locks across calls to this function, drivers must
* be careful to avoid creating a deadlock. During the data transfer,
* page fault resolution may result in another I/O to the same device.
* For example, this could occur if the driver controls the disk drive
* used as the swap device.
*
* The driver source buffer must be completely within the kernel address
* space, or the system can panic.
*
*-SEE ALSO:
* copyin (), uiomove (), ureadc (), uwritec ()
*/
#if __USE_PROTO__
int (copyout) (_VOID * driverbuf, _VOID * userbuf, size_t count)
#else
int
copyout __ARGS ((driverbuf, userbuf, count))
_VOID * driverbuf;
_VOID * userbuf;
size_t count;
#endif
{
ASSERT (driverbuf != NULL);
return TOUSER (userbuf, driverbuf, count) != count ? -1 : 0;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* uiomove Copy data using uio structure.
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <sys/uio.h>
*
* int uiomove (caddr_t addr, long nbytes, uio_rw_t rwflag, uio_t * uiop);
*
*-ARGUMENTS:
* addr Source/destination kernel address of the copy.
*
* nbytes Number of bytes to copy.
*
* rwflag Flag indicating read or write operation. Possible
* values are UIO_READ and UIO_WRITE.
*
* uiop Pointer to the "uio" structure for the copy.
*
*-DESCRIPTION:
* The uiomove () function copies "nbytes" of data between the kernel
* address "addr" and the space defined by the "uio" structure pointed to
* by "uiop". If "rwflg" is "UIO_READ", the data is copied from "addr" to
* the space described by the "uio" structure. If "rwflag" is "UIO_WRITE",
* the data is copied from the space described by the "uio" structure to
* "addr".
*
* The "uio_segflg" member of the "uio" structure specifies the type of
* space described by the "uio" structure. If "uio_segflg" is set to
* "UIO_SYSSPACE" the "uio" structure describes a portion of the kernel
* address space. If "uio_segflg" is set to "UIO_USERSPACE" the "uio"
* structure describes a portion of the user address space.
*
* If the copy is successful, uiomove () updates the appropriate members
* of the "uio" and "iovec" structures to reflect the copy ("uio_offset"
* and "iov_base" and increased by "nbytes" and "uio_resid" and "iov_len"
* are decreased by "nbytes").
*
*-RETURN VALUE:
* uiomove () returns 0 on success or an error number on failure. If a
* partial transfer occurs, the "uio" structure is updated to indicate
* the amount not transferred and an error is returned.
*
*-LEVEL:
* Base only if "uio_segflg" is set to "UIO_USERSPACE". Base or interrupt
* if "uio_segflg" is set to "UIO_SYSSPACE".
*
*-NOTES:
* May sleep if "uio_segflg" is set to "UIO_USERSPACE".
*
* Driver-defined basic locks and read/write locks may be held across
* calls to this function if "uio_segflg" is "UIO_SYSSPACE" but may not be
* held if "uio_segflg" is "UIO_USERSPACE".
*
* Driver-defined sleep locks may be held across calls to this function
* regardless of the value of "uio_segflg".
*
* When holding locks across calls to this function, drivers must be
* careful to avoid creating a deadlock. During the data transfer, page
* fault resolution might result in another I/O to the same device. For
* example, this could occur if the driver controls the disk drive used
* as the swap device.
*
* If "addr" specifies an address in user space, or if the value of
* "uio_segflg" is not consistent with the type of address space
* described by the "uio" structure, the system can panic.
*
*-SEE ALSO:
* bcopy (), copyin (), copyout (), ureadc (), uwritec (), iovec, uio
*/
#if __USE_PROTO__
int (uiomove) (caddr_t addr, long nbytes, uio_rw_t rwflag, uio_t * uiop)
#else
int
uiomove __ARGS ((addr, nbytes, rwflag, uiop))
caddr_t addr;
long nbytes;
uio_rw_t rwflag;
uio_t * uiop;
#endif
{
iovec_t * scan;
iovec_t * end;
ASSERT (addr != NULL);
ASSERT (uiop != NULL);
ASSERT (nbytes > 0);
ASSERT (nbytes <= SIZE_T_MAX);
ASSERT (uiop->uio_resid <= SIZE_T_MAX);
ASSERT (rwflag == UIO_READ || rwflag == UIO_WRITE);
ASSERT (uiop->uio_segflg == UIO_SYSSPACE ||
uiop->uio_segflg == UIO_USERSPACE);
ASSERT (uiop->uio_resid > 0);
for (scan = uiop->uio_iov, end = scan + uiop->uio_iovcnt ;
nbytes > 0 ; scan ++) {
size_t size;
/*
* Now we are going to move a single block of the scatter/
* gather request. We assume that scan->iov_len is
* less than or equal to uiop->uio_resid!
*/
ASSERT (scan < end);
ASSERT (scan->iov_len > 0);
ASSERT (scan->iov_len <= uiop->uio_resid);
if (nbytes < (size = (size_t) uiop->uio_iov->iov_len))
size = (size_t) nbytes;
if (size == 0)
continue;
/*
* Transfer a segment as indicated.
*/
if (rwflag == UIO_READ) {
if (uiop->uio_segflg == UIO_USERSPACE) {
/*
* Transfers involving user memory may sleep
* due to paging!
*/
if (TOUSER (scan->iov_base, addr,
size) != size)
break;
} else
memcpy (scan->iov_base, addr, size);
} else {
if (uiop->uio_segflg == UIO_USERSPACE) {
/*
* Transfers involving user memory may sleep
* due to paging!
*/
if (FROMUSER (addr, scan->iov_base,
size) != size)
break;
} else
memcpy (addr, scan->iov_base, size);
}
addr += size;
nbytes -= size;
uiop->uio_resid -= size;
uiop->uio_offset += size;
scan->iov_len -= size;
scan->iov_base += size;
}
return nbytes == 0 ? 0 : EFAULT;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* ureadc Copy a character to space described by a "uio"
* structure.
*
*-SYNOPSIS:
* #include <sys/uio.h>
*
* int ureadc (int c, uio_t * uiop);
*
*-ARGUMENTS:
* c The character to be copied.
*
* uiop Pointer to the "uio" structure.
*
*-DESCRIPTION:
* ureadc () copies the character "c" into the space described by the
* "uio" structure pointed to by "uiop".
*
* The "uio_segflg" member of the "uio" structure specifies the type of
* space to which the copy is made. If "uio_segflg" is set to
* "UIO_SYSSPACE" the character is copied to a kernel address. If
* "uio_segflg" is set to "UIO_USERSPACE" the character is copied to a
* user address.
*
* If the character is successfully copied, ureadc () updates the
* appropriate members of the "uio" and "iovec" structures to reflect
* the copy ("uio_offset" and "iov_base" are incremented and "uio_resid"
* and "iov_len" are decremented).
*
*-RETURN VALUE:
* ureadc () returns 0 on success or an error number on failure.
*
*-LEVEL:
* Base only if "uio_segflg" is set to "UIO_USERSPACE". Base or interrupt
* if "uio_segflg" is set to "UIO_SYSSPACE".
*
*-NOTES:
* May sleep if "uio_segflg" is set to "UIO_USERSPACE".
*
* Driver-defined basic locks and read/write locks may be held across
* calls to this function if "uio_segflg" is set to "UIO_SYSSPACE" but
* may not be held if "uio_segflg" is set to "UIO_USERSPACE".
*
* Driver-defined sleep locks may be held across calls to this function
* regardless of the value of "uio_segflg".
*
* When holding locks across calls to this function, drivers must be
* careful to avoid creating a deadlock. During the data transfer, page
* fault resolution might result in another I/O to the same device. For
* example, this could occur if the driver controls the disk drive used
* as the swap device.
*
*-SEE ALSO:
* uiomove (), uwritec (), iovec, uio
*/
#if __USE_PROTO__
int (ureadc) (int c, uio_t * uiop)
#else
int
ureadc __ARGS ((c, uiop))
int c;
uio_t * uiop;
#endif
{
iovec_t * scan;
ASSERT (uiop != NULL);
ASSERT (uiop->uio_segflg == UIO_SYSSPACE ||
uiop->uio_segflg == UIO_USERSPACE);
ASSERT (uiop->uio_segflg == UIO_SYSSPACE ||
uiop->uio_segflg == UIO_USERSPACE);
ASSERT (uiop->uio_resid > 0);
/*
* What to do if the structure is empty? As indicated above, I
* consider that an error worthy of a kernel panic. At a minimum we
* should make it a recoverable error as the code below does.
*/
if (uiop->uio_resid <= 0)
return EFAULT;
/*
* For now, we loop over any initial empty segments. It is not clear
* to me whether this code can alter "uio_iov" or not, although we
* definitely are permitted to maintain a separate entry in the
* structure to the same effect.
*/
for (scan = uiop->uio_iov ; scan->iov_len == 0 ; scan ++)
;
ASSERT (scan < uiop->uio_iov + uiop->uio_iovcnt);
/*
* Now, write a single character to the space indicated.
*/
if (uiop->uio_segflg == UIO_USERSPACE) {
if (PUTBYTE (c, scan->iov_base) != 0)
return EFAULT;
scan->iov_base ++;
} else
* scan->iov_base ++ = c;
scan->iov_len ++;
uiop->uio_resid --;
uiop->uio_offset ++;
return 0;
}
/*
*-STATUS:
* DDI/DKI
*
*-NAME:
* uwritec Return a character from space described by a "uio"
* structure.
*
*-SYNOPSIS:
* #include <sys/uio.h>
*
* int uwritec (uio_t * uiop);
*
*-ARGUMENTS:
* uiop Pointer to the "uio" structure.
*
*-DESCRIPTION:
* uwritec () copies a character from the space described by the "uio"
* structure pointed to by "uiop" and returns the character to the
* caller.
*
* The "uio_segflg" member of the "uio" structure specifies the type of
* space from which the copy is made. If "uio_segflg" is set to
* "UIO_SYSSPACE" the character is copied from a kernel address. If
* "uio_segflg" is set to "UIO_USERSPACE" the character is copied from a
* user address.
*
* If the character is successfully copied, uwritec () updates the
* appropriate members of the "uio" and "iovec" structures to reflect the
* copy ("uio_offset" and "iov_base" are incremented and "uio_resid" and
* "iov_len" are decremented).
*
*-RETURN VALUE:
* If successful, uwritec () returns the character. -1 is returned if the
* space described by the "uio" structure is empty or if there is an
* error.
*
*-LEVEL:
* Base only if "uio_segflg" is set to "UIO_USERSPACE". Base or interrupt
* if "uio_segflg" is set to "UIO_SYSSPACE".
*
*-NOTES:
* May sleep if "uio_segflg" is set to "UIO_USERSPACE".
* Driver-defined basic locks and read/write locks may be held across
* calls to this function if "uio_segflg" is "UIO_SYSSPACE" but may not
* be held if "uio_segflg" is "UIO_USERSPACE".
*
* Driver-defined sleep locks may be held across calls to this function
* regardless of the value of "uio_segflg".
*
* When holding locks across calls to this function, drivers must be
* careful to avoid creating a deadlock. During the data transfer, page
* fault resolution might result in another I/O to the same device. For
* example, this could occur if the driver controls the disk drive used
* as the swap device.
*
*-SEE ALSO:
* uiomove (), ureadc (), iovec, uio
*/
#if __USE_PROTO__
int (uwritec) (uio_t * uiop)
#else
int
uwritec __ARGS ((uiop))
uio_t * uiop;
#endif
{
iovec_t * scan;
int c;
ASSERT (uiop != NULL);
ASSERT (uiop->uio_segflg == UIO_SYSSPACE ||
uiop->uio_segflg == UIO_USERSPACE);
ASSERT (uiop->uio_segflg == UIO_SYSSPACE ||
uiop->uio_segflg == UIO_USERSPACE);
/*
* What to do if the structure is empty? Unlike ureadc (), we are
* allowed to return -1.
*/
if (uiop->uio_resid <= 0)
return -1;
/*
* For now, we loop over any initial empty segments. It is not clear
* to me whether this code can alter "uio_iov" or not, although we
* definitely are permitted to maintain a separate entry in the
* structure to the same effect.
*/
for (scan = uiop->uio_iov ; scan->iov_len == 0 ; scan ++)
;
ASSERT (scan < uiop->uio_iov + uiop->uio_iovcnt);
/*
* Now, read a single character from the space indicated.
*/
if (uiop->uio_segflg == UIO_USERSPACE) {
if ((c = GETBYTE (scan->iov_base)) < 0)
return -1;
scan->iov_base ++;
} else
c = * scan->iov_base ++;
scan->iov_len --;
uiop->uio_resid --;
uiop->uio_offset ++;
return c;
}