Commit fa5ad94a authored by Luc Verhaegen's avatar Luc Verhaegen
Browse files

import ump r2p4 source


Signed-off-by: default avatarLuc Verhaegen <libv@skynet.be>
parent cb781d1c
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump.h
*
* This file contains the user space part of the UMP API.
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_H_
#define _UNIFIED_MEMORY_PROVIDER_H_
/** @defgroup ump_user_space_api UMP User Space API
* @{ */
#include "ump_platform.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* External representation of a UMP handle in user space.
*/
typedef void * ump_handle;
/**
* Typedef for a secure ID, a system wide identificator for UMP memory buffers.
*/
typedef unsigned int ump_secure_id;
/**
* Value to indicate an invalid UMP memory handle.
*/
#define UMP_INVALID_MEMORY_HANDLE ((ump_handle)0)
/**
* Value to indicate an invalid secure Id.
*/
#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1)
/**
* UMP error codes for user space.
*/
typedef enum
{
UMP_OK = 0, /**< indicates success */
UMP_ERROR, /**< indicates failure */
} ump_result;
/**
* Opens and initializes the UMP library.
*
* This function must be called at least once before calling any other UMP API functions.
* Each open is reference counted and must be matched with a call to @ref ump_close "ump_close".
*
* @see ump_close
*
* @return UMP_OK indicates success, UMP_ERROR indicates failure.
*/
UMP_API_EXPORT ump_result ump_open(void);
/**
* Terminate the UMP library.
*
* This must be called once for every successful @ref ump_open "ump_open". The UMP library is
* terminated when, and only when, the last open reference to the UMP interface is closed.
*
* @see ump_open
*/
UMP_API_EXPORT void ump_close(void);
/**
* Retrieves the secure ID for the specified UMP memory.
*
* This identificator is unique across the entire system, and uniquely identifies
* the specified UMP memory. This identificator can later be used through the
* @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" or
* @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id"
* functions in order to access this UMP memory, for instance from another process.
*
* @note There is a kernel space equivalent function called @ref ump_dd_secure_id_get "ump_dd_secure_id_get"
*
* @see ump_handle_create_from_secure_id
* @see ump_dd_handle_create_from_secure_id
* @see ump_dd_secure_id_get
*
* @param mem Handle to UMP memory.
*
* @return Returns the secure ID for the specified UMP memory.
*/
UMP_API_EXPORT ump_secure_id ump_secure_id_get(ump_handle mem);
/**
* Retrieves a handle to allocated UMP memory.
*
* The usage of UMP memory is reference counted, so this will increment the reference
* count by one for the specified UMP memory.
* Use @ref ump_reference_release "ump_reference_release" when there is no longer any
* use for the retrieved handle.
*
* @note There is a kernel space equivalent function called @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id"
*
* @see ump_reference_release
* @see ump_dd_handle_create_from_secure_id
*
* @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function.
*
* @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned.
*/
UMP_API_EXPORT ump_handle ump_handle_create_from_secure_id(ump_secure_id secure_id);
/**
* Retrieves the actual size of the specified UMP memory.
*
* The size is reported in bytes, and is typically page aligned.
*
* @note There is a kernel space equivalent function called @ref ump_dd_size_get "ump_dd_size_get"
*
* @see ump_dd_size_get
*
* @param mem Handle to UMP memory.
*
* @return Returns the allocated size of the specified UMP memory, in bytes.
*/
UMP_API_EXPORT unsigned long ump_size_get(ump_handle mem);
/**
* Read from specified UMP memory.
*
* Another way of reading from (and writing to) UMP memory is to use the
* @ref ump_mapped_pointer_get "ump_mapped_pointer_get" to retrieve
* a CPU mapped pointer to the memory.
*
* @see ump_mapped_pointer_get
*
* @param dst Destination buffer.
* @param src Handle to UMP memory to read from.
* @param offset Where to start reading, given in bytes.
* @param length How much to read, given in bytes.
*/
UMP_API_EXPORT void ump_read(void * dst, ump_handle src, unsigned long offset, unsigned long length);
/**
* Write to specified UMP memory.
*
* Another way of writing to (and reading from) UMP memory is to use the
* @ref ump_mapped_pointer_get "ump_mapped_pointer_get" to retrieve
* a CPU mapped pointer to the memory.
*
* @see ump_mapped_pointer_get
*
* @param dst Handle to UMP memory to write to.
* @param offset Where to start writing, given in bytes.
* @param src Buffer to read from.
* @param length How much to write, given in bytes.
*/
UMP_API_EXPORT void ump_write(ump_handle dst, unsigned long offset, const void * src, unsigned long length);
/**
* Retrieves a memory mapped pointer to the specified UMP memory.
*
* This function retrieves a memory mapped pointer to the specified UMP memory,
* that can be used by the CPU. Every successful call to
* @ref ump_mapped_pointer_get "ump_mapped_pointer_get" is reference counted,
* and must therefor be followed by a call to
* @ref ump_mapped_pointer_release "ump_mapped_pointer_release " when the
* memory mapping is no longer needed.
*
* @note Systems without a MMU for the CPU only return the physical address, because no mapping is required.
*
* @see ump_mapped_pointer_release
*
* @param mem Handle to UMP memory.
*
* @return NULL indicates failure, otherwise a CPU mapped pointer is returned.
*/
UMP_API_EXPORT void * ump_mapped_pointer_get(ump_handle mem);
/**
* Releases a previously mapped pointer to the specified UMP memory.
*
* The CPU mapping of the specified UMP memory memory is reference counted,
* so every call to @ref ump_mapped_pointer_get "ump_mapped_pointer_get" must
* be matched with a call to this function when the mapping is no longer needed.
*
* The CPU mapping is not removed before all references to the mapping is released.
*
* @note Systems without a MMU must still implement this function, even though no unmapping should be needed.
*
* @param mem Handle to UMP memory.
*/
UMP_API_EXPORT void ump_mapped_pointer_release(ump_handle mem);
/**
* Adds an extra reference to the specified UMP memory.
*
* This function adds an extra reference to the specified UMP memory. This function should
* be used every time a UMP memory handle is duplicated, that is, assigned to another ump_handle
* variable. The function @ref ump_reference_release "ump_reference_release" must then be used
* to release each copy of the UMP memory handle.
*
* @note You are not required to call @ref ump_reference_add "ump_reference_add"
* for UMP handles returned from
* @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id",
* because these handles are already reference counted by this function.
*
* @note There is a kernel space equivalent function called @ref ump_dd_reference_add "ump_dd_reference_add"
*
* @see ump_dd_reference_add
*
* @param mem Handle to UMP memory.
*/
UMP_API_EXPORT void ump_reference_add(ump_handle mem);
/**
* Releases a reference from the specified UMP memory.
*
* This function should be called once for every reference to the UMP memory handle.
* When the last reference is released, all resources associated with this UMP memory
* handle are freed.
*
* @note There is a kernel space equivalent function called @ref ump_dd_reference_release "ump_dd_reference_release"
*
* @see ump_dd_reference_release
*
* @param mem Handle to UMP memory.
*/
UMP_API_EXPORT void ump_reference_release(ump_handle mem);
#ifdef __cplusplus
}
#endif
/** @} */ /* end group ump_user_space_api */
#endif /*_UNIFIED_MEMORY_PROVIDER_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_arch.c
*
* UMP arch layer for UMP-UDD
*/
#include <ump/ump.h>
#include "ump_arch.h"
#include <ump/ump_debug.h>
#include <ump/ump_uk_types.h>
#include "ump_uku.h"
/** Pointer to an OS-Specific context that we should pass in _uku_ calls */
void *ump_uk_ctx = NULL;
/** Reference counting of ump_arch_open() and ump_arch_close(). */
static volatile int ump_ref_count = 0;
/** Lock for critical section in open/close */
_ump_osu_lock_t * ump_lock_arch = NULL;
ump_result ump_arch_open(void)
{
ump_result retval = UMP_OK;
_ump_osu_lock_auto_init( &ump_lock_arch, 0, 0, 0 );
/* Check that the lock was initialized */
if (NULL == ump_lock_arch)
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_open() failed to init lock\n"));
return UMP_ERROR;
}
/* Attempt to obtain a lock */
if( _UMP_OSU_ERR_OK != _ump_osu_lock_wait( ump_lock_arch, _UMP_OSU_LOCKMODE_RW ) )
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_open() failed to acquire lock\n"));
return UMP_ERROR;
}
/* ASSERT NEEDED */
UMP_DEBUG_ASSERT(0 <= ump_ref_count, ("UMP: Reference count invalid at _ump_base_arch_open()"));
ump_ref_count++;
if (1 == ump_ref_count)
{
/* We are the first, open the UMP device driver */
if (_UMP_OSU_ERR_OK != _ump_uku_open( &ump_uk_ctx ))
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_open() failed to open UMP device driver\n"));
retval = UMP_ERROR;
ump_ref_count--;
}
}
/* Signal the lock so someone else can use it */
_ump_osu_lock_signal( ump_lock_arch, _UMP_OSU_LOCKMODE_RW );
return retval;
}
void ump_arch_close(void)
{
_ump_osu_lock_auto_init( &ump_lock_arch, 0, 0, 0 );
/* Check that the lock was initialized */
if(NULL == ump_lock_arch)
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_close() failed to init lock\n"));
return;
}
/* Attempt to obtain a lock */
if( _UMP_OSU_ERR_OK != _ump_osu_lock_wait( ump_lock_arch, _UMP_OSU_LOCKMODE_RW ) )
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_close() failed to acquire lock\n"));
return;
}
UMP_DEBUG_ASSERT(0 < ump_ref_count, ("UMP: ump_arch_close() called while no references exist"));
if (ump_ref_count > 0)
{
ump_ref_count--;
if (0 == ump_ref_count)
{
_ump_osu_errcode_t retval = _ump_uku_close(&ump_uk_ctx);
UMP_DEBUG_ASSERT(retval == _UMP_OSU_ERR_OK, ("UMP: Failed to close UMP interface"));
UMP_IGNORE(retval);
ump_uk_ctx = NULL;
_ump_osu_lock_signal( ump_lock_arch, _UMP_OSU_LOCKMODE_RW );
_ump_osu_lock_term( ump_lock_arch ); /* Not 100% thread safe, since another thread can already be waiting for this lock in ump_arch_open() */
ump_lock_arch = NULL;
return;
}
}
/* Signal the lock so someone else can use it */
_ump_osu_lock_signal( ump_lock_arch, _UMP_OSU_LOCKMODE_RW );
}
ump_secure_id ump_arch_allocate(unsigned long * size, ump_alloc_constraints constraints)
{
_ump_uk_allocate_s call_arg;
if ( NULL == size )
{
return UMP_INVALID_SECURE_ID;
}
call_arg.ctx = ump_uk_ctx;
call_arg.secure_id = UMP_INVALID_SECURE_ID;
call_arg.size = *size;
#ifdef UMP_DEBUG_SKIP_CODE
/** Run-time ASSERTing that _ump_uk_api_version_s and ump_alloc_constraints are
* interchangable */
switch (constraints)
{
case UMP_REF_DRV_CONSTRAINT_NONE:
UMP_DEBUG_ASSERT( UMP_REF_DRV_UK_CONSTRAINT_NONE == constraints, ("ump_uk_alloc_constraints out of sync with ump_alloc_constraints") );
break;
case UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR:
UMP_DEBUG_ASSERT( UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR == constraints, ("ump_uk_alloc_constraints out of sync with ump_alloc_constraints") );
break;
default:
UMP_DEBUG_ASSERT( 1, ("ump_uk_alloc_constraints out of sync with ump_alloc_constraints: %d unrecognized", constraints) );
break;
}
#endif
call_arg.constraints = (ump_uk_alloc_constraints)constraints;
if ( _UMP_OSU_ERR_OK != _ump_uku_allocate(&call_arg) )
{
return UMP_INVALID_SECURE_ID;
}
*size = call_arg.size;
UMP_DEBUG_PRINT(4, ("UMP: Allocated ID %u, size %ul", call_arg.secure_id, call_arg.size));
return call_arg.secure_id;
}
unsigned long ump_arch_size_get(ump_secure_id secure_id)
{
_ump_uk_size_get_s dd_size_call_arg;
dd_size_call_arg.ctx = ump_uk_ctx;
dd_size_call_arg.secure_id = secure_id;
dd_size_call_arg.size = 0;
if (_UMP_OSU_ERR_OK == _ump_uku_size_get( &dd_size_call_arg ) )
{
return dd_size_call_arg.size;
}
return 0;
}
void ump_arch_reference_release(ump_secure_id secure_id)
{
_ump_uk_release_s dd_release_call_arg;
_ump_osu_errcode_t retval;
dd_release_call_arg.ctx = ump_uk_ctx;
dd_release_call_arg.secure_id = secure_id;
UMP_DEBUG_PRINT(4, ("UMP: Releasing ID %u", secure_id));
retval = _ump_uku_release( &dd_release_call_arg );
UMP_DEBUG_ASSERT(retval == _UMP_OSU_ERR_OK, ("UMP: Failed to release reference to UMP memory"));
UMP_IGNORE(retval);
}
void* ump_arch_map(ump_secure_id secure_id, unsigned long size, ump_cache_enabled cache, unsigned long *cookie_out)
{
_ump_uk_map_mem_s dd_map_call_arg;
UMP_DEBUG_ASSERT_POINTER( cookie_out );
dd_map_call_arg.ctx = ump_uk_ctx;
dd_map_call_arg.secure_id = secure_id;
dd_map_call_arg.size = size;
dd_map_call_arg.is_cached = (u32) (UMP_CACHE_ENABLE==cache);
if ( -1 == _ump_uku_map_mem( &dd_map_call_arg ) )
{
UMP_DEBUG_PRINT(4, ("UMP: Mapping failed for ID %u", secure_id));
return NULL;
}
UMP_DEBUG_PRINT(4, ("Mapped %u at 0x%08lx", secure_id, (unsigned long)dd_map_call_arg.mapping));
*cookie_out = dd_map_call_arg.cookie;
return dd_map_call_arg.mapping;
}
void ump_arch_unmap(void* mapping, unsigned long size, unsigned long cookie)
{
_ump_uk_unmap_mem_s dd_unmap_call_arg;
dd_unmap_call_arg.ctx = ump_uk_ctx;
dd_unmap_call_arg.mapping = mapping;
dd_unmap_call_arg.size = size;
dd_unmap_call_arg.cookie = cookie;
UMP_DEBUG_PRINT(4, ("Unmapping 0x%08lx", (unsigned long)mapping));
_ump_uku_unmap_mem( &dd_unmap_call_arg );
}
/** Memory synchronization - cache flushing of mapped memory */
int ump_arch_msync(ump_secure_id secure_id, void* mapping, unsigned long cookie, void * address, unsigned long size, ump_cpu_msync_op op)
{
_ump_uk_msync_s dd_msync_call_arg;
dd_msync_call_arg.ctx = ump_uk_ctx;
dd_msync_call_arg.mapping = mapping;
dd_msync_call_arg.address = address;
dd_msync_call_arg.size = size;
dd_msync_call_arg.op = (ump_uk_msync_op)op;
dd_msync_call_arg.cookie = cookie;
dd_msync_call_arg.secure_id = secure_id;
dd_msync_call_arg.is_cached = 0;
UMP_DEBUG_PRINT(4, ("Msync 0x%08lx", (unsigned long)mapping));
_ump_uku_msynch( &dd_msync_call_arg );
if ( 0==dd_msync_call_arg.is_cached )
{
UMP_DEBUG_PRINT(4, ("Trying to flush uncached UMP mem ID: %d", secure_id));
}
return dd_msync_call_arg.is_cached;
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_arch.h
*
* Header file for the arch dependent backend, which will do the communication with the UMP device driver.
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_ARCH_H_
#define _UNIFIED_MEMORY_PROVIDER_ARCH_H_
#include <ump/ump.h>
#include <ump/ump_ref_drv.h>
#include "ump_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Open UMP interface. */
ump_result ump_arch_open(void);
/** Close UMP interface. */
void ump_arch_close(void);
/** Allocate UMP memory. */
ump_secure_id ump_arch_allocate(unsigned long * size, ump_alloc_constraints constraints);
/** Query size of specified UMP memory, in bytes. */
unsigned long ump_arch_size_get(ump_secure_id secure_id);
/** Release a reference from specified UMP memory. */
void ump_arch_reference_release(ump_secure_id secure_id);
/** Map specified UMP memory into CPU address space */
void* ump_arch_map(ump_secure_id secure_id, unsigned long size, ump_cache_enabled cache, unsigned long *cookie_out);
/** Unmap specified UMP memory from CPU adderss space */
void ump_arch_unmap(void* mapping, unsigned long size, unsigned long cookie);
/** Memory synchronization - cache flushing of mapped memory
* @return Is_cached: 1==True 0==NonCached */
int ump_arch_msync(ump_secure_id secure_id, void* mapping, unsigned long cookie, void * address, unsigned long size, ump_cpu_msync_op op);
#ifdef __cplusplus
}
#endif
#endif /* _UNIFIED_MEMORY_PROVIDER_ARCH_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_debug.h
*
* The file include several useful macros for debugging and printing.
* - UMP_PRINTF(...) Do not use this function: Will be included in Release builds.
* - UMP_DEBUG_TRACE() Prints current location in code.
* - UMP_DEBUG_PRINT(nr, (X) ) Prints the second argument if nr<=UMP_DEBUG_LEVEL.
* - UMP_DEBUG_TPRINT(nr, X ) Prints the source trace and second argument if nr<=UMP_DEBUG_LEVEL.
* - UMP_DEBUG_ERROR( (X) ) Prints an errortext, a source trace, and the given error message.
* - UMP_DEBUG_ASSERT(exp,(X)) If the asserted expr is false, the program will exit.
* - UMP_DEBUG_ASSERT_RANGE(x, min, max) Triggers if variable x is not between or equal to max and min.
* - UMP_DEBUG_ASSERT_LEQ(x, max) Triggers if variable x is not less than equal to max.
* - UMP_DEBUG_ASSERT_POINTER(pointer) Triggers if the pointer is a zero pointer.
* - UMP_DEBUG_CODE( X ) The code inside the macro is only copiled in Debug builds.
*
* The (X) means that you must add an extra parantese around the argumentlist.
*
* The printf function: UMP_PRINTF(...) is routed to _ump_sys_printf
*
* Suggested range for the DEBUG-LEVEL is [1:6] where
* [1:2] Is messages with highest priority, indicate possible errors.
* [3:4] Is messages with medium priority, output important variables.
* [5:6] Is messages with low priority, used during extensive debugging.
*
*/
#ifndef _UMP_DEBUG_H_
#define _UMP_DEBUG_H_
#include <stdio.h>
#include <stdlib.h>
/* START: Configuration */
#ifndef UMP_PRINTF
#define UMP_PRINTF printf
#endif /* UMP_PRINTF */
#ifndef UMP_PRINT_FLUSH
#define UMP_PRINT_FLUSH do {} while (0)
#endif /* UMP_PRINT_FLUSH */
#ifndef UMP_DEBUG_LEVEL
#define UMP_DEBUG_LEVEL 1
#endif /* UMP_DEBUG_LEVEL */
#ifndef UMP_DEBUG_ERROR_START_MSG
#define UMP_DEBUG_ERROR_START_MSG do {\
UMP_PRINTF("*********************************************************************\n");\
UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_ERROR_START_MSG */
#ifndef UMP_DEBUG_ERROR_STOP_MSG
#define UMP_DEBUG_ERROR_STOP_MSG do { UMP_PRINTF("\n"); UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_ERROR_STOP_MSG */
#ifndef UMP_ASSERT_QUIT_CMD
#define UMP_ASSERT_QUIT_CMD abort()
#endif /* UMP_ASSERT_QUIT_CMD */
/* STOP: Configuration */
/**
* The macro UMP_FUNCTION evaluates to the name of the function enclosing
* this macro's usage, or "<unknown>" if not supported.
*/
#if (defined(__SYMBIAN32__) && defined(__ARMCC__)) || defined(_MSC_VER)
# define UMP_FUNCTION __FUNCTION__
#elif __STDC__ && __STDC_VERSION__ >= 199901L
# define UMP_FUNCTION __FUNCTION__
#elif defined(__GNUC__) && __GNUC__ >= 2
# define UMP_FUNCTION __FUNCTION__
#elif defined(__func__)
# define UMP_FUNCTION __func__
#else
# define UMP_FUNCTION "<unknown>"
#endif
/**
* Explicitly ignore a parameter passed into a function, to suppress compiler warnings.
* Should only be used with parameter names.
*/
#define UMP_IGNORE(x) (void)x
/**
* @def UMP_DEBUG_TRACE()
* @brief Prints current location in code.
* Can be turned off by defining UMP_DEBUG_SKIP_TRACE
*/
#ifndef UMP_DEBUG_SKIP_TRACE
#ifndef UMP_DEBUG_SKIP_PRINT_FUNCTION_NAME
#define UMP_DEBUG_TRACE() do { UMP_PRINTF( "In file: "__FILE__ \
" function: %s() line:%4d\n" , UMP_FUNCTION, __LINE__); UMP_PRINT_FLUSH; } while (0)
#else
#define UMP_DEBUG_TRACE() do { UMP_PRINTF( "In file: "__FILE__ " line:%4d\n" , __LINE__); UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_SKIP_PRINT_FUNCTION_NAME */
#else
#define UMP_DEBUG_TRACE()
#endif /* UMP_DEBUG_SKIP_TRACE */
/**
* @def UMP_DEBUG_PRINT(nr, (X) )
* @brief Prints the second argument if nr<=UMP_DEBUG_LEVEL.
* Can be turned off by defining UMP_DEBUG_SKIP_PRINT
* @param nr If nr <= UMP_DEBUG_LEVEL, we print the text.
* @param X A parantese with the contents to be sent to UMP_PRINTF
*/
#ifndef UMP_DEBUG_SKIP_PRINT
#define UMP_DEBUG_PRINT(nr, X ) do { if ( nr<=UMP_DEBUG_LEVEL ) { UMP_PRINTF X ; UMP_PRINT_FLUSH; } } while (0)
#else
#define UMP_DEBUG_PRINT(nr, X )
#endif /* UMP_DEBUG_SKIP_PRINT */
/**
* @def UMP_DEBUG_TPRINT(nr, (X) )
* @brief Prints the second argument if nr<=UMP_DEBUG_LEVEL.
* Can be turned off by defining UMP_DEBUG_SKIP_TPRINT.
* Can be shortened by defining UMP_DEBUG_TPRINT_SKIP_FUNCTION.
* @param nr If nr <= UMP_DEBUG_LEVEL, we print the text.
* @param X A parantese with the contents to be sent to UMP_PRINTF
*/
/* helper to handle if the function name should be included or not */
#ifndef UMP_DEBUG_TPRINT_SKIP_FUNCTION
#define UMP_DEBUG_TPRINT_INTERN do {UMP_PRINTF( ""__FILE__" %s()%4d " , UMP_FUNCTION, __LINE__); UMP_PRINT_FLUSH; } while (0)
#else
#define UMP_DEBUG_TPRINT_INTERN do {UMP_PRINTF( ""__FILE__ "%4d " , __LINE__); UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_TPRINT_SKIP_FUNCTION */
#ifndef UMP_DEBUG_SKIP_TPRINT
#define UMP_DEBUG_TPRINT(nr, X ) \
do{\
if ( nr<=UMP_DEBUG_LEVEL )\
{\
UMP_DEBUG_TPRINT_INTERN;\
UMP_PRINTF X ;\
UMP_PRINT_FLUSH;\
}\
} while (0)
#else
#define UMP_DEBUG_TPRINT(nr, X )
#endif /* UMP_DEBUG_SKIP_TPRINT */
/**
* @def UMP_DEBUG_ERROR( (X) )
* @brief Prints an errortext, a source Trace, and the given error message.
* Prints filename, function, linenr, and the given error message.
* The error message must be inside a second parantese.
* The error message is written on a separate line, and a NL char is added.
* Can be turned of by defining UMP_DEBUG_SKIP_ERROR;
* You do not need to type the words ERROR in the message, since it will
* be added anyway.
*
* @note You should not end the text with a newline, since it is added by the macro.
* @note You should not write "ERROR" in the text, since it is added by the macro.
* @param X A parantese with the contents to be sent to UMP_PRINTF
*/
#ifndef UMP_DEBUG_SKIP_ERROR
#define UMP_DEBUG_ERROR( X ) \
do{ \
UMP_DEBUG_ERROR_START_MSG;\
UMP_PRINTF("ERROR: ");\
UMP_PRINT_FLUSH;\
UMP_DEBUG_TRACE(); \
UMP_PRINTF X ; \
UMP_PRINT_FLUSH;\
UMP_DEBUG_ERROR_STOP_MSG;\
} while (0)
#else
#define UMP_DEBUG_ERROR( X ) do{ ; } while ( 0 )
#endif /* UMP_DEBUG_SKIP_ERROR */
/**
* @def UMP_DEBUG_ASSERT(expr, (X) )
* @brief If the asserted expr is false, the program will exit.
* Prints filename, function, linenr, and the given error message.
* The error message must be inside a second parantese.
* The error message is written on a separate line, and a NL char is added.
* Can be turned of by defining UMP_DEBUG_SKIP_ERROR;
* You do not need to type the words ASSERT in the message, since it will
* be added anyway.
*
* @param X A parantese with the contents to be sent to UMP_PRINTF
* Prints filename, function, linenr, and the error message
* on a separte line. A newline char is added at the end.
* Can be turned of by defining UMP_DEBUG_SKIP_ASSERT
* @param expr Will exit program if \a expr is false;
* @param (X) Text that will be written if the assertion toggles.
*/
#ifndef UMP_DEBUG_SKIP_ASSERT
#define UMP_DEBUG_ASSERT(expr, X ) \
do{\
if ( !(expr) ) \
{ \
UMP_DEBUG_ERROR_START_MSG;\
UMP_PRINTF("ASSERT EXIT: ");\
UMP_PRINT_FLUSH;\
UMP_DEBUG_TRACE(); \
UMP_PRINTF X ; \
UMP_PRINT_FLUSH;\
UMP_DEBUG_ERROR_STOP_MSG;\
UMP_ASSERT_QUIT_CMD;\
}\
} while (0)
#else
#define UMP_DEBUG_ASSERT(expr, X)
#endif /* UMP_DEBUG_SKIP_ASSERT */
/**
* @def UMP_DEBUG_ASSERT_POINTER(pointer)
* @brief If the asserted pointer is NULL, the program terminates and TRACE info is printed
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_POINTER(pointer) UMP_DEBUG_ASSERT(pointer, ("Null pointer " #pointer) )
/**
* @def UMP_DEBUG_ASSERT_HANDLE(handle)
* @brief If the asserted handle is not a valid handle, the program terminates and TRACE info is printed
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_HANDLE(handle) UMP_DEBUG_ASSERT(UMP_NO_HANDLE != (handle), ("Invalid handle" #handle) )
/**
* @def UMP_DEBUG_ASSERT_ALIGNMENT(ptr, align)
* @brief If the asserted pointer is not aligned to align, the program terminates with trace info printed.
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#ifndef UMP_DEBUG_SKIP_ASSERT
#define UMP_DEBUG_ASSERT_ALIGNMENT(ptr, align) do { \
UMP_DEBUG_ASSERT(0 == (align & (align - 1)), ("align %d is not a power-of-two", align)); \
UMP_DEBUG_ASSERT(0 == (((u32)(ptr)) & (align - 1)), ("ptr %p not aligned to %d bytes", (void*)ptr, align)); \
} while (0)
#else
#define UMP_DEBUG_ASSERT_ALIGNMENT(ptr, align)
#endif /* UMP_DEBUG_SKIP_ASSERT */
/**
* @def UMP_DEBUG_ASSERT_RANGE(x,min,max)
* @brief If variable x is not between or equal to max and min, the assertion triggers.
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_RANGE(x, min, max) \
UMP_DEBUG_ASSERT( (x) >= (min) && (x) <= (max), \
(#x " out of range (%2.2f)", (double)x ) \
)
/**
* @def UMP_DEBUG_ASSERT_LEQ(x,max)
* @brief If variable x is less than or equal to max, the assertion triggers.
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_LEQ(x, max) \
UMP_DEBUG_ASSERT( (x) <= (max), \
(#x " out of range (%2.2f)", (double)x ) \
)
/**
* @def UMP_DEBUG_CODE( X )
* @brief Run the code X on debug builds.
* The code will not be used if UMP_DEBUG_SKIP_CODE is defined .
*
*/
#ifdef UMP_DEBUG_SKIP_CODE
#define UMP_DEBUG_CODE( X )
#else
#define UMP_DEBUG_CODE( X ) X
#endif /* UMP_DEBUG_SKIP_CODE */
#endif /* _UMP_DEBUG_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_frontend.c
*
* This file implements the user space API of the UMP API.
* It relies heavily on a arch backend to do the communication with the UMP device driver.
*/
#include <ump/ump.h>
#include "ump_internal.h"
#include "ump_arch.h"
#include <ump/ump_debug.h>
#include <ump/ump_osu.h>
UMP_API_EXPORT ump_result ump_open(void)
{
return ump_arch_open();
}
UMP_API_EXPORT void ump_close(void)
{
ump_arch_close();
}
UMP_API_EXPORT ump_secure_id ump_secure_id_get(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
return mem->secure_id;
}
UMP_API_EXPORT ump_handle ump_handle_create_from_secure_id(ump_secure_id secure_id)
{
unsigned long size;
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != secure_id, ("Secure ID is invalid"));
size = ump_arch_size_get(secure_id);
if (0 != size)
{
unsigned long cookie;
/*
* The UMP memory which the secure_id referes to could now be deleted and re-created
* since we don't have any references to it yet. The mapping below will however fail if
* we have supplied incorrect size, so we are safe.
*/
void * mapping = ump_arch_map(secure_id, size, UMP_CACHE_DISABLE, &cookie);
if (NULL != mapping)
{
ump_mem * mem = _ump_osu_calloc(1, sizeof(*mem));
if (NULL != mem)
{
mem->secure_id = secure_id;
mem->mapped_mem = mapping;
mem->size = size;
mem->cookie = cookie;
mem->is_cached = 1; /* Is set to actually check in the ump_cpu_msync_now() function */
_ump_osu_lock_auto_init(&mem->ref_lock, 0, 0, 0);
UMP_DEBUG_ASSERT(NULL != mem->ref_lock, ("Failed to initialize lock\n"));
mem->ref_count = 1;
/* This is called only to set the cache settings in this handle */
ump_cpu_msync_now((ump_handle)mem, UMP_MSYNC_READOUT_CACHE_ENABLED, NULL, 0);
UMP_DEBUG_PRINT(4, ("UMP handle created for ID %u of size %lu, mapped into address 0x%08lx", mem->secure_id, mem->size, (unsigned long)mem->mapped_mem));
return (ump_handle)mem;
}
ump_arch_unmap(mapping, size, cookie);
}
}
UMP_DEBUG_PRINT(2, ("UMP handle creation failed for ID %u", secure_id));
return UMP_INVALID_MEMORY_HANDLE;
}
UMP_API_EXPORT unsigned long ump_size_get(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
return mem->size;
}
UMP_API_EXPORT void ump_read(void *dst, ump_handle srch, unsigned long offset, unsigned long length)
{
ump_mem * src = (ump_mem*)srch;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != srch, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != src->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < src->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < src->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != src->mapped_mem, ("UMP Memory is not mapped"));
UMP_DEBUG_ASSERT((src->size) >= (offset + length), ("Requested read beyond end of UMP memory"));
_ump_osu_memcpy(dst,(char*)(src->mapped_mem) + offset, length);
}
UMP_API_EXPORT void ump_write(ump_handle dsth, unsigned long offset, const void *src, unsigned long length)
{
ump_mem * dst = (ump_mem*)dsth;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != dsth, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != dst->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < dst->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < dst->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != dst->mapped_mem, ("UMP Memory is not mapped"));
UMP_DEBUG_ASSERT((dst->size) >= (offset + length), ("Requested write beyond end of UMP memory"));
_ump_osu_memcpy((char*)(dst->mapped_mem) + offset, src, length);
}
UMP_API_EXPORT void* ump_mapped_pointer_get(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != mem->mapped_mem, ("Error in mapping pointer (not mapped)"));
return mem->mapped_mem;
}
UMP_API_EXPORT void ump_mapped_pointer_release(ump_handle memh)
{
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != ((ump_mem*)memh)->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < ((ump_mem*)memh)->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < ((ump_mem*)memh)->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != ((ump_mem*)memh)->mapped_mem, ("Error in mapping pointer (not mapped)"));
/* noop, cos we map in the pointer when handle is created, and unmap it when handle is destroyed */
}
UMP_API_EXPORT void ump_reference_add(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
_ump_osu_lock_wait(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
mem->ref_count += 1;
_ump_osu_lock_signal(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
}
UMP_API_EXPORT void ump_reference_release(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != ((ump_mem*)mem)->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < (((ump_mem*)mem)->ref_count), ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < ((ump_mem*)mem)->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != ((ump_mem*)mem)->mapped_mem, ("Error in mapping pointer (not mapped)"));
_ump_osu_lock_wait(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
mem->ref_count -= 1;
if (0 == mem->ref_count)
{
/* Remove memory mapping, which holds our only reference towards the UMP kernel space driver */
ump_arch_unmap(mem->mapped_mem, mem->size, mem->cookie);
_ump_osu_lock_signal(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
/* Free the lock protecting the reference count */
_ump_osu_lock_term(mem->ref_lock);
/* Free the memory for this handle */
_ump_osu_free(mem);
} else {
_ump_osu_lock_signal(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
}
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_internal.c
*
* Internal definitions and debugging macros for the UMP implementation.
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_INTERNAL_H_
#define _UNIFIED_MEMORY_PROVIDER_INTERNAL_H_
#include <ump/ump.h>
#include <ump/ump_osu.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum ump_cache_enabled
{
UMP_CACHE_DISABLE = 0,
UMP_CACHE_ENABLE = 1
} ump_cache_enabled;
/**
* The actual (hidden) definition of ump_handles.
*/
typedef struct ump_mem
{
ump_secure_id secure_id; /**< UMP device driver cookie */
void * mapped_mem; /**< Mapped memory; all read and write use this */
unsigned long size; /**< Size of allocated memory */
_ump_osu_lock_t* ref_lock; /**< Lock protection ref_count */
int ref_count; /**< The reference count of the ump_handle in userspace. It is used for finding out
when to free the memory used by this userspace handle. It is NOT the same as the
real ump_mem reference count in the devicedriver which do reference counting
for the memory that this handle reveals. */
unsigned long cookie; /**< cookie for use in arch_unmap calls */
ump_cache_enabled is_cached;
} ump_mem;
#ifdef __cplusplus
}
#endif
#endif /* _UNIFIED_MEMORY_PROVIDER_INTERNAL_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#ifndef __UMP_IOCTL_H__
#define __UMP_IOCTL_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <linux/types.h>
#include <linux/ioctl.h>
#include <ump_uk_types.h>
#ifndef __user
#define __user
#endif
/**
* @file UMP_ioctl.h
* This file describes the interface needed to use the Linux device driver.
* The interface is used by the userpace UMP driver.
*/
#define UMP_IOCTL_NR 0x90
#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s)
#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s)
#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s)
#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s)
#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_size_get_s)
#ifdef __cplusplus
}
#endif
#endif /* __UMP_IOCTL_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_osu.h
* Defines the OS abstraction layer for the base driver
*/
#ifndef __UMP_OSU_H__
#define __UMP_OSU_H__
#include <stdarg.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef unsigned int u32;
#ifdef _MSC_VER
typedef unsigned __int64 u64;
typedef signed __int64 s64;
#else
typedef unsigned long long u64;
typedef signed long long s64;
#endif
#ifndef NULL
#define NULL ((void*)0)
#endif
typedef unsigned long ump_bool;
#ifndef UMP_TRUE
#define UMP_TRUE ((ump_bool)1)
#endif
#ifndef UMP_FALSE
#define UMP_FALSE ((ump_bool)0)
#endif
#define UMP_STATIC static
/**
* @addtogroup ump_user_space_api Unified Device Driver (UDD) APIs used by UMP
*
* @{
*/
/**
* @defgroup ump_osuapi UDD OS Abstraction for User-side (OSU) APIs for UMP
*
* @{
*/
/* The following is necessary to prevent the _ump_osk_errcode_t doxygen from
* becoming unreadable: */
/** @cond OSU_COPY_OF__UMP_OSU_ERRCODE_T */
/**
* @brief OSU/OSK Error codes.
*
* Each OS may use its own set of error codes, and may require that the
* User/Kernel interface take certain error code. This means that the common
* error codes need to be sufficiently rich to pass the correct error code
* through from the OSK/OSU to U/K layer, across all OSs.
*
* The result is that some error codes will appear redundant on some OSs.
* Under all OSs, the OSK/OSU layer must translate native OS error codes to
* _ump_osk/u_errcode_t codes. Similarly, the U/K layer must translate from
* _ump_osk/u_errcode_t codes to native OS error codes.
*
*/
typedef enum
{
_UMP_OSK_ERR_OK = 0, /**< Success. */
_UMP_OSK_ERR_FAULT = -1, /**< General non-success */
_UMP_OSK_ERR_INVALID_FUNC = -2, /**< Invalid function requested through User/Kernel interface (e.g. bad IOCTL number) */
_UMP_OSK_ERR_INVALID_ARGS = -3, /**< Invalid arguments passed through User/Kernel interface */
_UMP_OSK_ERR_NOMEM = -4, /**< Insufficient memory */
_UMP_OSK_ERR_TIMEOUT = -5, /**< Timeout occured */
_UMP_OSK_ERR_RESTARTSYSCALL = -6, /**< Special: On certain OSs, must report when an interruptable mutex is interrupted. Ignore otherwise. */
_UMP_OSK_ERR_ITEM_NOT_FOUND = -7, /**< Table Lookup failed */
_UMP_OSK_ERR_BUSY = -8, /**< Device/operation is busy. Try again later */
_UMP_OSK_ERR_UNSUPPORTED = -9, /**< Optional part of the interface used, and is unsupported */
} _ump_osk_errcode_t;
/** @endcond */ /* end cond OSU_COPY_OF__UMP_OSU_ERRCODE_T */
/**
* @brief OSU Error codes.
*
* OSU error codes - enum values intentionally same as OSK
*/
typedef enum
{
_UMP_OSU_ERR_OK = 0, /**< Success. */
_UMP_OSU_ERR_FAULT = -1, /**< General non-success */
_UMP_OSU_ERR_TIMEOUT = -2, /**< Timeout occured */
} _ump_osu_errcode_t;
/** @brief Translate OSU error code to base driver error code.
*
* The _UMP_OSU_TRANSLATE_ERROR macro translates an OSU error code to the
* error codes in use by the base driver.
*/
#define _UMP_OSU_TRANSLATE_ERROR(_ump_osu_errcode) ( ( _UMP_OSU_ERR_OK == (_ump_osu_errcode) ) ? UMP_ERR_NO_ERROR : UMP_ERR_FUNCTION_FAILED)
/** @defgroup _ump_osu_lock OSU Mutual Exclusion Locks
* @{ */
/** @brief OSU Mutual Exclusion Lock flags type.
*
* This is made to look like and function identically to the OSK locks (refer
* to \ref _ump_osk_lock). However, please note the following \b important
* differences:
* - the OSU default lock is a Sleeping, non-interruptible mutex.
* - the OSU adds the ANYUNLOCK type of lock which allows a thread which doesn't
* own the lock to release the lock.
* - the order parameter when creating a lock is currently unused
*
* @note Pay careful attention to the difference in default locks for OSU and
* OSK locks; OSU locks are always non-interruptible, but OSK locks are by
* default, interruptible. This has implications for systems that do not
* distinguish between user and kernel mode.
*/
typedef enum
{
_UMP_OSU_LOCKFLAG_DEFAULT = 0, /**< Default lock type. */
/** @enum _ump_osu_lock_flags_t
*
* Flags from 0x0--0x8000 are RESERVED for Kernel-mode
*/
_UMP_OSU_LOCKFLAG_ANYUNLOCK = 0x10000, /**< Mutex that guarantees that any thread can unlock it when locked. Otherwise, this will not be possible. */
/** @enum _ump_osu_lock_flags_t
*
* Flags from 0x10000 are RESERVED for User-mode
*/
_UMP_OSU_LOCKFLAG_STATIC = 0x20000, /* Flag in OSU reserved range to identify lock as a statically initialized lock */
} _ump_osu_lock_flags_t;
typedef enum
{
_UMP_OSU_LOCKMODE_UNDEF = -1, /**< Undefined lock mode. For internal use only */
_UMP_OSU_LOCKMODE_RW = 0x0, /**< Default. Lock is used to protect data that is read from and written to */
/** @enum _ump_osu_lock_mode_t
*
* Lock modes 0x1--0x3F are RESERVED for Kernel-mode */
} _ump_osu_lock_mode_t;
/** @brief Private type for Mutual Exclusion lock objects. */
typedef struct _ump_osu_lock_t_struct _ump_osu_lock_t;
/** @brief The number of static locks supported in _ump_osu_lock_static(). */
#define UMP_OSU_STATIC_LOCK_COUNT (sizeof(_ump_osu_static_locks) / sizeof(_ump_osu_lock_t))
/** @} */ /* end group _ump_osu_lock */
/** @defgroup _ump_osu_memory OSU Memory Allocation
* @{ */
/** @brief Allocate zero-initialized memory.
*
* Returns a buffer capable of containing at least \a n elements of \a size
* bytes each. The buffer is initialized to zero.
*
* The buffer is suitably aligned for storage and subsequent access of every
* type that the compiler supports. Therefore, the pointer to the start of the
* buffer may be cast into any pointer type, and be subsequently accessed from
* such a pointer, without loss of information.
*
* When the buffer is no longer in use, it must be freed with _ump_osu_free().
* Failure to do so will cause a memory leak.
*
* @note Most toolchains supply memory allocation functions that meet the
* compiler's alignment requirements.
*
* @param n Number of elements to allocate
* @param size Size of each element
* @return On success, the zero-initialized buffer allocated. NULL on failure
*/
void *_ump_osu_calloc( u32 n, u32 size );
/** @brief Allocate memory.
*
* Returns a buffer capable of containing at least \a size bytes. The
* contents of the buffer are undefined.
*
* The buffer is suitably aligned for storage and subsequent access of every
* type that the compiler supports. Therefore, the pointer to the start of the
* buffer may be cast into any pointer type, and be subsequently accessed from
* such a pointer, without loss of information.
*
* When the buffer is no longer in use, it must be freed with _ump_osu_free().
* Failure to do so will cause a memory leak.
*
* @note Most toolchains supply memory allocation functions that meet the
* compiler's alignment requirements.
*
* Remember to free memory using _ump_osu_free().
* @param size Number of bytes to allocate
* @return On success, the buffer allocated. NULL on failure.
*/
void *_ump_osu_malloc( u32 size );
/** @brief Free memory.
*
* Reclaims the buffer pointed to by the parameter \a ptr for the system.
* All memory returned from _ump_osu_malloc(), _ump_osu_calloc() and
* _ump_osu_realloc() must be freed before the application exits. Otherwise,
* a memory leak will occur.
*
* Memory must be freed once. It is an error to free the same non-NULL pointer
* more than once.
*
* It is legal to free the NULL pointer.
*
* @param ptr Pointer to buffer to free
*/
void _ump_osu_free( void *ptr );
/** @brief Copies memory.
*
* Copies the \a len bytes from the buffer pointed by the parameter \a src
* directly to the buffer pointed by \a dst.
*
* It is an error for \a src to overlap \a dst anywhere in \a len bytes.
*
* @param dst Pointer to the destination array where the content is to be
* copied.
* @param src Pointer to the source of data to be copied.
* @param len Number of bytes to copy.
* @return \a dst is always passed through unmodified.
*/
void *_ump_osu_memcpy( void *dst, const void *src, u32 len );
/** @brief Fills memory.
*
* Sets the first \a size bytes of the block of memory pointed to by \a ptr to
* the specified value
* @param ptr Pointer to the block of memory to fill.
* @param chr Value to be set, passed as u32. Only the 8 Least Significant Bits (LSB)
* are used.
* @param size Number of bytes to be set to the value.
* @return \a ptr is always passed through unmodified
*/
void *_ump_osu_memset( void *ptr, u32 chr, u32 size );
/** @} */ /* end group _ump_osu_memory */
/** @addtogroup _ump_osu_lock
* @{ */
/** @brief Initialize a Mutual Exclusion Lock.
*
* Locks are created in the signalled (unlocked) state.
*
* The parameter \a initial must be zero.
*
* At present, the parameter \a order must be zero. It remains for future
* expansion for mutex order checking.
*
* @param flags flags combined with bitwise OR ('|'), or zero. There are
* restrictions on which flags can be combined, see \ref _ump_osu_lock_flags_t.
* @param initial For future expansion into semaphores. SBZ.
* @param order The locking order of the mutex. SBZ.
* @return On success, a pointer to a \ref _ump_osu_lock_t object. NULL on failure.
*/
_ump_osu_lock_t *_ump_osu_lock_init( _ump_osu_lock_flags_t flags, u32 initial, u32 order );
/** @brief Obtain a statically initialized Mutual Exclusion Lock.
*
* Retrieves a reference to a statically initialized lock. Up to
* _UMP_OSU_STATIC_LOCK_COUNT statically initialized locks are
* available. Only _ump_osu_lock_wait(), _ump_osu_lock_trywait(),
* _ump_osu_lock_signal() can be used with statically initialized locks.
* _UMP_OSU_LOCKMODE_RW mode should be used when waiting and signalling
* statically initialized locks.
*
* For the same \a nr a pointer to the same statically initialized lock is
* returned. That is, given the following code:
* @code
* extern u32 n;
*
* _ump_osu_lock_t *locka = _ump_osu_lock_static(n);
* _ump_osu_lock_t *lockb = _ump_osu_lock_static(n);
* @endcode
* Then (locka == lockb), for all 0 <= n < UMP_OSU_STATIC_LOCK_COUNT.
*
* @param nr index of a statically initialized lock [0..UMP_OSU_STATIC_LOCK_COUNT-1]
* @return On success, a pointer to a _ump_osu_lock_t object. NULL on failure.
*/
_ump_osu_lock_t *_ump_osu_lock_static( u32 nr );
/** @brief Initialize a Mutual Exclusion Lock safely across multiple threads.
*
* The _ump_osu_lock_auto_init() function guarantees that the given lock will
* be initialized once and precisely once, even in a situation involving
* multiple threads.
*
* This is necessary because the first call to certain Public API functions must
* initialize the API. However, there can be a race involved to call the first
* library function in multi-threaded applications. To resolve this race, a
* mutex can be used. This mutex must be initialized, but initialized only once
* by any thread that might compete for its initialization. This function
* guarantees the initialization to happen correctly, even when there is an
* initialization race between multiple threads.
*
* Otherwise, the operation is identical to the _ump_osu_lock_init() function.
* For more details, refer to _ump_osu_lock_init().
*
* @param pplock pointer to storage for a _ump_osu_lock_t pointer. This
* _ump_osu_lock_t pointer may point to a _ump_osu_lock_t that has been
* initialized already
* @param flags flags combined with bitwise OR ('|'), or zero. There are
* restrictions on which flags can be combined. Refer to
* \ref _ump_osu_lock_flags_t for more information.
* The absence of any flags (the value 0) results in a sleeping-mutex,
* which is non-interruptible.
* @param initial For future expansion into semaphores. SBZ.
* @param order The locking order of the mutex. SBZ.
* @return On success, _UMP_OSU_ERR_OK is returned and a pointer to an
* initialized \ref _ump_osu_lock_t object is written into \a *pplock.
* _UMP_OSU_ERR_FAULT is returned on failure.
*/
_ump_osu_errcode_t _ump_osu_lock_auto_init( _ump_osu_lock_t **pplock, _ump_osu_lock_flags_t flags, u32 initial, u32 order );
/** @brief Wait for a lock to be signalled (obtained).
*
* After a thread has successfully waited on the lock, the lock is obtained by
* the thread, and is marked as unsignalled. The thread releases the lock by
* signalling it.
*
* To prevent deadlock, locks must always be obtained in the same order.
*
* @param lock the lock to wait upon (obtain).
* @param mode the mode in which the lock should be obtained. Currently this
* must be _UMP_OSU_LOCKMODE_RW.
* @return On success, _UMP_OSU_ERR_OK, _UMP_OSU_ERR_FAULT on error.
*/
_ump_osu_errcode_t _ump_osu_lock_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode);
/** @brief Wait for a lock to be signalled (obtained) with timeout
*
* After a thread has successfully waited on the lock, the lock is obtained by
* the thread, and is marked as unsignalled. The thread releases the lock by
* signalling it.
*
* To prevent deadlock, locks must always be obtained in the same order.
*
* This version can return early if it cannot obtain the lock within the given timeout.
*
* @param lock the lock to wait upon (obtain).
* @param mode the mode in which the lock should be obtained. Currently this
* must be _UMP_OSU_LOCKMODE_RW.
* @param timeout Relative time in microseconds for the timeout
* @return _UMP_OSU_ERR_OK if the lock was obtained, _UMP_OSU_ERR_TIMEOUT if the timeout expired or _UMP_OSU_ERR_FAULT on error.
*/
_ump_osu_errcode_t _ump_osu_lock_timed_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode, u64 timeout);
/** @brief Test for a lock to be signalled and obtains the lock when so.
*
* Obtains the lock only when it is in signalled state. The lock is then
* marked as unsignalled. The lock is released again by signalling
* it by _ump_osu_lock_signal().
*
* If the lock could not be obtained immediately (that is, another thread
* currently holds the lock), then this function \b does \b not wait for the
* lock to be in a signalled state. Instead, an error code is immediately
* returned to indicate that the thread could not obtain the lock.
*
* To prevent deadlock, locks must always be obtained in the same order.
*
* @param lock the lock to wait upon (obtain).
* @param mode the mode in which the lock should be obtained. Currently this
* must be _UMP_OSU_LOCKMODE_RW.
* @return When the lock was obtained, _UMP_OSU_ERR_OK. If the lock could not
* be obtained, _UMP_OSU_ERR_FAULT.
*/
_ump_osu_errcode_t _ump_osu_lock_trywait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode);
/** @brief Signal (release) a lock.
*
* Locks may only be signalled by the thread that originally waited upon the
* lock, unless the lock was created using the _UMP_OSU_LOCKFLAG_ANYUNLOCK flag.
*
* @param lock the lock to signal (release).
* @param mode the mode in which the lock should be obtained. This must match
* the mode in which the lock was waited upon.
*/
void _ump_osu_lock_signal( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode );
/** @brief Terminate a lock.
*
* This terminates a lock and frees all associated resources.
*
* It is a programming error to terminate the lock when it is held (unsignalled)
* by a thread.
*
* @param lock the lock to terminate.
*/
void _ump_osu_lock_term( _ump_osu_lock_t *lock );
/** @} */ /* end group _ump_osu_lock */
/** @} */ /* end group osuapi */
/** @} */ /* end group uddapi */
#ifdef __cplusplus
}
#endif
#endif /* __UMP_OSU_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#if ((!defined _XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 600))
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#define _POSIX_C_SOURCE 200112L
#include <ump/ump_osu.h>
#include <ump/ump_debug.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
/**
* @file ump_osu_locks.c
* File implements the user side of the OS interface
*/
/** @opt Most of the time, we use the plain mutex type of osu_lock, and so
* only require the flags and mutex members. This costs 2 extra DWORDS, but
* most of the time we don't use those DWORDS.
* Therefore, ANY_UNLOCK type osu_locks can be implemented as a second
* structure containing the member _ump_osu_lock_t lock_t, plus the extra
* state required. Then, we use &container->lock_t when passing out of the
* OSU api, and CONTAINER_OF() when passing back in to recover the original
* structure. */
/** Private declaration of the OSU lock type */
struct _ump_osu_lock_t_struct
{
/** At present, only two types of mutex, so we store this information as
* the flags supplied at init time */
_ump_osu_lock_flags_t flags;
pthread_mutex_t mutex; /**< Used in both plain and ANY_UNLOCK osu_locks */
/* Extra State for ANY_UNLOCK osu_locks. These are UNINITIALIZED when
* flags does not contain _UMP_OSU_LOCKFLAG_ANYUNLOCK: */
pthread_cond_t condition; /**< The condition object to use while blocking */
ump_bool state; /**< The boolean which indicates the event's state */
UMP_DEBUG_CODE(
/** debug checking of locks */
_ump_osu_lock_mode_t locked_as;
) /* UMP_DEBUG_CODE */
};
/* Provide two statically initialized locks */
UMP_STATIC _ump_osu_lock_t _ump_osu_static_locks[] =
{
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
};
/* Critical section for auto_init */
UMP_STATIC pthread_mutex_t static_auto_init_mutex = PTHREAD_MUTEX_INITIALIZER;
_ump_osu_errcode_t _ump_osu_lock_auto_init( _ump_osu_lock_t **pplock, _ump_osu_lock_flags_t flags, u32 initial, u32 order )
{
int call_result;
/* Validate parameters: */
UMP_DEBUG_ASSERT_POINTER( pplock );
/** @opt We don't lock the Critical Section or do anything if this is already non-null */
if ( NULL != *pplock)
{
return _UMP_OSU_ERR_OK;
}
/* We MIGHT need to initialize it, lock the Critical Section and check again */
call_result = pthread_mutex_lock(&static_auto_init_mutex);
/* It would be a programming error for this to fail: */
UMP_DEBUG_ASSERT( 0 == call_result,
("failed to lock critical section\n") );
if ( NULL != *pplock )
{
/*
We caught a race condition to initialize this osu_lock.
The other thread won the race, so the osu_lock is now initialized.
*/
call_result = pthread_mutex_unlock(&static_auto_init_mutex);
UMP_DEBUG_ASSERT(0 == call_result,
("failed to unlock critical section\n"));
return _UMP_OSU_ERR_OK;
}
/* We're the first thread in: initialize the osu_lock */
*pplock = _ump_osu_lock_init( flags, initial, order );
if ( NULL == *pplock )
{
/* osu_lock creation failed */
call_result = pthread_mutex_unlock(&static_auto_init_mutex);
UMP_DEBUG_ASSERT(0 == call_result,
("failed to unlock critical section\n"));
return _UMP_OSU_ERR_FAULT;
}
/* osu_lock created OK */
call_result = pthread_mutex_unlock(&static_auto_init_mutex);
UMP_DEBUG_ASSERT(0 == call_result,
("failed to unlock critical section\n"));
UMP_IGNORE( call_result );
return _UMP_OSU_ERR_OK;
}
_ump_osu_lock_t *_ump_osu_lock_init( _ump_osu_lock_flags_t flags, u32 initial, u32 order )
{
_ump_osu_lock_t * lock;
pthread_mutexattr_t mutex_attributes;
UMP_IGNORE(order); /* order isn't implemented yet, for now callers should set it to zero. */
/* Validate parameters: */
/* Flags acceptable */
UMP_DEBUG_ASSERT( 0 == ( flags & ~( _UMP_OSU_LOCKFLAG_ANYUNLOCK)),
("incorrect flags or trying to initialise a statically initialized lock, %.8X\n", flags) );
/* Parameter initial SBZ - for future expansion */
UMP_DEBUG_ASSERT( 0 == initial,
("initial must be zero\n") );
if (0 != pthread_mutexattr_init(&mutex_attributes))
{
return NULL;
}
#if UMP_DEBUG_EXTENDED_MUTEX_LOCK_CHECKING
#define UMP_PTHREADS_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
#else
#define UMP_PTHREADS_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT
#endif
if (0 != pthread_mutexattr_settype(&mutex_attributes, UMP_PTHREADS_MUTEX_TYPE))
{
/** Return NULL on failure */
pthread_mutexattr_destroy(&mutex_attributes);
return NULL;
}
#undef UMP_PTHREADS_MUTEX_TYPE
/** @opt use containing structures for the ANY_UNLOCK type, to
* save 2 DWORDS when not in use */
lock = _ump_osu_malloc( sizeof(_ump_osu_lock_t) );
if( NULL == lock )
{
/** Return NULL on failure */
pthread_mutexattr_destroy(&mutex_attributes);
return NULL;
}
if (0 != pthread_mutex_init( &lock->mutex, &mutex_attributes ))
{
pthread_mutexattr_destroy(&mutex_attributes);
_ump_osu_free( lock );
return NULL;
}
/* done with the mutexattr object */
pthread_mutexattr_destroy(&mutex_attributes);
/* ANY_UNLOCK type */
if ( flags & _UMP_OSU_LOCKFLAG_ANYUNLOCK )
{
if (0 != pthread_cond_init( &lock->condition, NULL ))
{
/* cleanup */
pthread_mutex_destroy( &lock->mutex );
_ump_osu_free( lock );
return NULL;
}
lock->state = UMP_FALSE; /* mark as unlocked by default */
}
lock->flags = flags;
/** Debug lock checking */
UMP_DEBUG_CODE( lock->locked_as = _UMP_OSU_LOCKMODE_UNDEF );
return lock;
}
_ump_osu_errcode_t _ump_osu_lock_timed_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode, u64 timeout)
{
/* absolute time specifier */
struct timespec ts;
struct timeval tv;
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKFLAG_ANYUNLOCK == lock->flags, ("Timed operations only implemented for ANYUNLOCK type locks"));
/* calculate the realtime timeout value */
if (0 != gettimeofday(&tv, NULL))
{
UMP_DEBUG_PRINT(1,("Could not get the current realtime value to calculate the absolute value for a timed mutex lock with a timeout"));
return _UMP_OSU_ERR_FAULT;
}
tv.tv_usec += timeout;
#define UMP_USECS_PER_SECOND 1000000LL
#define UMP_NANOSECS_PER_USEC 1000LL
/* did we overflow a second in the usec part? */
while (tv.tv_usec >= UMP_USECS_PER_SECOND)
{
tv.tv_usec -= UMP_USECS_PER_SECOND;
tv.tv_sec++;
}
/* copy to the correct struct */
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = (tv.tv_usec * UMP_NANOSECS_PER_USEC);
#undef UMP_USECS_PER_SECOND
#undef UMP_NANOSECS_PER_USEC
/* lock the mutex protecting access to the state field */
pthread_mutex_lock( &lock->mutex );
/* loop while locked (state is UMP_TRUE) */
/* pthread_cond_timedwait unlocks the mutex, wait, and locks the mutex once unblocked (either due to the event or the timeout) */
while ( UMP_TRUE == lock->state )
{
int res;
res = pthread_cond_timedwait( &lock->condition, &lock->mutex, &ts );
if (0 == res) continue; /* test the state variable again (loop condition) */
else if (ETIMEDOUT == res)
{
/* timeout, need to clean up and return the correct error code */
pthread_mutex_unlock(&lock->mutex);
return _UMP_OSU_ERR_TIMEOUT;
}
else
{
UMP_DEBUG_PRINT(1, ("Unexpected return from pthread_cond_timedwait 0x%08X\n", res));
pthread_mutex_unlock(&lock->mutex);
return _UMP_OSU_ERR_FAULT;
}
}
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("This lock was already locked\n") );
UMP_DEBUG_CODE( lock->locked_as = mode );
/* the state is UMP_FALSE (unlocked), so we set it to UMP_TRUE to indicate that it's locked and can return knowing that we own the lock */
lock->state = UMP_TRUE;
/* final unlock of the mutex */
pthread_mutex_unlock(&lock->mutex);
return _UMP_OSU_ERR_OK;
}
_ump_osu_errcode_t _ump_osu_lock_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode)
{
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
/** @note since only one flag can be set, we use a switch statement here.
* Otherwise, MUST add an enum into the _ump_osu_lock_t to store the
* implemented lock type */
switch ( lock->flags )
{
case _UMP_OSU_LOCKFLAG_STATIC:
case 0:
/* Usual Mutex type */
{
int call_result;
call_result = pthread_mutex_lock( &lock->mutex );
UMP_DEBUG_ASSERT( 0 == call_result,
("pthread_mutex_lock call failed with error code %d\n", call_result));
UMP_IGNORE( call_result );
}
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("This lock was already locked\n") );
UMP_DEBUG_CODE( lock->locked_as = mode );
break;
case _UMP_OSU_LOCKFLAG_ANYUNLOCK:
/** @note Use of bitflags in a case statement ONLY works because this
* is the ONLY flag that is supported */
/* lock the mutex protecting access to the state field */
pthread_mutex_lock( &lock->mutex );
/* loop while locked (state is UMP_TRUE) */
/* pthread_cond_wait unlocks the mutex, wait, and locks the mutex once unblocked */
while ( UMP_TRUE == lock->state ) pthread_cond_wait( &lock->condition, &lock->mutex );
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("This lock was already locked\n") );
UMP_DEBUG_CODE( lock->locked_as = mode );
/* the state is UMP_FALSE (unlocked), so we set it to UMP_TRUE to indicate that it's locked and can return knowing that we own the lock */
lock->state = UMP_TRUE;
/* final unlock of the mutex */
pthread_mutex_unlock(&lock->mutex);
break;
default:
UMP_DEBUG_ERROR( ("lock has incorrect flags==%.8X\n", lock->flags) );
break;
}
return _UMP_OSU_ERR_OK;
}
_ump_osu_errcode_t _ump_osu_lock_trywait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode)
{
_ump_osu_errcode_t err = _UMP_OSU_ERR_FAULT;
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
/** @note since only one flag can be set, we use a switch statement here.
* Otherwise, MUST add an enum into the _ump_osu_lock_t to store the
* implemented lock type */
switch ( lock->flags )
{
case _UMP_OSU_LOCKFLAG_STATIC:
case 0:
/* Usual Mutex type */
{
/* This is not subject to UMP_CHECK - overriding the result would cause a programming error */
if ( 0 == pthread_mutex_trylock( &lock->mutex ) )
{
err = _UMP_OSU_ERR_OK;
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as
|| mode == lock->locked_as,
("tried as mode==%.8X, but was locked as %.8X\n", mode, lock->locked_as) );
UMP_DEBUG_CODE( lock->locked_as = mode );
}
}
break;
case _UMP_OSU_LOCKFLAG_ANYUNLOCK:
/** @note Use of bitflags in a case statement ONLY works because this
* is the ONLY flag that is supported */
/* lock the mutex protecting access to the state field */
pthread_mutex_lock(&lock->mutex);
if ( UMP_FALSE == lock->state)
{
/* unlocked, take the lock */
lock->state = UMP_TRUE;
err = _UMP_OSU_ERR_OK;
}
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
/* Can do this regardless of whether we obtained ANYUNLOCK: */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as
|| mode == lock->locked_as,
("tried as mode==%.8X, but was locked as %.8X\n", mode, lock->locked_as) );
/* If we were already locked, this does no harm, because of the above assert: */
UMP_DEBUG_CODE( lock->locked_as = mode );
pthread_mutex_unlock(&lock->mutex);
break;
default:
UMP_DEBUG_ERROR( ("lock has incorrect flags==%.8X\n", lock->flags) );
break;
}
return err;
}
void _ump_osu_lock_signal( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode )
{
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
/** @note since only one flag can be set, we use a switch statement here.
* Otherwise, MUST add an enum into the _ump_osu_lock_t to store the
* implemented lock type */
switch ( lock->flags )
{
case _UMP_OSU_LOCKFLAG_STATIC:
case 0:
/* Usual Mutex type */
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( mode == lock->locked_as,
("This lock was locked as==%.8X, but tried to unlock as mode==%.8X\n", lock->locked_as, mode));
UMP_DEBUG_CODE( lock->locked_as = _UMP_OSU_LOCKMODE_UNDEF );
{
int call_result;
call_result = pthread_mutex_unlock( &lock->mutex );
UMP_DEBUG_ASSERT( 0 == call_result,
("pthread_mutex_lock call failed with error code %d\n", call_result));
UMP_IGNORE( call_result );
}
break;
case _UMP_OSU_LOCKFLAG_ANYUNLOCK:
/** @note Use of bitflags in a case statement ONLY works because this
* is the ONLY flag that is supported */
pthread_mutex_lock(&lock->mutex);
UMP_DEBUG_ASSERT( UMP_TRUE == lock->state, ("Unlocking a _ump_osu_lock_t %p which is not locked\n", lock));
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( mode == lock->locked_as,
("This lock was locked as==%.8X, but tried to unlock as %.8X\n", lock->locked_as, mode ));
UMP_DEBUG_CODE( lock->locked_as = _UMP_OSU_LOCKMODE_UNDEF );
/* mark as unlocked */
lock->state = UMP_FALSE;
/* signal the condition, only wake a single thread */
pthread_cond_signal(&lock->condition);
pthread_mutex_unlock(&lock->mutex);
break;
default:
UMP_DEBUG_ERROR( ("lock has incorrect flags==%.8X\n", lock->flags) );
break;
}
}
void _ump_osu_lock_term( _ump_osu_lock_t *lock )
{
int call_result;
UMP_DEBUG_ASSERT_POINTER( lock );
/** Debug lock checking: */
/* Lock is signalled on terminate - not a guarantee, since we could be locked immediately beforehand */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("cannot terminate held lock\n") );
call_result = pthread_mutex_destroy( &lock->mutex );
UMP_DEBUG_ASSERT( 0 == call_result,
("Incorrect mutex use detected: pthread_mutex_destroy call failed with error code %d\n", call_result) );
/* Destroy extra state for ANY_UNLOCK type osu_locks */
if ( lock->flags & _UMP_OSU_LOCKFLAG_ANYUNLOCK )
{
UMP_DEBUG_ASSERT( UMP_FALSE == lock->state, ("terminate called on locked object %p\n", lock));
call_result = pthread_cond_destroy(&lock->condition);
UMP_DEBUG_ASSERT( 0 == call_result,
("Incorrect condition-variable use detected: pthread_cond_destroy call failed with error code %d\n", call_result) );
}
UMP_IGNORE(call_result);
_ump_osu_free( lock );
}
_ump_osu_lock_t *_ump_osu_lock_static( u32 nr )
{
UMP_DEBUG_ASSERT( nr < UMP_OSU_STATIC_LOCK_COUNT,
("provided static lock index (%d) out of bounds (0 < nr < %d)\n", nr, UMP_OSU_STATIC_LOCK_COUNT) );
return &_ump_osu_static_locks[nr];
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#include <ump/ump_osu.h>
#include <stdlib.h>
#include <string.h> /* memcmp, memchr, memset */
/**
* @file ump_osu_memory.c
* File implements the user side of the OS interface
*/
void *_ump_osu_calloc( u32 n, u32 size )
{
return calloc( n, size );
}
void *_ump_osu_malloc( u32 size )
{
return malloc( size );
}
void *_ump_osu_realloc( void *ptr, u32 size )
{
return realloc( ptr, size );
}
void _ump_osu_free( void *ptr )
{
free( ptr );
}
void *_ump_osu_memcpy( void *dst, const void *src, u32 len )
{
return memcpy( dst, src, len );
}
void *_ump_osu_memset( void *ptr, u32 chr, u32 size )
{
return memset( ptr, chr, size );
}
int _ump_osu_memcmp( const void *ptr1, const void *ptr2, u32 size )
{
return memcmp( ptr1, ptr2, size );
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_platform.h
*
* This file should define UMP_API_EXPORT,
* which dictates how the UMP user space API should be exported/imported.
* Modify this file, if needed, to match your platform setup.
*/
#ifndef __UMP_PLATFORM_H__
#define __UMP_PLATFORM_H__
/** @addtogroup ump_user_space_api
* @{ */
/**
* A define which controls how UMP user space API functions are imported and exported.
* This define should be set by the implementor of the UMP API.
*/
#if defined(_WIN32)
#define UMP_API_EXPORT
#elif defined(__SYMBIAN32__)
#define UMP_API_EXPORT IMPORT_C
#else
#if defined(__GNUC__)
#if __GNUC__ >= 4
# define MALI_VISIBLE __attribute__ ((visibility ("default"))) /**< Function should be visible from outside the dll */
#else
# define MALI_VISIBLE
#endif
#elif defined(__ARMCC_VERSION)
/* ARMCC specific */
# define MALI_VISIBLE __declspec(dllexport)
#else
# define MALI_VISIBLE
#endif
#define UMP_API_EXPORT MALI_VISIBLE
#endif
/** @} */ /* end group ump_user_space_api */
#endif /* __UMP_PLATFORM_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_ref_drv.c
*
* Implementation of the user space API extensions provided by the reference implementation.
*/
#include <ump/ump_ref_drv.h>
#include <ump/ump.h>
#include "ump_internal.h"
#include "ump_arch.h"
#include <ump/ump_debug.h>
#include <ump/ump_osu.h>
/* Allocate a buffer which can be used directly by hardware, 4kb aligned */
static ump_handle ump_ref_drv_allocate_internal(unsigned long size, ump_alloc_constraints constraints, ump_cache_enabled cache);
/* Allocate a buffer which can be used directly by hardware, 4kb aligned */
ump_handle ump_ref_drv_allocate(unsigned long size, ump_alloc_constraints constraints)
{
ump_cache_enabled cache= UMP_CACHE_DISABLE;
if ( 0!=(constraints&UMP_REF_DRV_CONSTRAINT_USE_CACHE) )
{
cache = UMP_CACHE_ENABLE;
}
return ump_ref_drv_allocate_internal(size, constraints, cache);
}
UMP_API_EXPORT int ump_cpu_msync_now(ump_handle memh, ump_cpu_msync_op op, void* address, int size)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
/* If the op is readout, we do the readout from DD.
Else we skip flushing if the userspace handle says that it is uncached */
if ((UMP_MSYNC_READOUT_CACHE_ENABLED!=op) && (0 == mem->is_cached) ) return 0;
UMP_DEBUG_ASSERT(0 < (((ump_mem*)mem)->ref_count), ("Reference count too low"));
UMP_DEBUG_ASSERT((size>=0) && (size <= (int)((ump_mem*)mem)->size), ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != ((ump_mem*)mem)->mapped_mem, ("Error in mapping pointer (not mapped)"));
if (size > (int)mem->size) size = mem->size;
mem->is_cached = ump_arch_msync(mem->secure_id, mem->mapped_mem, mem->cookie, address, size, op);
return mem->is_cached ;
}
/* Allocate a buffer which can be used directly by hardware, 4kb aligned */
static ump_handle ump_ref_drv_allocate_internal(unsigned long size, ump_alloc_constraints constraints, ump_cache_enabled cache)
{
ump_secure_id secure_id;
unsigned long allocated_size = size;
UMP_DEBUG_PRINT(4, ("Allocating UMP memory of size %lu", size));
secure_id = ump_arch_allocate(&allocated_size, constraints);
if (secure_id != UMP_INVALID_SECURE_ID)
{
unsigned long cookie;
void * mapping;
mapping = ump_arch_map(secure_id, allocated_size, cache, &cookie);
if (NULL != mapping)
{
/*
* PS: By now we have actually increased the ref count in the device driver by 2,
* one for the allocation iteself, and one for the mapping.
*/
ump_mem * mem;
mem = _ump_osu_calloc(1, sizeof(*mem));
if (NULL != mem)
{
mem->secure_id = secure_id;
mem->mapped_mem = mapping;
mem->size = allocated_size;
mem->cookie = cookie;
mem->is_cached = 1; /* Default to ON, is disabled later if not */
_ump_osu_lock_auto_init(&mem->ref_lock, 0, 0, 0);
UMP_DEBUG_ASSERT(NULL != mem->ref_lock, ("Failed to initialize lock\n"));
mem->ref_count = 1;
/*
* ump_arch_allocate() gave us a kernel space reference, and the same did ump_arch_map()
* We release the one from ump_arch_allocate(), and rely solely on the one from the ump_arch_map()
* That is, ump_arch_unmap() should now do the final release towards the UMP kernel space driver.
*/
ump_arch_reference_release(secure_id);
/* This is called only to set the cache settings in this handle */
ump_cpu_msync_now((ump_handle)mem, UMP_MSYNC_READOUT_CACHE_ENABLED, NULL, 0);
UMP_DEBUG_PRINT(4, ("UMP handle created for ID %u of size %lu, mapped into address 0x%08lx", mem->secure_id, mem->size, (unsigned long)mem->mapped_mem));
return (ump_handle)mem;
}
ump_arch_unmap(mapping, allocated_size, cookie); /* Unmap the memory */
ump_arch_reference_release(secure_id); /* Release reference added when we allocated the UMP memory */
}
ump_arch_reference_release(secure_id); /* Release reference added when we allocated the UMP memory */
}
UMP_DEBUG_PRINT(4, ("Allocation of UMP memory failed"));
return UMP_INVALID_MEMORY_HANDLE;
}
/*
* Copyright (C) 2010, 2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_ref_drv.h
*
* Reference driver extensions to the UMP user space API for allocating UMP memory
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_REF_DRV_H_
#define _UNIFIED_MEMORY_PROVIDER_REF_DRV_H_
#include "ump.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
/* This enum must match with the IOCTL enum in ump_ioctl.h */
UMP_REF_DRV_CONSTRAINT_NONE = 0,
UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR = 1,
UMP_REF_DRV_CONSTRAINT_USE_CACHE = 4,
} ump_alloc_constraints;
/** Allocate an UMP handle containing a memory buffer.
* Input: Size: The minimum size for the allocation.
* Usage: If this is UMP_REF_DRV_CONSTRAINT_USE_CACHE, the allocation is mapped as cached by the cpu.
* If it is UMP_REF_DRV_CONSTRAINT_NONE it is mapped as noncached.
* The flag UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR is not supported.*/
UMP_API_EXPORT ump_handle ump_ref_drv_allocate(unsigned long size, ump_alloc_constraints usage);
typedef enum
{
UMP_MSYNC_CLEAN = 0 ,
UMP_MSYNC_CLEAN_AND_INVALIDATE = 1,
UMP_MSYNC_READOUT_CACHE_ENABLED = 128,
} ump_cpu_msync_op;
/** Flushing cache for an ump_handle.
* The function will always CLEAN_AND_INVALIDATE as long as the \a op is not UMP_MSYNC_READOUT_CACHE_ENABLED.
* If so it will only report back if the given ump_handle is cacheable.
* At the momement the implementation does not use \a address or \a size.
* Return value is 1 if cache is enabled, and 0 if it is disabled for the given allocation.*/
UMP_API_EXPORT int ump_cpu_msync_now(ump_handle mem, ump_cpu_msync_op op, void* address, int size);
#ifdef __cplusplus
}
#endif
#endif /*_UNIFIED_MEMORY_PROVIDER_REF_DRV_H_ */
/*
* Copyright (C) 2010, 2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_uk_types.h
* Defines the types and constants used in the user-kernel interface
*/
#ifndef __UMP_UK_TYPES_H__
#define __UMP_UK_TYPES_H__
#ifdef __cplusplus
extern "C"
{
#endif
/* Helpers for API version handling */
#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x))
#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF))
#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF)
#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y))))
/**
* API version define.
* Indicates the version of the kernel API
* The version is a 16bit integer incremented on each API change.
* The 16bit integer is stored twice in a 32bit integer
* So for version 1 the value would be 0x00010001
*/
#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2)
typedef enum
{
_UMP_IOC_QUERY_API_VERSION = 1,
_UMP_IOC_ALLOCATE,
_UMP_IOC_RELEASE,
_UMP_IOC_SIZE_GET,
_UMP_IOC_MAP_MEM, /* not used in Linux */
_UMP_IOC_UNMAP_MEM, /* not used in Linux */
_UMP_IOC_MSYNC,
}_ump_uk_functions;
typedef enum
{
UMP_REF_DRV_UK_CONSTRAINT_NONE = 0,
UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1,
UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 4,
} ump_uk_alloc_constraints;
typedef enum
{
_UMP_UK_MSYNC_CLEAN = 0,
_UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1,
_UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128,
} ump_uk_msync_op;
/**
* Get API version ([in,out] u32 api_version, [out] u32 compatible)
*/
typedef struct _ump_uk_api_version_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */
u32 compatible; /**< Non-null if the device is compatible with the client */
} _ump_uk_api_version_s;
/**
* ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints)
*/
typedef struct _ump_uk_allocate_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< Return value from DD to Userdriver */
u32 size; /**< Input and output. Requested size; input. Returned size; output */
ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */
} _ump_uk_allocate_s;
/**
* SIZE_GET ([in] u32 secure_id, [out]size )
*/
typedef struct _ump_uk_size_get_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< Input to DD */
u32 size; /**< Returned size; output */
} _ump_uk_size_get_s;
/**
* Release ([in] u32 secure_id)
*/
typedef struct _ump_uk_release_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< Input to DD */
} _ump_uk_release_s;
typedef struct _ump_uk_map_mem_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
void *mapping; /**< [out] Returns user-space virtual address for the mapping */
void *phys_addr; /**< [in] physical address */
unsigned long size; /**< [in] size */
u32 secure_id; /**< [in] secure_id to assign to mapping */
void * _ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */
u32 cookie;
u32 is_cached; /**< [in,out] caching of CPU mappings */
} _ump_uk_map_mem_s;
typedef struct _ump_uk_unmap_mem_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
void *mapping;
u32 size;
void * _ukk_private;
u32 cookie;
} _ump_uk_unmap_mem_s;
typedef struct _ump_uk_msync_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
void *mapping; /**< [in] mapping addr */
void *address; /**< [in] flush start addr */
u32 size; /**< [in] size to flush */
ump_uk_msync_op op; /**< [in] flush operation */
u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */
u32 secure_id; /**< [in] cookie stored with reference to the kernel mapping internals */
u32 is_cached; /**< [out] caching of CPU mappings */
} _ump_uk_msync_s;
#ifdef __cplusplus
}
#endif
#endif /* __UMP_UK_TYPES_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_uku.c
* File implements the user side of the user-kernel interface
*/
#include "ump_uku.h"
#include <stdio.h>
#include "ump_ioctl.h"
#include <sys/mman.h>
/* Needed for file operations on the device file*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
static _ump_osu_errcode_t ump_driver_ioctl(void *context, u32 command, void *args);
static int ump_ioctl_api_version_used = UMP_IOCTL_API_VERSION;
/**
* The device file to access the UMP device driver
* This is a character special file giving access to the device driver.
* Usually created using the mknod command line utility.
*/
static const char ump_device_file_name[] = "/dev/ump";
_ump_osu_errcode_t _ump_uku_open( void **context )
{
int ump_device_file;
if(NULL == context)
{
return _UMP_OSU_ERR_FAULT;
}
ump_device_file = open(ump_device_file_name, O_RDWR);
if (-1 == ump_device_file)
{
return _UMP_OSU_ERR_FAULT;
}
{
struct _ump_uk_api_version_s args;
args.ctx = (void*)ump_device_file;
args.version = UMP_IOCTL_API_VERSION;
args.compatible = 3;
ump_driver_ioctl(args.ctx, UMP_IOC_QUERY_API_VERSION, &args);
if ( 1 != args.compatible )
{
if (IS_API_MATCH(MAKE_VERSION_ID(1), args.version))
{
ump_ioctl_api_version_used = MAKE_VERSION_ID(1);
UMP_PRINTF("The UMP devicedriver does not support cached UMP. Update it if this is needed.\n");
}
else
{
UMP_PRINTF("The UMP devicedriver is version: %d, UMP libraries is version: %d.\n", GET_VERSION(args.version), GET_VERSION(UMP_IOCTL_API_VERSION) );
close(ump_device_file);
return _UMP_OSU_ERR_FAULT;
}
}
}
*context = (void *) ump_device_file;
return _UMP_OSU_ERR_OK;
}
_ump_osu_errcode_t _ump_uku_close( void **context )
{
if(NULL == context)
{
return _UMP_OSU_ERR_FAULT;
}
if(-1 == (int)*context)
{
return _UMP_OSU_ERR_FAULT;
}
close((int)*context);
*context = (void *)-1;
return _UMP_OSU_ERR_OK;
}
int _ump_uku_allocate(_ump_uk_allocate_s *args)
{
return ump_driver_ioctl(args->ctx, UMP_IOC_ALLOCATE, args);
}
_ump_osu_errcode_t _ump_uku_release(_ump_uk_release_s *args)
{
return ump_driver_ioctl(args->ctx, UMP_IOC_RELEASE, args);
}
_ump_osu_errcode_t _ump_uku_size_get(_ump_uk_size_get_s *args)
{
return ump_driver_ioctl(args->ctx, UMP_IOC_SIZE_GET, args);
}
void _ump_uku_msynch(_ump_uk_msync_s *args)
{
/* This is for backwards compatibillity */
if ( MAKE_VERSION_ID(1) == ump_ioctl_api_version_used)
{
args->is_cached = 0;
if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED != args->op )
{
UMP_DEBUG_PRINT(3, ("Warning: Doing UMP cache flush operations on a Device Driver that does not support cached UMP mem.\n"));
}
return;
}
ump_driver_ioctl(args->ctx, UMP_IOC_MSYNC, args);
}
int _ump_uku_map_mem(_ump_uk_map_mem_s *args)
{
int flags;
if( -1 == (int)args->ctx )
{
return -1;
}
flags = MAP_SHARED;
/* This is for backwards compatibillity */
if ( MAKE_VERSION_ID(1) == ump_ioctl_api_version_used)
{
args->is_cached = 0;
}
/* If we want the Caching to be enabled we set the flags to be PRIVATE. The UMP DD reads this and do proper handling
Note: this enforces the user to use proper invalidation*/
if ( args->is_cached ) flags = MAP_PRIVATE;
args->mapping = mmap(NULL, args->size, PROT_READ | PROT_WRITE ,flags , (int)args->ctx, (off_t)args->secure_id * sysconf(_SC_PAGE_SIZE));
if (MAP_FAILED == args->mapping)
{
return -1;
}
args->cookie = 0; /* Cookie is not used in linux _ump_uku_unmap_mem */
return 0;
}
void _ump_uku_unmap_mem( _ump_uk_unmap_mem_s *args )
{
/*
* If a smaller size is used Linux will just remove the requested range but don't tell
* the ump driver before all of it is unmapped, either via another unmap request or upon process shutdown.
* Unmapping too much will just ignore the overhead or hit undefined behavior,
* only affecting the calling process which could mess itself up in other ways anyway.
* So we don't need any security checks here.
*/
munmap(args->mapping, args->size);
}
static _ump_osu_errcode_t ump_driver_ioctl(void *context, u32 command, void *args)
{
/*UMP_CHECK_NON_NULL(args, _UMP_OSK_ERR_INVALID_ARGS);*/
/* check for a valid file descriptor */
/** @note manual type safety check-point */
if( -1 == (int)context )
{
return _UMP_OSU_ERR_FAULT;
}
/* call ioctl handler of driver */
if (0 != ioctl((int)context, command, args)) return -1;
return _UMP_OSU_ERR_OK;
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/**
* @file ump_uku.h
* Defines the user-side interface of the user-kernel interface
*/
#ifndef __UMP_UKU_H__
#define __UMP_UKU_H__
#include <ump/ump_osu.h>
#include <ump/ump_debug.h>
#include <ump/ump_uk_types.h>
#ifdef __cplusplus
extern "C"
{
#endif
_ump_osu_errcode_t _ump_uku_open( void **context );
_ump_osu_errcode_t _ump_uku_close( void **context );
_ump_osu_errcode_t _ump_uku_allocate( _ump_uk_allocate_s *args );
_ump_osu_errcode_t _ump_uku_release( _ump_uk_release_s *args );
_ump_osu_errcode_t _ump_uku_size_get( _ump_uk_size_get_s *args );
_ump_osu_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args );
int _ump_uku_map_mem( _ump_uk_map_mem_s *args );
void _ump_uku_unmap_mem( _ump_uk_unmap_mem_s *args );
void _ump_uku_msynch(_ump_uk_msync_s *args);
#ifdef __cplusplus
}
#endif
#endif /* __UMP_UKU_H__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment