/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef COMMON_MEM_H #define COMMON_MEM_H #ifndef MEM_DONT_USE_SMALL_ALLOC_POOL #define MEM_USE_SMALL_ALLOC_POOL #endif #ifndef MEM_CHUNK_SIZE #define MEM_CHUNK_SIZE 0x40000 #endif #include #include "vcinclude/common.h" #ifdef _MSC_VER #define RCM_ALIGNOF(T) __alignof(T) #else #define RCM_ALIGNOF(T) (sizeof(struct { T t; char ch; }) - sizeof(T)) #endif #ifdef _MSC_VER #define RCM_INLINE __inline #else #ifdef __LCC__ #define RCM_INLINE #else #define RCM_INLINE inline #endif #endif /****************************************************************************** Global initialisation helpers ******************************************************************************/ /* Locate the relocatable heap partition, or malloc sufficient space, then call mem_init. */ int32_t rtos_common_mem_init( void ); /****************************************************************************** Pool management API ******************************************************************************/ /* Options for mem_compact. */ typedef enum { /* these values are duplicated in rtos_common_mem.inc */ MEM_COMPACT_NONE = 0, /* No compaction allowed */ MEM_COMPACT_NORMAL = 1, /* Move unlocked blocks */ MEM_COMPACT_DISCARD = 2, /* _NORMAL + discard blocks where possible */ MEM_COMPACT_AGGRESSIVE = 4, /* _NORMAL + move locked blocks where possible */ MEM_COMPACT_ALL = MEM_COMPACT_NORMAL | MEM_COMPACT_DISCARD | MEM_COMPACT_AGGRESSIVE, MEM_COMPACT_NOT_BLOCKING = 8, /* don't retry if initial allocation fails */ MEM_COMPACT_SHUFFLE = 0x80 /* Move the lowest unlocked block up to the top (space permitting) - for testing */ } mem_compact_mode_t; /* Get default values for memory pool defined in the platform makefile */ extern void mem_get_default_partition(void **mempool_base, uint32_t *mempool_size, void **mempool_handles_base, uint32_t *mempool_handles_size); /* Initialize the memory subsystem, allocating a pool of a given size and with space for the given number of handles. */ extern int mem_init(void *mempool_base, uint32_t mempool_size, void *mempool_handles_base, uint32_t mempool_handles_size); /* Terminate the memory subsystem, releasing the pool. */ extern void mem_term(void); /* The heap is compacted to the maximum possible extent. If (mode & MEM_COMPACT_DISCARD) is non-zero, all discardable, unlocked, and unretained MEM_HANDLE_Ts are resized to size 0. If (mode & MEM_COMPACT_AGGRESSIVE) is non-zero, all long-term block owners (which are obliged to have registered a callback) are asked to unlock their blocks for the duration of the compaction. */ extern void mem_compact(mem_compact_mode_t mode); /****************************************************************************** Movable memory core API ******************************************************************************/ /* A MEM_HANDLE_T refers to a movable block of memory. The only way to get a MEM_HANDLE_T is to call mem_alloc. The MEM_HANDLE_T you get is immutable and remains valid until its reference count reaches 0. A MEM_HANDLE_T has an initial reference count of 1. This can be incremented by calling mem_acquire and decremented by calling mem_release. */ /* MEM_ZERO_SIZE_HANDLE is a preallocated handle to a zero-size block of memory MEM_EMPTY_STRING_HANDLE is a preallocated handle to a block of memory containing the empty string MEM_HANDLE_INVALID is the equivalent of NULL for MEM_HANDLE_Ts -- no valid MEM_HANDLE_T will ever equal MEM_HANDLE_INVALID. */ typedef enum { /* enum to get stricter type checking on msvc */ #ifdef MEM_USE_SMALL_ALLOC_POOL MEM_ZERO_SIZE_HANDLE = 0x80000000, MEM_EMPTY_STRING_HANDLE = 0x80000001, #else MEM_ZERO_SIZE_HANDLE = 1, MEM_EMPTY_STRING_HANDLE = 2, #endif MEM_HANDLE_INVALID = 0, MEM_HANDLE_FORCE_32BIT = 0x80000000, // deprecated - for backward compatibility MEM_INVALID_HANDLE = MEM_HANDLE_INVALID } MEM_HANDLE_T; /* Flags are set once in mem_alloc -- they do not change over the lifetime of the MEM_HANDLE_T. */ typedef enum { MEM_FLAG_NONE = 0, /* If a MEM_HANDLE_T is discardable, the memory manager may resize it to size 0 at any time when it is not locked or retained. */ MEM_FLAG_DISCARDABLE = 1 << 0, /* MEM_FLAG_RETAINED should only ever be used when passing flags to mem_alloc. If it is set, the initial retain count is 1, otherwise it is 0. */ MEM_FLAG_RETAINED = 1 << 9, /* shared with MEM_FLAG_ABANDONED. only used when passing flags to mem_alloc */ /* Block must be kept within bottom 256M region of the relocatable heap. Specifying this flag means that an allocation will fail if the block cannot be allocated within that region, and the block will not be moved out of that range. (This is to support memory blocks used by the codec cache, which must have same top 4 bits; see HW-3058) This flag is ignored on non-VideoCore platforms. */ MEM_FLAG_LOW_256M = 1 << 1, /* Define a mask to extract the memory alias used by the block of memory. */ MEM_FLAG_ALIAS_MASK = 3 << 2, /* If a MEM_HANDLE_T is allocating (or normal), its block of memory will be accessed in an allocating fashion through the cache. */ MEM_FLAG_NORMAL = 0 << 2, MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL, /* If a MEM_HANDLE_T is direct, its block of memory will be accessed directly, bypassing the cache. */ MEM_FLAG_DIRECT = 1 << 2, /* If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a non-allocating fashion through the cache. */ MEM_FLAG_COHERENT = 2 << 2, /* If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by the VPU in a fashion which is allocating in L2, but only coherent in L1. */ MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT), /* If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than MEM_HANDLE_INVALID on allocation and resize up. */ MEM_FLAG_ZERO = 1 << 4, /* If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value (either zero, or all 1's) on allocation. */ MEM_FLAG_NO_INIT = 1 << 5, /* The INIT flag is a placeholder, designed to make it explicit that initialisation is required, and to make it possible to change the sense of this bit at a later date. */ MEM_FLAG_INIT = 0 << 5, /* Hints. */ MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */ MEM_FLAG_HINT_ALL = 0xc0, MEM_FLAG_USER = 1 << 7, MEM_FLAG_HINT_GROW = 1 << 7, /* Likely to grow in size over time. If this flag is specified, MEM_FLAG_RESIZEABLE must also be. */ MEM_FLAG_UNUSED = 1 << 7, /* If a MEM_HANDLE_T is to be resized with mem_resize, this flag must be present. This flag prevents things from being allocated out of the small allocation pool. */ MEM_FLAG_RESIZEABLE = 1 << 8, /* If the ABANDONED flag is set, because mem_abandon was called when the lock count was zero, the contents are undefined. This flag is cleared by mem_lock. Automatically set for blocks allocated with MEM_FLAG_NO_INIT. */ MEM_FLAG_ABANDONED = 1 << 9, /* shared with MEM_FLAG_RETAINED. never used when passing flags to mem_alloc */ /* There is currently room in MEM_HEADER_X_T for 10 flags */ MEM_FLAG_ALL = 0x3ff } MEM_FLAG_T; /* A MEM_HANDLE_T may optionally have a terminator. This is a function that will be called just before the MEM_HANDLE_T becomes invalid: see mem_release. */ typedef void (*MEM_TERM_T)(void *, uint32_t); /* A common way of storing a MEM_HANDLE_T together with an offset into it. */ typedef struct { MEM_HANDLE_T mh_handle; uint32_t offset; } MEM_HANDLE_OFFSET_T; /* Attempts to allocate a movable block of memory of the specified size and alignment. size may be 0. A MEM_HANDLE_T with size 0 is special: see mem_lock/mem_unlock. mode specifies the types of compaction permitted, including MEM_COMPACT_NONE. Preconditions: - align is a power of 2. - flags only has set bits within the range specified by MEM_FLAG_ALL. - desc is NULL or a pointer to a null-terminated string. - the caller of this function is contracted to later call mem_release (or pass such responsibility on) if we don't return MEM_HANDLE_INVALID Postconditions: If the attempt succeeds: - A fresh MEM_HANDLE_T referring to the allocated block of memory is returned. - The MEM_HANDLE_T is unlocked, without a terminator, and has a reference count of 1. - If MEM_FLAG_RETAINED was specified, the MEM_HANDLE_T has a retain count of 1, otherwise it is unretained. - If the MEM_FLAG_ZERO flag was specified, the block of memory is set to 0. Otherwise, each aligned word is set to MEM_HANDLE_INVALID. If the attempt fails: - MEM_HANDLE_INVALID is returned. */ extern MEM_HANDLE_T mem_alloc_ex( uint32_t size, uint32_t align, MEM_FLAG_T flags, const char *desc, mem_compact_mode_t mode); #define mem_alloc(s,a,f,d) mem_alloc_ex(s,a,f,d,MEM_COMPACT_ALL) #define MEM_WRAP_HACK #ifdef MEM_WRAP_HACK extern MEM_HANDLE_T mem_wrap(void *p, uint32_t size, uint32_t align, MEM_FLAG_T flags, const char *desc); typedef void (*MEM_WRAP_TERM_T)(void * /*priv*/, MEM_HANDLE_T /*term_handle*/, void * /*p*/, int /*size*/); /* int mem_wrap_set_on_term(MEM_HANDLE_T handle, MEM_WRAP_TERM_T term_cb, void *cb_priv); Adds an additional release callback for wrapped MEM_HANDLE_T. This allows the underlying allocator to release the wrapped memory. The MEM_HANDLE_T must NOT be accessed with mem_lock/mem_acquire etc from the callback context as the handle is already partially released. */ extern int mem_wrap_set_on_term(MEM_HANDLE_T handle, MEM_WRAP_TERM_T term_cb, void *cb_priv); #endif /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The reference count of the MEM_HANDLE_T is incremented. */ extern void mem_acquire( MEM_HANDLE_T handle); /* Calls mem_acquire and also calls mem_retain if the handle is discardable. */ extern void mem_acquire_retain(MEM_HANDLE_T handle); /* If the reference count of the MEM_HANDLE_T is 1 and it has a terminator, the terminator is called with a pointer to and the size of the MEM_HANDLE_T's block of memory (or NULL/0 if the size of the MEM_HANDLE_T is 0). The MEM_HANDLE_T may not be used during the call. Preconditions: - handle is a valid MEM_HANDLE_T. - If its reference count is 1, it must not be locked or retained. Postconditions: If the reference count of the MEM_HANDLE_T was 1: - The MEM_HANDLE_T is now invalid. The associated block of memory has been freed. Otherwise: - The reference count of the MEM_HANDLE_T is decremented. */ extern void mem_release( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: If the reference count of the MEM_HANDLE_T was 1: - false is returned. - The reference count of the MEM_HANDLE_T is still 1. Otherwise: - true is returned. - The reference count of the MEM_HANDLE_T is decremented. */ extern int mem_try_release( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The total space used by the MEM_HANDLE_T is returned (this includes the header, the actual block, and padding). - sum_over_handles(mem_get_space(handle)) + mem_get_free_space() is constant over the lifetime of the pool. */ extern uint32_t mem_get_space( MEM_HANDLE_T handle); /* uint32_t mem_get_size(MEM_HANDLE_T handle); The size of the MEM_HANDLE_T's block of memory is returned. This is consistent with the size requested in a mem_alloc call. Implementation notes: - Preconditions: handle is not MEM_HANDLE_INVALID Postconditions: result <= INT_MAX Invariants preserved: - */ extern uint32_t mem_get_size( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The minimum required alignment of the MEM_HANDLE_T's block of memory (as passed to mem_alloc) is returned. - */ extern uint32_t mem_get_align( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's flags (as passed to mem_alloc) are returned. */ extern MEM_FLAG_T mem_get_flags( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's reference count is returned. */ extern uint32_t mem_get_ref_count( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's lock count is returned. */ extern uint32_t mem_get_lock_count( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's retain count is returned. */ extern uint32_t mem_get_retain_count( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's description string is returned. */ extern const char *mem_get_desc( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. - desc is NULL or a pointer to a null-terminated string. Postconditions: - The MEM_HANDLE_T's description is set to desc. */ extern void mem_set_desc( MEM_HANDLE_T handle, const char *desc); extern void mem_set_desc_vprintf( MEM_HANDLE_T handle, const char *fmt, va_list ap); extern void mem_set_desc_printf( MEM_HANDLE_T handle, const char *fmt, ...); /* void mem_set_term(MEM_HANDLE_T handle, MEM_TERM_T term) The MEM_HANDLE_T's terminator is set to term (if term was NULL, the MEM_HANDLE_T no longer has a terminator). The MEM_HANDLE_T's terminator is called just before the MEM_HANDLE_T becomes invalid: see mem_release. Preconditions: handle is a valid handle to a (possibly uninitialised or partially initialised*) object of type X This implies mem_get_size(handle) == sizeof(type X) memory management invariants for handle are satisfied term must be NULL or a pointer to a function compatible with the MEM_TERM_T type: void *term(void *v, uint32_t size) if term is not NULL, its precondition must be no stronger than the following: is only called from memory manager internals during destruction of an object of type X v is a valid pointer to a (possibly uninitialised or partially initialised*) object of type X memory management invariants for v are satisfied size == sizeof(type X) if term is not NULL, its postcondition must be at least as strong as the following: Frees any references to resources that are referred to by the object of type X Postconditions: - */ extern void mem_set_term( MEM_HANDLE_T handle, MEM_TERM_T term); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's terminator is returned, or NULL if there is none. */ extern MEM_TERM_T mem_get_term( MEM_HANDLE_T handle); /* void mem_set_user_flag(MEM_HANDLE_T handle, int flag) Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: - The MEM_HANDLE_T's user flag is set to 0 if flag is 0, or to 1 otherwise. */ extern void mem_set_user_flag( MEM_HANDLE_T handle, int flag); /* Attempts to resize the MEM_HANDLE_T's block of memory. The attempt is guaranteed to succeed if the new size is less than or equal to the old size. size may be 0. A MEM_HANDLE_T with size 0 is special: see mem_lock/mem_unlock. mode specifies the types of compaction permitted, including MEM_COMPACT_NONE. Preconditions: - handle is a valid MEM_HANDLE_T. - It must not be locked. Postconditions: If the attempt succeeds: - true is returned. - The MEM_HANDLE_T's block of memory has been resized. - The contents in the region [0, min(old size, new size)) are unchanged. If the MEM_HANDLE_T is zero'd, the region [min(old size, new size), new size) is set to 0. Otherwise, each aligned word in the region [min(old size, new size), new size) is set to MEM_HANDLE_INVALID. If the attempt fails: - false is returned. - The MEM_HANDLE_T is still valid. - Its block of memory is unchanged. */ extern int mem_resize_ex( MEM_HANDLE_T handle, uint32_t size, mem_compact_mode_t mode); #define mem_resize(h,s) mem_resize_ex(h,s,MEM_COMPACT_ALL) /* A MEM_HANDLE_T with a lock count greater than 0 is considered to be locked and may not be moved by the memory manager. Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: If the MEM_HANDLE_T's size is 0: - NULL is returned. - The MEM_HANDLE_T is completely unchanged. Otherwise: - A pointer to the MEM_HANDLE_T's block of memory is returned. The pointer is valid until the MEM_HANDLE_T's lock count reaches 0. - The MEM_HANDLE_T's lock count is incremented. - Clears MEM_FLAG_ABANDONED. */ /*@null@*/ extern void *mem_lock( MEM_HANDLE_T handle); /* Lock a number of memory handles and store the results in an array of pointers. May be faster than calling mem_lock repeatedly as we only need to acquire the memory mutex once. For convenience you can also pass invalid handles in, and get out either null pointers or valid pointers (depending on the associated offset field) Preconditions: pointers is a valid pointer to n elements handles is a valid pointer to n elements For all 0 <= i < n - handles[i].mh_handle is MEM_HANDLE_INVALID or a valid MEM_HANDLE_T. - If handles[i] != MEM_HANDLE_INVALID then handles[i].offset <= handles[i].size Postconditions: For all 0 <= i < n If handles[i] == MEM_HANDLE_INVALID: - pointers[i] is set to offsets[i] Else if handles[i].mh_handle.size == 0: - pointers[i] is set to 0. - handles[i].mh_handle is completely unchanged. Otherwise: - pointers[i] is set to a pointer which is valid until handles[i].lockcount reaches 0 - pointers[i] points to a block of size (handles[i].size - handles[i].offset) - handles[i].mh_handle.lockcount is incremented. - MEM_FLAG_ABANDONED is cleared in handles[i].mh_handle.x.flags */ extern void mem_lock_multiple( void **pointers, MEM_HANDLE_OFFSET_T *handles, uint32_t n); /* Preconditions: - handle is a valid MEM_HANDLE_T. - If its size is not 0, it must be locked. Postconditions: If the MEM_HANDLE_T's size is 0: - The MEM_HANDLE_T is completely unchanged. Otherwise: - The MEM_HANDLE_T's lock count is decremented. */ extern void mem_unlock( MEM_HANDLE_T handle); /* Unlock a number of memory handles. May be faster than calling mem_unlock repeatedly as we only need to acquire the memory mutex once. Preconditions: pointers is a valid pointer to n elements handles is a valid pointer to n elements For all 0 <= i < n - handles[i].mh_handle is a valid MEM_HANDLE_T. - If handles[i].mh_handle.size is not 0, it must be locked. Postconditions: For all 0 <= i < n If handles[i] == MEM_HANDLE_INVALID or handles[i].mh_handle.size == 0: - handles[i].mh_handle is completely unchanged. Otherwise: - handles[i].mh_handle.lockcount is decremented. */ extern void mem_unlock_multiple( MEM_HANDLE_OFFSET_T *handles, uint32_t n); /* Like mem_unlock_multiple, but will unretain handles if they are discardable. Also releases handles. */ extern void mem_unlock_unretain_release_multiple( MEM_HANDLE_OFFSET_T *handles, uint32_t n); /* Like mem_unlock_unretain_release_multiple, but without the unlocking. Also releases handles. */ extern void mem_unretain_release_multiple( MEM_HANDLE_OFFSET_T *handles, uint32_t n); /* Preconditions: - handle is a valid MEM_HANDLE_T. Postconditions: If the MEM_HANDLE_T is not a small handle: - Sets MEM_FLAG_ABANDONED, which causes the data content to become undefined when the lock count reaches zero. - Sets MEM_FLAG_NO_INIT. Otherwise: - Does nothing. */ extern void mem_abandon( MEM_HANDLE_T handle); /* A discardable MEM_HANDLE_T with a retain count greater than 0 is considered retained and may not be discarded by the memory manager. Preconditions: - handle is a valid MEM_HANDLE_T. - It must be discardable. Postconditions: - 0 is returned if the size of the MEM_HANDLE_T's block of memory is 0, otherwise 1 is returned. - The retain count of the MEM_HANDLE_T is incremented. */ extern int mem_retain( MEM_HANDLE_T handle); /* Preconditions: - handle is a valid MEM_HANDLE_T. - It must be retained. Postconditions: - The retain count of the MEM_HANDLE_T is decremented. */ extern void mem_unretain( MEM_HANDLE_T handle); /* A version of mem_lock which adds an indication that an aggressive compaction should not wait for the block to be unlocked. */ extern void *mem_lock_perma( MEM_HANDLE_T handle); /* A version of mem_unlock which removes an indication that an aggressive compaction should not wait for the block to be unlocked. */ extern void mem_unlock_perma( MEM_HANDLE_T handle); /****************************************************************************** Legacy memory blocks API ******************************************************************************/ /* Allocate a fixed-size block. The size of legacy blocks is a constant for the life of the memory subsystem (and may be a build-time constant). Legacy blocks are stored in offline chunks, so this is a veneer over mem_offline. Preconditions: - flags must specify a memory alias o MEM_FLAG_NORMAL o MEM_FLAG_COHERENT o MEM_FLAG_DIRECT o MEM_FLAG_L1_NONALLOCATING (VC4 only) - other permitted flags o MEM_FLAG_LOW_256M Postconditions: If the attempt succeeds: - A pointer to the legacy block is returned. If the attempt fails: - NULL (0) is returned. */ extern void *mem_alloc_legacy_ex(MEM_FLAG_T flags); #define mem_alloc_legacy() mem_alloc_legacy_ex(MEM_FLAG_NORMAL) /* Free a previously-allocated fixed-size block. Preconditions: - ptr must point to a block previously returned by mem_alloc_legacy. Postconditions: - None. */ extern void mem_free_legacy(void *ptr); /* If count_max is positive then it sets a maximum number of legacy blocks which can be allocated, otherwise the maximum possible (capped at 32) are allowed. Preconditions: - align must be a power of two. - There must be no allocated legacy blocks. Postconditions: If the attempt succeeds: - Returns the maximum number of legacy blocks, assuming no other allocations. If the attempt fails: - Returns -1. */ extern int mem_init_legacy(uint32_t size, uint32_t align, int count_max); /* Preconditions: - None. Postconditions: - Returns the size of the legacy blocks. */ extern uint32_t mem_get_legacy_size(void); /* Preconditions: - None. Postconditions: - Returns the alignment of the legacy blocks. */ extern uint32_t mem_get_legacy_align(void); /****************************************************************************** Offline chunks API ******************************************************************************/ /* Mark a contiguous chunk as being "offline". This is similar to a regular block which is allocated and locked, except that chunks are always a multiple of MEM_CHUNK_SIZE, and have no headers. Also, the allocator prefers chunks at higher addresses. mode specifies the types of compaction permitted, including MEM_COMPACT_NONE. If no contiguous range of chunks can be found, the heap will be compacted before retrying. Preconditions: - size must be an integer multiple of MEM_CHUNK_SIZE - flags must specify a memory alias o MEM_FLAG_NORMAL o MEM_FLAG_COHERENT o MEM_FLAG_DIRECT o MEM_FLAG_L1_NONALLOCATING (VC4 only) - other permitted flags o MEM_FLAG_LOW_256M Postconditions: If the attempt succeeds: - A pointer to the offline block is returned. If the attempt fails: - NULL (0) is returned. */ extern void *mem_offline(uint32_t size, MEM_FLAG_T flags, mem_compact_mode_t mode); extern int mem_offline_chunks(uint32_t num_chunks, MEM_FLAG_T flags, mem_compact_mode_t mode); /* Free a previously-allocated fixed-size block. Note that it is legal to take a large chunk offline and then bring a portion of it back online. Preconditions: - ptr must point to a block previously returned by mem_alloc_legacy, or a chunk-aligned section thereof. - size must be an integer multiple of MEM_CHUNK_SIZE. Postconditions: - None. */ extern void mem_online(void *ptr, uint32_t size); extern void mem_online_chunks(int chunk, int num_chunks); /* Retrieves various statistics about the heap chunks. Note that _used + _available may not equal _total in the case of overlapping areas. */ extern void mem_get_chunk_stats(uint32_t *total, uint32_t *legacy_used, uint32_t *legacy_available, uint32_t *legacy_total, uint32_t *offline_used, uint32_t *offline_available, uint32_t *offline_total); /****************************************************************************** Long-term lock owners' API ******************************************************************************/ typedef enum { /* An aggressive compaction is beginning. Any long-term locks should be released. */ MEM_CALLBACK_REASON_UNLOCK, /* An aggressive compaction has completed. Any long-term locks can be reclaimed. */ MEM_CALLBACK_REASON_RELOCK, /* The total amount of free memory has fallen below the threshold * defined by mem_set_low_memory_threshold. * To avoid repeated callbacks this callback is only invoked for the * allocation that caused the free memory threshold to be crossed. * * Caveats * The internal overheads of the heap are ignored. * Small allocs are ignored. * The available memory may be fragmented. */ MEM_CALLBACK_REASON_LOW_MEMORY, MEM_CALLBACK_REASON_MAX } mem_callback_reason_t; typedef void (*mem_callback_func_t)(mem_callback_reason_t reason, uintptr_t context); /* Returns 1 on success, 0 on failure. */ extern int mem_register_callback(mem_callback_func_t func, uintptr_t context); /* Defines the threshold in bytes at which the * MEM_CALLBACK_REASON_LOW_MEMORY will be invoked. */ extern void mem_set_low_mem_threshold(uint32_t threshold); extern void mem_unregister_callback(mem_callback_func_t func, uintptr_t context); /****************************************************************************** Compaction notification API ******************************************************************************/ /** Type of compaction operation. */ typedef enum { /* A compaction is about to begin. */ MEM_COMPACT_OP_BEGIN, /* A compaction has just ended. */ MEM_COMPACT_OP_END, MEM_COMPACT_OP_MAX } mem_compact_op_t; /** * Compaction notification function. Called when a relocatable heap compaction is * about to begin or has just ended. * This gives the callback the ability to delay the start of compaction. * Will be invoked independently from those registered via mem_register_callback(). * @param op Compaction operation. * @param context Context passed in compaction registration. * @param retries Number of retries remaining (assuming at least one callback returns non-zero). * 0 means the last call for this compaction. * @return non-zero to delay compaction; 0 if ok for compaction to start. */ typedef int (*mem_compact_cb_t)(mem_compact_op_t op, uintptr_t context, int retries); /** * Register a callback function to be invoked at the start and end of every relocatable * heap compaction. * @return 1 on success; 0 on failure. * @note Do not call from a compaction notification function. */ int mem_register_compact_cb(mem_compact_cb_t func, uintptr_t context); /** * Unregister a callback registered via mem_register_compact_cb(). * @note Do not call from a compaction notification function. */ void mem_unregister_compact_cb(mem_compact_cb_t func, uintptr_t context); /****************************************************************************** Movable memory helpers ******************************************************************************/ /* Enable/disable the memory shuffler. Only has effect if the memory shuffler has been included in the build (by setting the MEM_SHUFFLE define). The shuffler will be started and enabled by default. A zero value of enable will disable compactions. A non-zero value for enable will enable compactions. */ void rtos_common_mem_shuffle_enable(int enable); extern MEM_HANDLE_T mem_strdup_ex( const char *str, mem_compact_mode_t mode); #define mem_strdup(str) mem_strdup_ex((str),MEM_COMPACT_ALL) /* Allocate a new buffer (with a single reference) with the same size and contents as the supplied buffer. This is intended to be used for data buffers rather than structures (structures would require adding a reference to each of their member handles). Hence the returned buffer does not have a terminator and we require that the supplied buffer doesn't have one either. At present, *all* flags are carried across to the new buffer. I'm not sure if this is wise. TODO: decide which we should allow, and whether any extra ones should be passed as arguments. Alignment is carried across too. A valid MEM_HANDLE_T must be passed. It must have no terminator. */ extern MEM_HANDLE_T mem_dup_ex( MEM_HANDLE_T handle, const char *desc, mem_compact_mode_t mode); #define mem_dup(handle,desc) mem_dup_ex((handle),(desc),MEM_COMPACT_ALL) extern void mem_print_state(void); extern void mem_print_small_alloc_pool_state(void); extern uint32_t mem_debug_get_alloc_count(void); /* Retrieves various statistics about the allocated blocks. */ extern void mem_get_stats(uint32_t *blocks, uint32_t *bytes, uint32_t *locked); /* Returns the size of the pool. */ extern uint32_t mem_get_total_space(void); /* Returns the size of the largest possible allocation, assuming no fragmentation. */ extern uint32_t mem_get_free_space(void); /* Returns the actual amount of free space. You won't actually be able to allocate anything this big due to overhead. If the heap is empty, the return value should equal mem_get_total_space() */ extern uint32_t mem_get_actual_free_space(void); /* Returns the free space statistics. NULL pointers will not be written to. */ extern void mem_get_free_space_stats(uint32_t *total, uint32_t *max, uint32_t *count); /* Returns the current estimate of available free space. Quicker than mem_get_free_space. */ extern uint32_t mem_get_low_mem_available_space(void); /* Check the internal consistency of the heap. A corruption will result in a failed assert, which will cause a breakpoint in a debug build. If MEM_FILL_FREE_SPACE is defined for the build, then free space is also checked for corruption; this has a large performance penalty, but can help to track down random memory corruptions. Note that defining MEM_AUTO_VALIDATE will enable the automatic validation of the heap at key points - allocation, deallocation and compaction. Preconditions: - None. Postconditions: - None. */ extern void mem_validate(void); /* Attempts to allocate a movable block of memory of the same size and alignment as the specified structure type. Implementation Notes: The returned object obeys the invariants of the memory subsystem only. Invariants of the desired structure type may not yet be obeyed. The memory will be filled such that any handles in the structure would be interpreted as MEM_HANDLE_INVALID Preconditions: STRUCT is a structure type the caller of this macro is contracted to later call mem_release (or pass such responsibility on) if we don't return MEM_HANDLE_INVALID Postconditions: If the attempt succeeds: - A fresh MEM_HANDLE_T referring to the allocated block of memory is returned. - The MEM_HANDLE_T is unlocked, unretained, without a terminator, and has a reference count of 1. If the attempt fails: - MEM_HANDLE_INVALID is returned. */ #define MEM_ALLOC_STRUCT_EX(STRUCT, mode) mem_alloc_ex(sizeof(STRUCT), RCM_ALIGNOF(STRUCT), MEM_FLAG_NONE, #STRUCT, mode) #define MEM_ALLOC_STRUCT(STRUCT) mem_alloc(sizeof(STRUCT), RCM_ALIGNOF(STRUCT), MEM_FLAG_NONE, #STRUCT) /* Find out if a memory pointer is within the relocatable pool (excluding legacy blocks). Returns non-zero if it is. */ extern int mem_is_relocatable(const void *ptr); #ifndef VCMODS_LCC // LCC doesn't support inline so cannot define these functions in a header file static RCM_INLINE void mem_assign(MEM_HANDLE_T *x, MEM_HANDLE_T y) { if (y != MEM_HANDLE_INVALID) mem_acquire(y); if (*x != MEM_HANDLE_INVALID) mem_release(*x); *x = y; } /* MEM_ASSIGN(x, y) Overwrite a handle with another handle, managing reference counts appropriately Implementation notes: Always use the macro version rather than the inline function above Preconditions: each of x and y is MEM_HANDLE_INVALID or a handle to a block with non-zero ref_count if x is not MEM_HANDLE_INVALID and x != y and x.ref_count is 1, x.lock_count is zero is y is not MEM_HANDLE_INVALID there must at some point be a MEM_ASSIGN(x, MEM_HANDLE_INVALID) Postconditions: Invariants preserved: */ #define MEM_ASSIGN(x, y) mem_assign(&(x), (y)) /*@null@*/ static RCM_INLINE void * mem_maybe_lock(MEM_HANDLE_T handle) { if (handle == MEM_HANDLE_INVALID) return 0; else return mem_lock(handle); } static RCM_INLINE void mem_maybe_unlock(MEM_HANDLE_T handle) { if (handle != MEM_HANDLE_INVALID) mem_unlock(handle); } #endif extern void mem_assign_null_multiple( MEM_HANDLE_OFFSET_T *handles, uint32_t n); struct MEM_MANAGER_STATS_T; extern void mem_get_internal_stats(struct MEM_MANAGER_STATS_T *stats); extern int mem_handle_acquire_if_valid(MEM_HANDLE_T handle); void mem_compact_task_enable(int enable); /****************************************************************************** API of memory access control using sandbox regions Users take the responsibility of assuring thread-safety ******************************************************************************/ #undef MEM_ACCESS_CTRL #ifdef MEM_ACCESS_CTRL # if defined( NDEBUG ) || !defined( __BCM2708__ ) || defined( __BCM2708A0__ ) # error "BCM2708b0 or better needed" # endif #define MEM_SSSR_PRIV_SECURE (0x18) #define MEM_SSSR_PRIV_SUPER (0x08) #define MEM_SSSR_PRIV_USER (0x00) #define MEM_SSSR_READ (0x04) #define MEM_SSSR_WRITE (0x02) #define MEM_SSSR_EXECUTE (0x01) #define MEM_ACCESSCTRL_BLOCKS_MAX (7) // Sandbox 0 is reserved for .text typedef struct { uint32_t start; uint32_t size; uint32_t flags; } MEM_ACCESSCTRL_BLOCK; void mem_clr_accessctrl(); void mem_set_accessctrl(MEM_ACCESSCTRL_BLOCK *blocks, uint32_t n); #define MEM_CLR_ACCESSCTRL mem_clr_accessctrl #define MEM_SET_ACCESSCTRL mem_set_accessctrl #else #define MEM_CLR_ACCESSCTRL() ((void)0) #define MEM_SET_ACCESSCTRL(BLOCKS, NUM) ((void)0) #endif //#ifdef MEM_ACCESS_CTRL #endif