Customize Memory Management

There are two independent methods to customize memory management in the Cafe OS.

The following describes both methods.

Customizing the Cafe OS MEM Arenas

When it is starting, the Cafe OS needs to load the DATA segment for the application (e.g. global data) in the memory space for the application. To do this, the OS uses an OS defined heap that is created in coreinit, for the MEM 2 Arena, often called the default heap. After the DATA segments are allocated, the Cafe OS loader can finish the task of making the application and its dependent RPLs ready for execution. Before any application code is executed, the Cafe OS provides the application an opportunity to call a pre-initialization function, __preinit_user, to customize the unused MEM 2 Arena memory and perform any other initializations that are needed before any application entry points are called and before any C++ static initializers run.

The __preinit_user function allows you to do the following:

The following are restrictions that __preinit_user must conform to:

Important considerations regarding the state of the load process when __preinit_user is called:

Creating your own default heap is one of the most common tasks that __preinit_user is used to perform. However, you may use __preinit_user even if you do not need to create a custom default heap. The following demo demonstrates how to create your own default heap:

$CAFE_ROOT:/system/src/demo/examplemake/cafe_sdk/replace_core_user

It also be demonstrates how to set up __preinit_user if you do not want to create your own default heap. For either task, the first step is to add __preinit_user.

Adding a Pre-Initialization Function

To add __preinit_user

  1. Put __preinit_user in your RPX export file, along with the other functions.
  2. In one of your RPX source files, add the following include file:
    #include <cafe/mem.h>
    
    mem.h includes defaultHeap.h, which has the prototype for __preinit_user.
  3. Create your version of __preinit_user. If you are not changing any of the heaps, do not modify any of the input parameters. If you are changing some of the heaps, see below.
  4. If you want to debug __preinit_user, pass -j to caferun or set the environment variable CAFE_DEBUG_PREINIT to 1.

Customizing the Default Heap

See the following demo:

$CAFE_ROOT:/system/src/demo/examplemake/cafe_sdk/replace_core_user/cu_defheap.c

First, define some functions to handle allocation and deallocation, and to initialize a heap. This demo is calling Cafe OS memory functions, but you may write your own memory management routines.

MEMHeapHandle CoreUser_gDefHeapHandle = MEM_HEAP_INVALID_HANDLE;

static void* sMEMAllocFromDefaultHeap (u32 size)
{
    return MEMAllocFromExpHeapEx(CoreUser_gDefHeapHandle, size, PPC_IO_BUFFER_ALIGN);
}

static void* sMEMAllocFromDefaultHeapEx (u32 size, int alignment)
{
    return MEMAllocFromExpHeapEx(CoreUser_gDefHeapHandle, size, alignment);
}

static void sMEMFreeToDefaultHeap (void* memBlock)
{
    MEMFreeToExpHeap(CoreUser_gDefHeapHandle, memBlock);
}

static MEMHeapHandle sMEMInitDefaultHeap(void* startAddress, u32 size)
{
    CoreUser_gDefHeapHandle = MEMCreateExpHeapEx(startAddress, size, MEM_HEAP_OPT_THREAD_SAFE);
    return CoreUser_gDefHeapHandle;
}

The next function to define is __preinit_user. The comments are self-explanatory. Note that the call to OSReport and OSSetThreadName are not necessary for your implementation.

This code assigns new values to the following function pointers:

These function pointers are exported from coreinit. If you want to customize the default heap, redefine these function pointers to point to your routines.

void   preinit_user(MEMHeapHandle *aMEM1_heap, MEMHeapHandle *aMEMFG_heap, MEMHeapHandle
*aMEM2_heap)
{
    u32 freeBytes;
    void * ptr;
    MEMHeapHandle defaultHeap;

    /* NO C++ Initializers have been run yet here */

    OSReport("DEMO: Replace coreinit.rpl's default heap in the RPX.\n");

    /* set the name of the default thread on Core 1 to something specific (used by test to
       confirm this code run) */
    OSSetThreadName(OSGetDefaultThread(1), "core_user_overridden");

    /* MEM_ARENA_1 and MEM_ARENA_FG heaps remain as created in coreinit.rpl.	*/
    /* The existing MEM_ARENA_2 heap allocations are for the data segments of	*/
    /* RPLs that were loaded with the RPX. They are 'freed' at game termination.	*/
    /* We allocate a pointer for the remaining memory and create a new heap at that */

    /* address.	                                                                */

    /* get the heap handle of the MEM_ARENA_2 */
    defaultHeap = *aMEM2_heap;
    /* MEMGetAllocatableSizeForExpHeap uses a 4 byte alignment; we need to allocate with this
       same alignment */
    /* if you want a larger alignment use MEMGetAllocatableSizeForExpHeapEx instead */
    freeBytes = MEMGetAllocatableSizeForExpHeap(defaultHeap);
    /* Notice that we are allocating from the original coreinit.rpl default heap */
    ptr = MEMAllocFromDefaultHeapEx(freeBytes, 4);

    /* the following function pointer variables can be reassigned by the RPX */
    MEMAllocFromDefaultHeap = sMEMAllocFromDefaultHeap;
    MEMAllocFromDefaultHeapEx = sMEMAllocFromDefaultHeapEx;
    MEMFreeToDefaultHeap = sMEMFreeToDefaultHeap;

    /* create the heap from this file's implementation of sMEMInitDefaultHeap */
    *aMEM2_heap = sMEMInitDefaultHeap(ptr, freeBytes);
    /* MEMSetBaseHeapHandle can only be called once per arena; if you need to access the new
       heap, use gAppHeapHandle */


    OSDynLoad_SetAllocator(CoreUser_DynLoad_DefaultAlloc, CoreUser_DynLoad_DefaultFree);

    /* any other USER pre-inits should go here */
}

__preinit_user allocates all of the remaining memory in the MEM 2 Arena to a single pointer, and then uses that pointer to create a new heap. It also establishes the allocator, via OSDynLoad_SetAllocator, used by the dynamic load system.

The allocator is used when an RPL is loaded and space needs to be reserved for its read/write data sections. See $CAFE_ROOT:/system/src/demo/examplemake/cafe_sdk/replace_core_user/cu_dynload.c:

#define MIN_DEFAULTHEAP_ALIGNMENT	4

int CoreUser_DynLoad_DefaultAlloc(int aMemBytes, int aMemAlign, void **appRetPtr)
{
    if (!appRetPtr)
	return OSDYNLOAD_ERR_ALLOCATOR_PTR_BAD;

    if (aMemAlign < 0)
    {
	if (aMemAlign > -MIN_DEFAULTHEAP_ALIGNMENT)
	    aMemAlign = -MIN_DEFAULTHEAP_ALIGNMENT;
    }
    else
    {
	if (aMemAlign < MIN_DEFAULTHEAP_ALIGNMENT)
	    aMemAlign = MIN_DEFAULTHEAP_ALIGNMENT;
    }

    *appRetPtr = MEMAllocFromDefaultHeapEx(aMemBytes, aMemAlign);
    if (*appRetPtr)
	return 0;

    return OSDYNLOAD_ERR_MEMORY_ALLOCATION_FAILURE;
}

void CoreUser_DynLoad_DefaultFree(void *pAddr)
{

    MEMFreeToDefaultHeap(pAddr);
}

The code in cu_dynload.c is not exported. Cafe OS or your application accesses these functions with OSDynLoad_GetAllocator.

Customizing the other MEM Arena Heaps

__preinit_user can customize any of the MEM Arena heaps. If you look at the deprecated $CAFE_ROOT:/system/src/demo/examplemake/cafe_sdk/replace_core_user/cu_entry.c, you will find code that accesses the other MEM Arena heaps. You may use that code to help customize the other MEM Arena heaps.

Customizing the sbrk and __ghs_alloc

sbrk and __ghs_alloc are in $CAFE_ROOT:/system/src/lib/libsys/ghsheap.c and are provided to facilitate the GHS runtime and malloc. You may modify them, and then rebuild libsys.a.

Revision History

2014/02/28 Changed MEMInitDefaultHeap to sMEMInitDefaultHeap.
2013/05/08 Automated cleanup pass.
2013/03/20 Converted to HTML.


CONFIDENTIAL