2015-06-19 13:56:08 -04:00
|
|
|
/* atomic.S - nanokernel atomic operators for IA-32 */
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2011-2014 Wind River Systems, Inc.
|
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2015-04-10 16:44:37 -07:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2015-04-10 16:44:37 -07:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2015-04-10 16:44:37 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
DESCRIPTION
|
2015-07-27 12:19:36 -04:00
|
|
|
This module provides the atomic operators for IA-32 architectures on platforms
|
2015-04-10 16:44:37 -07:00
|
|
|
that support the LOCK prefix instruction.
|
|
|
|
|
|
|
|
The atomic operations are guaranteed to be atomic with respect to interrupt
|
|
|
|
service routines, and to operations performed by peer processors.
|
|
|
|
|
|
|
|
INTERNAL
|
|
|
|
These operators are currently unavailable to user space applications,
|
|
|
|
as there is no requirement for this capability.
|
2015-07-01 17:22:39 -04:00
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
/* includes */
|
|
|
|
|
|
|
|
#if !defined(CONFIG_LOCK_INSTRUCTION_UNSUPPORTED)
|
|
|
|
|
|
|
|
#define _ASMLANGUAGE
|
|
|
|
|
2015-05-28 10:56:47 -07:00
|
|
|
#include <arch/x86/asm.h>
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
/* exports (public APIs) */
|
|
|
|
|
|
|
|
GTEXT(atomic_cas)
|
|
|
|
GTEXT(atomic_add)
|
|
|
|
GTEXT(atomic_sub)
|
|
|
|
GTEXT(atomic_inc)
|
|
|
|
GTEXT(atomic_dec)
|
|
|
|
GTEXT(atomic_get)
|
|
|
|
GTEXT(atomic_set)
|
|
|
|
GTEXT(atomic_clear)
|
|
|
|
GTEXT(atomic_or)
|
|
|
|
GTEXT(atomic_xor)
|
|
|
|
GTEXT(atomic_and)
|
|
|
|
GTEXT(atomic_nand)
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic compare-and-set primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the compare-and-set operator. If the original value at
|
|
|
|
* <target> equals <oldValue>, then <newValue> is stored at <target> and the
|
|
|
|
* function returns 1.
|
|
|
|
*
|
|
|
|
* If the original value at <target> does not equal <oldValue>, then the store
|
|
|
|
* is not done and the function returns 0.
|
|
|
|
*
|
|
|
|
* The reading of the original value at <target>, the comparison,
|
|
|
|
* and the write of the new value (if it occurs) all happen atomically with
|
|
|
|
* respect to both interrupts and accesses of other processors to <target>.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return Returns 1 if <newValue> is written, 0 otherwise.
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* int atomic_cas
|
|
|
|
* (
|
|
|
|
* atomic_t * target, /@ address to be tested @/
|
|
|
|
* atomic_val_t oldValue, /@ value to compare against @/
|
|
|
|
* atomic_val_t newValue /@ value to compare against @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'cmpxchg' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_cas)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl SP_ARG2(%esp),%eax /* get oldValue to cmp */
|
|
|
|
movl SP_ARG3(%esp),%ecx /* get newValue to set */
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
cmpxchg %ecx,(%edx) /* if (%eax == (%edx) */
|
|
|
|
/* {ZF = 1; (%edx) = %ecx;} */
|
|
|
|
/* else */
|
|
|
|
/* {ZF = 0; %eax = (%edx);} */
|
|
|
|
jne atomic_cas1
|
|
|
|
movl $1,%eax /* set return status to 1 */
|
|
|
|
ret
|
|
|
|
|
|
|
|
BRANCH_LABEL(atomic_cas1)
|
|
|
|
xorl %eax,%eax /* set return status to 0 */
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic add primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic addition operator. The <value> is
|
|
|
|
* atomically added to the value at <target>, placing the result at <target>,
|
|
|
|
* and the old value from <target> is returned.
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
|
|
|
* atomic_val_t atomic_add
|
2015-07-01 17:22:39 -04:00
|
|
|
* (
|
|
|
|
* atomic_t * target, /@ memory location to add to @/
|
|
|
|
* atomic_val_t value /@ value to add @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'xadd' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_add)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl SP_ARG2(%esp),%eax /* get value to add */
|
|
|
|
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
xadd %eax,(%edx) /* TMP = %eax + (%edx) */
|
|
|
|
/* %eax = (%edx) */
|
|
|
|
/* (%edx) = TMP */
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic subtraction primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic subtraction operator. The <value> is
|
|
|
|
* atomically subtracted from the value at <target>, placing the result at
|
|
|
|
* <target>, and the old value from <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_sub
|
|
|
|
* (
|
|
|
|
* atomic_t * target, /@ memory location to subtract from @/
|
|
|
|
* atomic_val_t value /@ value to subtract @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'xadd' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_sub)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl SP_ARG2(%esp),%eax /* get value to add */
|
|
|
|
negl %eax /* Negate %eax... */
|
|
|
|
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
xadd %eax,(%edx) /* TMP = %eax + (%edx) */
|
|
|
|
/* %eax = (%edx) */
|
|
|
|
/* (%edx) = TMP */
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic increment primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic increment operator. The value at <target>
|
|
|
|
* is atomically incremented by 1, and the old value from <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The value from <target> before the increment
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_inc
|
|
|
|
* (
|
|
|
|
* atomic_t *target /@ memory location to increment @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'xadd' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_inc)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
|
|
|
|
xorl %eax, %eax
|
|
|
|
incl %eax /* increment by 1 */
|
|
|
|
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
xadd %eax,(%edx) /* TMP = %eax + (%edx) */
|
|
|
|
/* %eax = (%edx) */
|
|
|
|
/* (%edx) = TMP */
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic decrement primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic decrement operator. The value at <target>
|
|
|
|
* is atomically decremented by 1, and the old value from <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The value from <target> prior to the decrement
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_dec
|
|
|
|
* (
|
|
|
|
* atomic_t *target /@ memory location to decrement @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'xadd' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_dec)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
orl $-1,%eax /* dec by 1 */
|
|
|
|
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
xadd %eax,(%edx) /* TMP = %eax + (%edx) */
|
|
|
|
/* %eax = (%edx) */
|
|
|
|
/* (%edx) = TMP */
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic get primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic get primitive to atomically read
|
|
|
|
* a value from <target>. It simply does an ordinary load. Note that <target>
|
|
|
|
* is expected to be aligned to a 4-byte boundary.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The value read from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_t atomic_get
|
|
|
|
* (
|
|
|
|
* atomic_t *target /@ memory location to read from @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_get)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl (%edx), %eax /* get value */
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic get-and-set primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic set operator. The <value> is atomically
|
|
|
|
* written at <target> and the previous value at <target> is returned.
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_set
|
|
|
|
* (
|
|
|
|
* atomic_t *target, /@ memory location to write to @/
|
|
|
|
* atomic_val_t value /@ value to set @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The XCHG instruction is executed on the specified address to
|
|
|
|
* swap in value. The value swapped out is returned by this function.
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_set)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl SP_ARG2(%esp),%eax /* get value to set */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The 'lock' prefix is not required with the 'xchg' instruction.
|
2015-08-22 18:41:06 -04:00
|
|
|
* According to the IA-32 instruction reference manual:
|
2015-04-10 16:44:37 -07:00
|
|
|
*
|
|
|
|
* "If a memory operand is referenced, the processor's locking
|
|
|
|
* protocol is automatically implemented for the duration of
|
|
|
|
* the exchange operation, regardless of the presence
|
|
|
|
* or absence of the LOCK prefix or of the value of the IOPL."
|
|
|
|
*/
|
|
|
|
|
|
|
|
xchg %eax,(%edx) /* set value with bus-lock */
|
|
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic clear primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic clear operator. The value of 0 is atomically
|
|
|
|
* written at <target> and the previous value at <target> is returned. (Hence,
|
|
|
|
* atomic_clear(pAtomicVar) is equivalent to atomic_set(pAtomicVar, 0).)
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_clear
|
|
|
|
* (
|
|
|
|
* atomic_t *target /@ memory location to write to @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_clear)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
xorl %eax,%eax /* clear value to set */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The 'lock' prefix is not required with the 'xchg' instruction.
|
2015-08-22 18:41:06 -04:00
|
|
|
* According to the IA-32 instruction reference manual:
|
2015-04-10 16:44:37 -07:00
|
|
|
*
|
|
|
|
* "If a memory operand is referenced, the processor's locking
|
|
|
|
* protocol is automatically implemented for the duration of
|
|
|
|
* the exchange operation, regardless of the presence
|
|
|
|
* or absence of the LOCK prefix or of the value of the IOPL."
|
|
|
|
*/
|
|
|
|
|
|
|
|
xchg %eax,(%edx) /* swap 'clear' value with bus-lock */
|
|
|
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic bitwise inclusive OR primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic bitwise inclusive OR operator. The <value>
|
|
|
|
* is atomically bitwise OR'ed with the value at <target>, placing the result
|
|
|
|
* at <target>, and the previous value at <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_or
|
|
|
|
* (
|
|
|
|
* atomic_t *target, /@ memory location to be modified @/
|
|
|
|
* atomic_val_t value /@ value to OR @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'cmpxchg' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_or)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl (%edx),%eax /* get old value */
|
|
|
|
|
|
|
|
BRANCH_LABEL(atomic_or_retry)
|
|
|
|
|
|
|
|
movl SP_ARG2(%esp),%ecx /* get value to OR */
|
|
|
|
orl %eax, %ecx
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
cmpxchg %ecx,(%edx) /* if (%eax == (%edx)) */
|
|
|
|
/* {ZF = 1 ; (%edx) = %ecx;} */
|
|
|
|
/* else */
|
|
|
|
/* {ZF = 0 ; %eax = (%edx);} */
|
|
|
|
jnz atomic_or_retry
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic bitwise exclusive OR (XOR) primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic bitwise exclusive OR operator. The <value>
|
|
|
|
* is atomically bitwise XOR'ed with the value at <target>, placing the result
|
|
|
|
* at <target>, and the previous value at <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_xor
|
|
|
|
* (
|
|
|
|
* atomic_t *target, /@ memory location to be modified @/
|
|
|
|
* atomic_t value /@ value to XOR @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'cmpxchg' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_xor)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl (%edx),%eax /* get old value */
|
|
|
|
|
|
|
|
BRANCH_LABEL(atomic_xor_retry)
|
|
|
|
|
|
|
|
movl SP_ARG2(%esp),%ecx /* get value to set */
|
|
|
|
xorl %eax, %ecx
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
cmpxchg %ecx,(%edx) /* if (%eax == (%edx)) */
|
|
|
|
/* {ZF = 1 ; (%edx) = %ecx;} */
|
|
|
|
/* else */
|
|
|
|
/* {ZF = 0 ; %eax = (%edx);} */
|
|
|
|
jnz atomic_xor_retry
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic bitwise AND primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic bitwise AND operator. The <value> is
|
|
|
|
* atomically bitwise AND'ed with the value at <target>, placing the result
|
|
|
|
* at <target>, and the previous value at <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-08-22 18:41:06 -04:00
|
|
|
*
|
2015-07-01 17:22:39 -04:00
|
|
|
* atomic_val_t atomic_and
|
|
|
|
* (
|
|
|
|
* atomic_t *target, /@ memory location to be modified @/
|
|
|
|
* atomic_val_t value /@ value to AND @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'cmpxchg' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_and)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl (%edx),%eax /* get old value */
|
|
|
|
|
|
|
|
BRANCH_LABEL(atomic_and_retry)
|
|
|
|
|
|
|
|
movl SP_ARG2(%esp),%ecx /* get value to set */
|
|
|
|
andl %eax, %ecx
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
cmpxchg %ecx,(%edx) /* if (%eax == (%edx)) */
|
|
|
|
/* {ZF = 1 ; (%edx) = %ecx;} */
|
|
|
|
/* else */
|
|
|
|
/* {ZF = 0 ; %eax = (%edx);} */
|
|
|
|
jnz atomic_and_retry
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
2015-07-01 17:22:39 -04:00
|
|
|
/**
|
|
|
|
*
|
2015-07-01 17:51:40 -04:00
|
|
|
* @brief Atomic bitwise NAND primitive
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* This routine provides the atomic bitwise NAND operator. The <value> is
|
|
|
|
* atomically bitwise NAND'ed with the value at <target>, placing the result
|
|
|
|
* at <target>, and the previous value at <target> is returned.
|
|
|
|
*
|
2015-07-01 17:29:04 -04:00
|
|
|
* @return The previous value from <target>
|
2015-07-01 17:22:39 -04:00
|
|
|
*
|
|
|
|
* atomic_val_t atomic_nand
|
|
|
|
* (
|
|
|
|
* atomic_t * target, /@ memory location to be modified @/
|
|
|
|
* atomic_val_t value /@ value to NAND @/
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* INTERNAL
|
|
|
|
* The 'cmpxchg' instruction is NOT supported on processor prior to the 80486
|
|
|
|
*/
|
2015-04-10 16:44:37 -07:00
|
|
|
|
|
|
|
SECTION_FUNC(TEXT, atomic_nand)
|
|
|
|
|
|
|
|
movl SP_ARG1(%esp),%edx /* get address */
|
|
|
|
movl (%edx),%eax /* get old value */
|
|
|
|
|
|
|
|
BRANCH_LABEL(atomic_nand_retry)
|
|
|
|
|
|
|
|
movl SP_ARG2(%esp),%ecx /* get value to nand with old value */
|
|
|
|
|
|
|
|
andl %eax,%ecx
|
|
|
|
not %ecx
|
|
|
|
|
|
|
|
lock /* lock the bus during the next inst */
|
|
|
|
cmpxchg %ecx,(%edx) /* if (%eax == (%edx)) */
|
|
|
|
/* {ZF = 1 ; (%edx) = %ecx;} */
|
|
|
|
/* else */
|
|
|
|
/* {ZF = 0 ; %eax = (%edx);} */
|
|
|
|
jnz atomic_nand_retry
|
|
|
|
ret
|
|
|
|
|
|
|
|
#endif /* !CONFIG_LOCK_INSTRUCTION_UNSUPPORTED */
|
|
|
|
|