Coherent4.2.10/include/kernel/x86lock.h

/* (-lgl
 *	Coherent 386 release 4.2
 *	Copyright (c) 1982, 1993 by Mark Williams Company.
 *	All rights reserved. May not be copied without permission.
 *	For copying permission and licensing info, write licensing@mwc.com
 -lgl) */

#ifndef	__KERNEL_X86LOCK_H__
#define	__KERNEL_X86LOCK_H__

/*
 * Here we encapsulate all the system-specific basics for dealing with multi-
 * and uni-processor locking in an i386 environment. Note that the functions
 * defined here for the user have names that are too long for common ISO C
 * external linkage limitations; this should not be an obstacle, since the
 * functions defined here will normally have internal linkage.
 *
 * Note that none of the functions here are defined in the DDI/DKI. This
 * header is purely for internal purposes and for this reason does not follow
 * the usual DDI/DKI header binary-compatibility structure with external-
 * linkage versions always available.
 */

#include <common/ccompat.h>
#include <common/xdebug.h>
#include <common/__types.h>
#include <kernel/__pl.h>


/*
 * On the 386, there are no "compare and swap" instructions as in the 68020
 * CPU. Unfortunately, this deficiency makes some of the more advanced
 * synchronization algorithms unavailable (and introduces locking requirements
 * where no locking is needed in the 68020).
 *
 * The facilities that do exist are an atomic fetch-and-set via XCHG, and
 * atomic arithmetic via the LOCK instruction prefix. Neither facility is
 * directly available in C, so this header file encapsulates the system-
 * specific methods for gaining access to these atomic operations.
 *
 * We define a wide range of atomic data types, so that in cases where a
 * machine might not be able to support a high-level atomic operation on a
 * data type it will be possible to build the high-level operation out of
 * primitive locking facilities like test-and-set.
 *
 * We use the single-element-array declarations for two reasons: 1) we may
 * as well have implicit reference-passing semantics, and 2) it seems like
 * the easiest way (until C++, at least) of preventing the accidental misuse
 * of data items of these types.
 *
 * Each atomic type is guaranteed to have the same range as the <limits.h>
 * header specifies, but may be considerably larger (and have different
 * alignment requirements) than objects of the base non-atomic type.
 *
 * Dealing with atomic pointers: with C++, there is currently no guarantee
 * equivalent to the ISO C requirement that any pointer type can be converted
 * to and from a value of type "void *" successfully without loss of
 * information. For now, we operate without this guarantee rather than
 * introducing possibly unnecessary complexity via a "generic" structure-
 * pointer type. In point of fact, we don't support the only known case where
 * this doesn't work anyway (Borland C++ medium model, SS != DS) since we
 * assume SS = DS in those small-data models for simplicity anyway.
 *
 * To make things easier for the Borland C++, all the functions defined here
 * are permitted to evaluate their "lock" argument multiple times, which is
 * why the names have been changed to all upper-case.
 */

/*
 * The split between the user-visible (implicit reference) and internal
 * (volatile, decayed pointer) types was effectively forced by differences
 * in the way the available translators dealt with type-qualifiers and casts
 * to values of array type.
 *
 * As it happens, this split appears to have produced the most aesthetically
 * pleasing code forms, much to my surprise, as well as pointing up a lurking
 * problem (a misplaced "volatile") in the treatment of atomic_ptr_t.
 */

typedef signed char			atomic_char_t [1];
typedef	short				atomic_short_t [1];
typedef	int				atomic_int_t [1];
typedef	long				atomic_long_t [1];

typedef	__uchar_t			atomic_uchar_t [1];
typedef __ushort_t			atomic_ushort_t [1];
typedef __uint_t			atomic_uint_t [1];
typedef __ulong_t			atomic_ulong_t [1];

typedef	__VOID__		      *	atomic_ptr_t [1];

typedef	__VOLATILE__ signed char      *	__atomic_char_t;
typedef	__VOLATILE__ __uchar_t	      *	__atomic_uchar_t;
typedef	__VOLATILE__ short	      *	__atomic_short_t;
typedef	__VOLATILE__ __ushort_t	      *	__atomic_ushort_t;
typedef	__VOLATILE__ int	      *	__atomic_int_t;
typedef	__VOLATILE__ __uint_t	      *	__atomic_uint_t;
typedef	__VOLATILE__ long	      *	__atomic_long_t;
typedef	__VOLATILE__ __ulong_t	      *	__atomic_ulong_t;
typedef	__VOID__       * __VOLATILE__ *	__atomic_ptr_t;


/*
 * Here we define C-linkage prototypes for the functions that we want to
 * define (possibly as macros) here, so that it's easy to suppress any macro
 * definitions given below and get full type-checking even for compilers that
 * need macros to get the inline versions.
 *
 * Note that while ATOMIC_TEST_AND_SET_UCHAR has () functionality that is
 * completely availble through ATOMIC_FETCH_AND_STORE_UCHAR (), we define it
 * as a separate function so as not to overly constrain implementations on
 * machines such as the 60820, which has the powerful "compare and swap"
 * operation and the basic "test and set operation", but no basic exchange
 * (whereas the 88100 implements only exchange, and only on bytes and 32-bit
 * long words).
 *
 * Being verbose here is a small price to pay for the later flexibility.
 */


#if	! (__GNUC__ && (defined (i386) || _I386))

__EXTERN_C_BEGIN__

char	   ATOMIC_FETCH_AND_STORE_CHAR	__PROTO ((__atomic_char_t _lock,
						  char _newvalue));
__uchar_t  ATOMIC_FETCH_AND_STORE_UCHAR	__PROTO ((__atomic_uchar_t _lock,
						  __uchar_t _newvalue));
short	   ATOMIC_FETCH_AND_STORE_SHORT	__PROTO ((__atomic_short_t _lock,
						  short _newvalue));
__ushort_t ATOMIC_FETCH_AND_STORE_USHORT __PROTO ((__atomic_ushort_t _lock,
						   __ushort_t _newvalue));
int	   ATOMIC_FETCH_AND_STORE_INT	__PROTO ((__atomic_int_t _lock,
						  int _newvalue));
__uint_t   ATOMIC_FETCH_AND_STORE_UINT	__PROTO ((__atomic_uint_t _lock,
						  __uint_t _newvalue));
long	   ATOMIC_FETCH_AND_STORE_LONG	__PROTO ((__atomic_long_t _lock,
						  long _newvalue));
__ulong_t  ATOMIC_FETCH_AND_STORE_ULONG	__PROTO ((__atomic_ulong_t _lock,
						  __ulong_t _newvalue));

__VOID__ * ATOMIC_FETCH_AND_STORE_PTR	__PROTO ((__atomic_ptr_t _lock,
						  __VOID__ * _newvalue));

char	   ATOMIC_FETCH_CHAR		__PROTO ((__atomic_char_t _lock));
__uchar_t  ATOMIC_FETCH_UCHAR		__PROTO ((__atomic_uchar_t _lock));
short	   ATOMIC_FETCH_SHORT		__PROTO ((__atomic_short_t _lock));
__ushort_t ATOMIC_FETCH_USHORT		__PROTO ((__atomic_ushort_t _lock));
int	   ATOMIC_FETCH_INT		__PROTO ((__atomic_int_t _lock));
__uint_t   ATOMIC_FETCH_UINT		__PROTO ((__atomic_uint_t _lock));
long	   ATOMIC_FETCH_LONG		__PROTO ((__atomic_long_t _lock));
__ulong_t  ATOMIC_FETCH_ULONG		__PROTO ((__atomic_ulong_t _lock));
__VOID__ * ATOMIC_FETCH_PTR		__PROTO ((__atomic_ptr_t _lock));

int	   ATOMIC_TEST_AND_SET_UCHAR	__PROTO ((__atomic_uchar_t _lock));

void	   ATOMIC_STORE_PTR		__PROTO ((__atomic_ptr_t _lock,
						  __VOID__ * _newvalue));

void	   ATOMIC_CLEAR_UCHAR		__PROTO ((__atomic_uchar_t _lock));
void	   ATOMIC_CLEAR_USHORT		__PROTO ((__atomic_ushort_t _lock));
void	   ATOMIC_CLEAR_PTR		__PROTO ((__atomic_ptr_t _lock));

__EXTERN_C_END__


#else	/* __GNUC__ && (defined (i386) || _I386) */


__LOCAL__ __INLINE__
char ATOMIC_FETCH_AND_STORE_CHAR (__atomic_char_t _lock,
				  char _newvalue) {
	__NON_ISO (asm) volatile ("xchgb %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
__uchar_t ATOMIC_FETCH_AND_STORE_UCHAR (__atomic_uchar_t _lock,
					__uchar_t _newvalue) {
	__NON_ISO (asm) volatile ("xchgb %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
short ATOMIC_FETCH_AND_STORE_SHORT (__atomic_short_t _lock,
				    short _newvalue) {
	__NON_ISO (asm) volatile ("xchgw %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
__ushort_t ATOMIC_FETCH_AND_STORE_USHORT (__atomic_ushort_t _lock,
					  __ushort_t _newvalue) {
	__NON_ISO (asm) volatile ("xchgw %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
int ATOMIC_FETCH_AND_STORE_INT (__atomic_int_t _lock,
				int _newvalue) {
	__NON_ISO (asm) volatile ("xchgl %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
__uint_t ATOMIC_FETCH_AND_STORE_UINT (__atomic_uint_t _lock,
				      __uint_t _newvalue) {
	__NON_ISO (asm) volatile ("xchgl %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
long ATOMIC_FETCH_AND_STORE_LONG (__atomic_long_t _lock,
				  long _newvalue) {
	__NON_ISO (asm) volatile ("xchgl %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
__ulong_t ATOMIC_FETCH_AND_STORE_ULONG (__atomic_ulong_t _lock,
					__ulong_t _newvalue) {
	__NON_ISO (asm) volatile ("xchgl %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__
__VOID__ * ATOMIC_FETCH_AND_STORE_PTR (__atomic_ptr_t _lock,
				       __VOID__ * _newvalue) {
	__NON_ISO (asm) volatile ("xchgl %0, %2" : "=r" (_newvalue) :
				  "0" (_newvalue), "m" (_lock [0]));
	return _newvalue;
}

__LOCAL__ __INLINE__ char ATOMIC_FETCH_CHAR (__atomic_char_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ __uchar_t ATOMIC_FETCH_UCHAR (__atomic_uchar_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ short ATOMIC_FETCH_SHORT (__atomic_short_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ __ushort_t ATOMIC_FETCH_USHORT (__atomic_ushort_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ int ATOMIC_FETCH_INT (__atomic_int_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ __uint_t ATOMIC_FETCH_UINT (__atomic_uint_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ long ATOMIC_FETCH_LONG (__atomic_long_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ __ulong_t ATOMIC_FETCH_ULONG (__atomic_ulong_t _lock) {
	return _lock [0];
}

__LOCAL__ __INLINE__ __VOID__ * ATOMIC_FETCH_PTR (__atomic_ptr_t _lock) {
	return (__VOID__ *) _lock [0];
}

__LOCAL__ __INLINE__ int ATOMIC_TEST_AND_SET_UCHAR (__atomic_uchar_t _lock) {
	return ATOMIC_FETCH_AND_STORE_UCHAR (_lock, (__uchar_t) -1);
}

__LOCAL__ __INLINE__ void ATOMIC_CLEAR_UCHAR (__atomic_uchar_t _lock) {
	_lock [0] = 0;
}

__LOCAL__ __INLINE__ void ATOMIC_CLEAR_USHORT (__atomic_ushort_t _lock) {
	_lock [0] = 0;
}

__LOCAL__ __INLINE__ void ATOMIC_CLEAR_PTR (__atomic_ptr_t _lock) {
	_lock [0] = 0;
}

__LOCAL__ __INLINE__
void ATOMIC_STORE_UCHAR (__atomic_uchar_t _lock, __uchar_t _newvalue) {
	_lock [0] = _newvalue;
}

__LOCAL__ __INLINE__
void ATOMIC_STORE_PTR (__atomic_ptr_t _lock, __VOID__ * _newvalue) {
	_lock [0] = _newvalue;
}

#endif


#if	__BORLANDC__

void	__emit__	(unsigned char __byte, ...);

/*
 * Borland C++ has many restrictions on assembly and in-line functions. To
 * get around some of these, we use the 32-bit-register pseudovariables that
 * are available when targetting the 386. Note how we convert 32-bit values
 * into 16-bit seg:offset pairs and back via the stack; this avoids several
 * of the nastier bugs in the 32-bit support.
 */

#define	__USE_ES__		0x26
#define	__XCHG_AL_ESBX__	__USE_ES__, 0x86, 0x07
#define	__XCHG_AX_ESBX__	__USE_ES__, 0x87, 0x07
#define	__XCHG_EAX_ESBX__	__USE_ES__, 0x66, 0x87, 0x07
#define	__MOV_AX_ESBX__		__USE_ES__, 0x8B, 0x07
#define	__MOV_EAX_ESBX__	__USE_ES__, 0x66, 0x8B, 0x07

#if	defined (__LARGE__) || defined (__HUGE__) || defined (__COMPACT__)
#define	__lockaddr(l)		(_ES = (unsigned) (void __seg *) \
						(void __far *) (l),\
				 _BX = (unsigned) (l))
#else
#define	__lockaddr(l)		(_ES = _DS, _BX = (unsigned) (l))
#endif


#define ATOMIC_FETCH_AND_STORE_CHAR(l,v) (__lockaddr (l), _AL = (v),\
					  __emit__ (__XCHG_AL_ESBX__),\
					  (char) _AL)
#define	ATOMIC_FETCH_AND_STORE_UCHAR(l,v) (__lockaddr (l), _AL = (v),\
					   __emit__ (__XCHG_AL_ESBX__),\
					   (__uchar_t) _AL)
#define ATOMIC_FETCH_AND_STORE_SHORT(l,v) (__lockaddr (l), _AX = (v),\
					   __emit__ (__XCHG_AX_ESBX__),\
					   (short) _AX)
#define	ATOMIC_FETCH_AND_STORE_USHORT(l,v) (__lockaddr (l), _AX = (v),\
					    __emit__ (__XCHG_AX_ESBX__),\
					    (__ushort_t) _AX)
#define ATOMIC_FETCH_AND_STORE_INT(l,v)	(__lockaddr (l), _AX = (v),\
					 __emit__ (__XCHG_AX_ESBX__),\
					 (int) _AX)
#define	ATOMIC_FETCH_AND_STORE_UINT(l,v) (__lockaddr (l), _AX = (v),\
					  __emit__ (__XCHG_AX_ESBX__),\
					  (__uint_t) _AX)
#define ATOMIC_FETCH_AND_STORE_LONG(l,v) (__lockaddr (l), _EAX = (v),\
					  __emit__ (__XCHG_EAX_ESBX__),\
					  (long) _EAX)
#define	ATOMIC_FETCH_AND_STORE_ULONG(l,v) (__lockaddr (l), _EAX = (v),\
					   __emit__ (__XCHG_EAX_ESBX__),\
					   (__ulong_t) _EAX)

#define	ATOMIC_FETCH_CHAR(l)		(((__atomic_char_t) (l)) [0])
#define	ATOMIC_FETCH_UCHAR(l)		(((__atomic_uchar_t) (l)) [0])
#define	ATOMIC_FETCH_SHORT(l)		(((__atomic_short_t) (l)) [0])
#define	ATOMIC_FETCH_USHORT(l)		(((__atomic_ushort_t) (l)) [0])
#define	ATOMIC_FETCH_INT(l)		(((__atomic_int_t) (l)) [0])
#define	ATOMIC_FETCH_UINT(l)		(((__atomic_uint_t) (l)) [0])
#define	ATOMIC_FETCH_LONG(l)		((long) (_EAX = ((__atomic_long_t) (l)) [0]))
#define	ATOMIC_FETCH_ULONG(l)		((__ulong_t) (_EAX = ((__atomic_ulong_t) (l)) [0]))

#define	ATOMIC_TEST_AND_SET_UCHAR(l)	ATOMIC_FETCH_AND_STORE_UCHAR (l, (__uchar_t) -1)

#define	ATOMIC_CLEAR_UCHAR(l)		((void) (((__atomic_uchar_t) (l)) [0] = 0))
#define	ATOMIC_CLEAR_USHORT(l)		((void) (((__atomic_ushort_t) (l)) [0] = 0))

#define	ATOMIC_STORE_UCHAR(l,v)		((void) (((__atomic_uchar_t) (l)) [0] = (v)))

#if	defined (__LARGE__) || defined (__HUGE__) || defined (__COMPACT__)

/*
 * The ATOMIC_FETCH_AND_STORE_PTR () operation cannot be performed in-line in
 * BC++ 3.1 in large data models due to code-generation bugs that appear to
 * be caused by a mix of problems casting 32-bit registers to pointers and
 * other problems dealing with far-pointer-valued returns from in-line
 * assembly.
 */

#define	ATOMIC_STORE_PTR(l,v)		(__lockaddr (l), _EAX = (long) (v),\
					 __emit__ (__XCHG_EAX_ESBX__))

#define	ATOMIC_FETCH_PTR(l)		((void *) (_EAX = ((__atomic_ulong_t) (l)) [0]))

#define	ATOMIC_CLEAR_PTR(l)		ATOMIC_STORE_PTR (l, 0)

#else	/* use simpler small-data version */

#define	ATOMIC_FETCH_AND_STORE_PTR(l,v)	(__lockaddr (l), _AX = (unsigned) (v),\
					 __emit__ (__XCHG_AX_ESBX__), \
					 (void *) _AX)

#define	ATOMIC_STORE_PTR(l,v)		(__lockaddr (l), _AX = (unsigned) (v),\
					 __emit__ (__XCHG_AX_ESBX__))

#define	ATOMIC_FETCH_PTR(l)		(((__atomic_ptr_t) (l)) [0])

#define	ATOMIC_CLEAR_PTR(l)		ATOMIC_STORE_PTR (l, 0)

#endif	/* small data model */


#elif	__COHERENT__


#define	ATOMIC_FETCH_CHAR(l)		(((__atomic_char_t) (l)) [0])
#define	ATOMIC_FETCH_UCHAR(l)		(((__atomic_uchar_t) (l)) [0])
#define	ATOMIC_FETCH_SHORT(l)		(((__atomic_short_t) (l)) [0])
#define	ATOMIC_FETCH_USHORT(l)		(((__atomic_ushort_t) (l)) [0])
#define	ATOMIC_FETCH_INT(l)		(((__atomic_int_t) (l)) [0])
#define	ATOMIC_FETCH_UINT(l)		(((__atomic_uint_t) (l)) [0])
#define	ATOMIC_FETCH_LONG(l)		(((__atomic_long_t) (l)) [0])
#define	ATOMIC_FETCH_ULONG(l)		(((__atomic_ulong_t) (l)) [0])
#define	ATOMIC_FETCH_PTR(l)		(((__atomic_ptr_t) (l)) [0])

#define	ATOMIC_TEST_AND_SET_UCHAR(l)	ATOMIC_FETCH_AND_STORE_UCHAR (l, (__uchar_t) -1)

#define	ATOMIC_CLEAR_UCHAR(l)		((void) (((__atomic_uchar_t) (l)) [0] = 0))
#define	ATOMIC_CLEAR_USHORT(l)		((void) (((__atomic_ushort_t) (l)) [0] = 0))
#define	ATOMIC_CLEAR_PTR(l)		((void) (((__atomic_ptr_t) (l)) [0] = 0))

#define	ATOMIC_STORE_UCHAR(l,v)		((void) (((__atomic_uchar_t) (l)) [0] = (v)))
#define	ATOMIC_STORE_PTR(l,v)		((void) (((__atomic_ptr_t) (l)) [0] = (v)))

#endif	/* __COHERENT__ */


#ifdef	_CHECK_LOCK_TYPES

#undef	ATOMIC_FETCH_AND_STORE_CHAR
#undef	ATOMIC_FETCH_AND_STORE_UCHAR
#undef	ATOMIC_FETCH_AND_STORE_SHORT
#undef	ATOMIC_FETCH_AND_STORE_USHORT
#undef	ATOMIC_FETCH_AND_STORE_INT
#undef	ATOMIC_FETCH_AND_STORE_UINT
#undef	ATOMIC_FETCH_AND_STORE_LONG
#undef	ATOMIC_FETCH_AND_STORE_ULONG
#undef	ATOMIC_FETCH_AND_STORE_PTR
#undef	ATOMIC_FETCH_CHAR
#undef	ATOMIC_FETCH_UCHAR
#undef	ATOMIC_FETCH_SHORT
#undef	ATOMIC_FETCH_USHORT
#undef	ATOMIC_FETCH_INT
#undef	ATOMIC_FETCH_UINT
#undef	ATOMIC_FETCH_LONG
#undef	ATOMIC_FETCH_ULONG
#undef	ATOMIC_FETCH_PTR

#undef	ATOMIC_CLEAR_UCHAR
#undef	ATOMIC_CLEAR_USHORT
#undef	ATOMIC_CLEAR_PTR

#undef	ATOMIC_STORE_UCHAR
#undef	ATOMIC_STORE_PTR

#endif	/* defined (_CHECK_LOCK_TYPES) */


__EXTERN_C_BEGIN__

__pl_t	TEST_AND_SET_LOCK	__PROTO ((__atomic_uchar_t _locked,
					  __pl_t _pl,
					  __CONST__ char * _name));

__EXTERN_C_END__


#endif	/* ! defined (__KERNEL_X86LOCK_H__) */