root/src/system/kernel/arch/arm/arch_atomic32.cpp
/*
 * Copyright 2013 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Ithamar R. Adema, ithamar@upgrade-android.com
 */

#include <KernelExport.h>

#include <kernel.h>
#include <user_atomic.h>

#include <util/AutoLock.h>

#ifdef ATOMIC_FUNCS_ARE_SYSCALLS

/*
 * NOTE: These functions are _intentionally_ not using spinlocks, unlike
 * the 64 bit versions. The reason for this is that they are used by the
 * spinlock code itself, and therefore would deadlock.
 *
 * Since these are only really needed for ARMv5, which is not SMP anyway,
 * this is an acceptable compromise.
 */

void
atomic_set(int32 *value, int32 newValue)
{
        InterruptsLocker locker;
        *value = newValue;
}

int32
atomic_get_and_set(int32 *value, int32 newValue)
{
        InterruptsLocker locker;
        int32 oldValue = *value;
        atomic_set(value, newValue);
        return oldValue;
}

int32
atomic_test_and_set(int32 *value, int32 newValue, int32 testAgainst)
{
        InterruptsLocker locker;

        int32 oldValue = *value;
        if (oldValue == testAgainst)
                *value = newValue;
        return oldValue;
}

int32
atomic_add(int32 *value, int32 addValue)
{
        InterruptsLocker locker;

        int32 oldValue = *value;
        *value += addValue;
        return oldValue;
}

int32
atomic_and(int32 *value, int32 andValue)
{
        InterruptsLocker locker;

        int32 oldValue = *value;
        *value &= andValue;
        return oldValue;
}

int32
atomic_or(int32 *value, int32 orValue)
{
        InterruptsLocker locker;

        int32 oldValue = *value;
        *value |= orValue;
        return oldValue;
}

int32
atomic_get(int32 *value)
{
        InterruptsLocker locker;

        int32 oldValue = *value;
        return oldValue;
}

void
_user_atomic_set(int32 *value, int32 newValue)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                atomic_set(value, newValue);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return;
        }

access_violation:
        // XXX kill application
        return;
}

int32
_user_atomic_get_and_set(int32 *value, int32 newValue)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                int32 oldValue = atomic_get_and_set(value, newValue);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return oldValue;
        }

access_violation:
        // XXX kill application
        return -1;
}

int32
_user_atomic_test_and_set(int32 *value, int32 newValue, int32 testAgainst)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                int32 oldValue = atomic_test_and_set((int32*)value, newValue, testAgainst);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return oldValue;
        }

access_violation:
        // XXX kill application
        return -1;
}

int32
_user_atomic_add(int32 *value, int32 addValue)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                int32 oldValue = atomic_add(value, addValue);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return oldValue;
        }

access_violation:
        // XXX kill application
        return -1;
}

int32
_user_atomic_and(int32 *value, int32 andValue)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                int32 oldValue = atomic_and(value, andValue);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return oldValue;
        }

access_violation:
        // XXX kill application
        return -1;
}

int32
_user_atomic_or(int32 *value, int32 orValue)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                int32 oldValue = atomic_or(value, orValue);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return oldValue;
        }

access_violation:
        // XXX kill application
        return -1;
}

int32
_user_atomic_get(int32 *value)
{
        if (IS_USER_ADDRESS(value)
                && lock_memory((void *)value, sizeof(int32), B_READ_DEVICE) == B_OK) {
                int32 oldValue = atomic_get(value);
                unlock_memory((void *)value, sizeof(int32), B_READ_DEVICE);
                return oldValue;
        }

access_violation:
        // XXX kill application
        return -1;
}

#endif /* ATOMIC_FUNCS_ARE_SYSCALLS */