diff --git a/Makefile.ctr b/Makefile.ctr index 3c7a19e7d5..2a61707362 100644 --- a/Makefile.ctr +++ b/Makefile.ctr @@ -30,15 +30,15 @@ APP_USE_SVCHAX = 0 include pkg/ctr/Makefile.cores OBJ := -OBJ += ctr/ctr_system.o -OBJ += ctr/ctr_memory.o -OBJ += ctr/ctr_linear.o -OBJ += ctr/gpu_old.o -OBJ += ctr/exec-3dsx/exec_cia.o \ - ctr/exec-3dsx/exec_3dsx.o \ - ctr/exec-3dsx/mini-hb-menu/launch.o \ - ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.o \ - ctr/exec-3dsx/mini-hb-menu/loaders/hax2.o +OBJ += os/ctr/ctr_system.o +OBJ += os/ctr/ctr_memory.o +OBJ += os/ctr/ctr_linear.o +OBJ += os/ctr/gpu_old.o +OBJ += os/ctr/exec-3dsx/exec_cia.o \ + os/ctr/exec-3dsx/exec_3dsx.o \ + os/ctr/exec-3dsx/mini-hb-menu/launch.o \ + os/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.o \ + os/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.o ifeq ($(APP_BIG_TEXT_SECTION), 1) APP_USE_SVCHAX = 1 @@ -46,7 +46,7 @@ ifeq ($(APP_BIG_TEXT_SECTION), 1) endif ifeq ($(APP_USE_SVCHAX), 1) - OBJ += ctr/ctr_svchax.o + OBJ += os/ctr/ctr_svchax.o endif DEFINES := @@ -208,7 +208,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 CFLAGS += -Werror=implicit-function-declaration ASFLAGS := -g $(ARCH) -O3 -LDFLAGS += -specs=ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +LDFLAGS += -specs=os/ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) CFLAGS += -std=gnu99 CFLAGS += -ffunction-sections -fdata-sections @@ -366,7 +366,7 @@ else endif $(DEVKITTOOLS)/bin/3dsxtool $< $@ $(_3DSXFLAGS) -$(TARGET).elf: ctr/3dsx_custom_crt0.o +$(TARGET).elf: os/ctr/3dsx_custom_crt0.o @$(if $(Q), $(shell echo echo LD $@),) $(Q)$(LD) $(LDFLAGS) $(OBJ) $(LIBDIRS) $(LIBS) -o $@ $(NM) -CSn $@ > $(notdir $*.lst) @@ -393,8 +393,7 @@ clean: $(Q)rm -f $(TARGET).smdh $(Q)rm -f $(TARGET).bnr $(Q)rm -f $(TARGET).icn - $(Q)rm -f ctr/ctr_config_*.o - $(Q)rm -f ctr/3dsx_custom_crt0.o + $(Q)rm -f os/ctr/3dsx_custom_crt0.o $(Q)rm -f gfx/drivers/ctr_shaders/*.shbin gfx/drivers/ctr_shaders/*.shbin.s .PHONY: clean diff --git a/Makefile.ctr.salamander b/Makefile.ctr.salamander index a336a42982..a14a5058a5 100644 --- a/Makefile.ctr.salamander +++ b/Makefile.ctr.salamander @@ -20,9 +20,9 @@ APP_RSF = pkg/ctr/tools/template.rsf APP_SYSTEM_MODE = 64MB APP_SYSTEM_MODE_EXT = 124MB -OBJ := ctr/ctr_system.o \ - ctr/ctr_memory.o \ - ctr/ctr_linear.o \ +OBJ := os/ctr/ctr_system.o \ + os/ctr/ctr_memory.o \ + os/ctr/ctr_linear.o \ frontend/frontend_salamander.o \ frontend/frontend_driver.o \ frontend/drivers/platform_ctr.o \ @@ -45,11 +45,11 @@ OBJ := ctr/ctr_system.o \ libretro-common/time/rtime.o \ verbosity.o -OBJ += ctr/exec-3dsx/exec_cia.o \ - ctr/exec-3dsx/exec_3dsx.o \ - ctr/exec-3dsx/mini-hb-menu/launch.o \ - ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.o \ - ctr/exec-3dsx/mini-hb-menu/loaders/hax2.o +OBJ += os/ctr/exec-3dsx/exec_cia.o \ + os/ctr/exec-3dsx/exec_3dsx.o \ + os/ctr/exec-3dsx/mini-hb-menu/launch.o \ + os/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.o \ + os/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.o ifeq ($(strip $(DEVKITPRO)),) $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitpro") @@ -110,7 +110,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 CFLAGS += -Werror=implicit-function-declaration ASFLAGS := -g $(ARCH) -O3 -LDFLAGS += -specs=ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +LDFLAGS += -specs=os/ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) CFLAGS += -std=gnu99 @@ -209,7 +209,7 @@ else endif $(DEVKITTOOLS)/bin/3dsxtool $< $@ $(_3DSXFLAGS) -$(TARGET).elf: ctr/3dsx_custom_crt0.o +$(TARGET).elf: os/ctr/3dsx_custom_crt0.o $(LD) $(LDFLAGS) $(OBJ) $(LIBDIRS) $(LIBS) -o $@ $(NM) -CSn $@ > $(notdir $*.lst) @@ -234,7 +234,6 @@ clean: rm -f $(TARGET).smdh rm -f $(TARGET).bnr rm -f $(TARGET).icn - rm -f ctr/ctr_config_*.o - rm -f ctr/3dsx_custom_crt0.o + rm -f os/ctr/3dsx_custom_crt0.o .PHONY: clean diff --git a/audio/drivers/ctr_dsp_audio.c b/audio/drivers/ctr_dsp_audio.c index 59a093707f..9f2d032f7f 100644 --- a/audio/drivers/ctr_dsp_audio.c +++ b/audio/drivers/ctr_dsp_audio.c @@ -21,7 +21,7 @@ #include #include "../audio_driver.h" -#include "../../ctr/ctr_debug.h" +#include "../../os/ctr/ctr_debug.h" #include "../../retroarch.h" #include "../../verbosity.h" diff --git a/ctr/ctr_linear.cpp b/ctr/ctr_linear.cpp deleted file mode 100644 index c8c0cb85a4..0000000000 --- a/ctr/ctr_linear.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* from https://github.com/smealum/ctrulib - * modified to allow reducing __linear_heap_size at runtime */ - -#include <3ds.h> -#include -#include <3ds/util/rbtree.h> -#include "ctr_debug.h" - -struct MemChunk -{ - u8* addr; - u32 size; -}; - -struct MemBlock -{ - MemBlock *prev, *next; - u8* base; - u32 size; - - static MemBlock* Create(u8* base, u32 size) - { - auto b = (MemBlock*)malloc(sizeof(MemBlock)); - if (!b) - return nullptr; - b->prev = nullptr; - b->next = nullptr; - b->base = base; - b->size = size; - return b; - } -}; - -struct MemPool -{ - MemBlock *first, *last; - - bool Ready() { return first != nullptr; } - - void AddBlock(MemBlock* blk) - { - blk->prev = last; - if (last) last->next = blk; - if (!first) first = blk; - last = blk; - } - - void DelBlock(MemBlock* b) - { - auto prev = b->prev, &pNext = prev ? prev->next : first; - auto next = b->next, &nPrev = next ? next->prev : last; - pNext = next; - nPrev = prev; - free(b); - } - - void InsertBefore(MemBlock* b, MemBlock* p) - { - auto prev = b->prev, &pNext = prev ? prev->next : first; - b->prev = p; - p->next = b; - p->prev = prev; - pNext = p; - } - - void InsertAfter(MemBlock* b, MemBlock* n) - { - auto next = b->next, &nPrev = next ? next->prev : last; - b->next = n; - n->prev = b; - n->next = next; - nPrev = n; - } - - void CoalesceRight(MemBlock* b); - - bool Allocate(MemChunk& chunk, u32 size, int align); - void Deallocate(const MemChunk& chunk); - - void Destroy() - { - MemBlock* next = nullptr; - for (auto b = first; b; b = next) - { - next = b->next; - free(b); - } - first = nullptr; - last = nullptr; - } - -#if 0 - void Dump(const char* title); -#endif - u32 GetFreeSpace(); -}; - -static rbtree_t sAddrMap; - -struct addrMapNode -{ - rbtree_node node; - MemChunk chunk; -}; - -#define getAddrMapNode(x) rbtree_item((x), addrMapNode, node) - -static int addrMapNodeComparator(const rbtree_node_t* _lhs, const rbtree_node_t* _rhs) -{ - auto lhs = getAddrMapNode(_lhs)->chunk.addr; - auto rhs = getAddrMapNode(_rhs)->chunk.addr; - if (lhs < rhs) - return -1; - if (lhs > rhs) - return 1; - return 0; -} - -static void addrMapNodeDestructor(rbtree_node_t* a) -{ - free(getAddrMapNode(a)); -} - -static addrMapNode* getNode(void* addr) -{ - addrMapNode n; - n.chunk.addr = (u8*)addr; - auto p = rbtree_find(&sAddrMap, &n.node); - return p ? getAddrMapNode(p) : nullptr; -} - -static addrMapNode* newNode(const MemChunk& chunk) -{ - auto p = (addrMapNode*)malloc(sizeof(addrMapNode)); - if (!p) return nullptr; - p->chunk = chunk; - return p; -} - -static void delNode(addrMapNode* node) -{ - rbtree_remove(&sAddrMap, &node->node, addrMapNodeDestructor); -} - -extern u32 __linear_heap, __linear_heap_size; - -static MemPool sLinearPool; -static u32 sLinearPool_maxaddr; - -static bool linearInit(void) -{ - auto blk = MemBlock::Create((u8*)__linear_heap, __linear_heap_size); - if (blk) - { - sLinearPool.AddBlock(blk); - sLinearPool_maxaddr = __linear_heap; - rbtree_init(&sAddrMap, addrMapNodeComparator); - return true; - } - return false; -} - -void* linearMemAlign(size_t len, size_t alignment) -{ - int shift; - /* Enforce minimum alignment */ - if (alignment < 16) - alignment = 16; - - /* Convert alignment to shift amount */ - for (shift = 4; shift < 32; shift ++) - { - if ((1U<node)) { - sLinearPool.Deallocate(chunk); - return nullptr; - } - - if (sLinearPool_maxaddr < (u32)sLinearPool.last->base) - sLinearPool_maxaddr = (u32)sLinearPool.last->base; - - return chunk.addr; -} - -void* linearAlloc(size_t len) -{ -#if 0 - if (ctrConsole && ctrConsole->consoleInitialised) - { - printf("linearAlloc : 0x%08X\n", len); - DEBUG_HOLD(); - } -#endif - return linearMemAlign(len, 0x80); -} - -void* linearRealloc(void* mem, size_t len) { return NULL; } - -void linearFree(void* mem) -{ - auto node = getNode(mem); - if (!node) - return; - - /* Free the chunk */ - sLinearPool.Deallocate(node->chunk); - - /* Free the node */ - delNode(node); -} - -u32 linearSpaceFree(void) -{ - return sLinearPool.GetFreeSpace(); -} - -extern "C" u32 ctr_get_linear_free(void) -{ - if (sLinearPool.last->base + sLinearPool.last->size != (u8*)__linear_heap + __linear_heap_size) - return 0; - return sLinearPool.last->size; -} - -extern "C" u32 ctr_get_linear_unused(void) -{ - return __linear_heap + __linear_heap_size - sLinearPool_maxaddr; -} - -extern "C" void ctr_linear_free_pages(u32 pages) -{ - u32 tmp, size; - if (sLinearPool.last->base + sLinearPool.last->size != (u8*)__linear_heap + __linear_heap_size) - return; - - size = pages << 12; - if (size > sLinearPool.last->size) - return; - - sLinearPool.last->size -= size; - __linear_heap_size -= size; - svcControlMemory(&tmp, __linear_heap + __linear_heap_size, 0x0, size, - MEMOP_FREE, (MemPerm)(MEMPERM_READ | MEMPERM_WRITE)); - -#if 0 - printf("l:0x%08X-->0x%08X(-0x%08X) \n", sLinearPool.last->size + size, sLinearPool.last->size, size); - DEBUG_HOLD(); -#endif -} - -extern "C" void ctr_linear_get_stats(void) -{ - printf("last:\n"); - printf("0x%08X --> 0x%08X (0x%08X) \n", sLinearPool.last->base, - sLinearPool.last->base + sLinearPool.last->size, sLinearPool.last->size); - printf("free: 0x%08X unused: 0x%08X \n", ctr_get_linear_unused(), ctr_get_linear_free()); -} diff --git a/frontend/drivers/platform_ctr.c b/frontend/drivers/platform_ctr.c index f180c47213..2dbdfb977f 100644 --- a/frontend/drivers/platform_ctr.c +++ b/frontend/drivers/platform_ctr.c @@ -47,9 +47,9 @@ #include "../../retroarch.h" #include "../../verbosity.h" -#include "ctr/ctr_debug.h" -#include "ctr/exec-3dsx/exec_3dsx.h" -#include "ctr/exec-3dsx/exec_cia.h" +#include "os/ctr/ctr_debug.h" +#include "os/ctr/exec-3dsx/exec_3dsx.h" +#include "os/ctr/exec-3dsx/exec_cia.h" #ifndef IS_SALAMANDER #ifdef HAVE_MENU diff --git a/gfx/drivers/ctr_gfx.c b/gfx/drivers/ctr_gfx.c index 3a06507f8e..d9f9df161c 100644 --- a/gfx/drivers/ctr_gfx.c +++ b/gfx/drivers/ctr_gfx.c @@ -43,7 +43,7 @@ #include "../font_driver.h" #include "../video_views.h" #define DEPRECATED -#include "../../ctr/gpu_old.h" +#include "../../os/ctr/gpu_old.h" #undef DEPRECATED #include "ctr_gu.h" diff --git a/gfx/drivers/ctr_gu.h b/gfx/drivers/ctr_gu.h index 96fa284a26..35ded9fb67 100644 --- a/gfx/drivers/ctr_gu.h +++ b/gfx/drivers/ctr_gu.h @@ -23,7 +23,7 @@ #include #include -#include "ctr/ctr_debug.h" +#include "os/ctr/ctr_debug.h" #define VIRT_TO_PHYS(vaddr) \ (((u32)(vaddr)) >= 0x14000000 && ((u32)(vaddr)) < 0x1c000000)?(void*)((u32)(vaddr) + 0x0c000000):\ diff --git a/ctr/3dsx_custom.specs b/os/ctr/3dsx_custom.specs similarity index 71% rename from ctr/3dsx_custom.specs rename to os/ctr/3dsx_custom.specs index 9ccabed06a..33341b9af0 100644 --- a/ctr/3dsx_custom.specs +++ b/os/ctr/3dsx_custom.specs @@ -4,4 +4,4 @@ %(old_link) -T 3dsx.ld%s -d --emit-relocs --use-blx --gc-sections *startfile: -ctr/3dsx_custom_crt0%O%s crti%O%s crtbegin%O%s +os/ctr/3dsx_custom_crt0%O%s crti%O%s crtbegin%O%s diff --git a/ctr/3dsx_custom_crt0.s b/os/ctr/3dsx_custom_crt0.s similarity index 100% rename from ctr/3dsx_custom_crt0.s rename to os/ctr/3dsx_custom_crt0.s diff --git a/ctr/ctr_debug.h b/os/ctr/ctr_debug.h similarity index 100% rename from ctr/ctr_debug.h rename to os/ctr/ctr_debug.h diff --git a/os/ctr/ctr_linear.c b/os/ctr/ctr_linear.c new file mode 100644 index 0000000000..dd21a65d78 --- /dev/null +++ b/os/ctr/ctr_linear.c @@ -0,0 +1,435 @@ +/* The linear heap allocator, in place of libctru's, so that the end of + * the linear heap can be handed back to the system at runtime + * (ctr_linear_free_pages). + * + * Free space is an address-ordered list of blocks, allocated first fit, + * the way libctru's MemPool does, so buffers land where they always + * have. Live allocations are found by address in an open-addressing + * table. List nodes come from slabs, so a walk of the list stays within + * a few cache lines, and a node a split or a free gives up is kept for + * the next one rather than going back to malloc. */ + +#include +#include +#include + +#include <3ds.h> + +#include +#include + +#define LINEAR_LIVE_MIN 64u +#define LINEAR_SLAB 64u /* nodes per slab */ +#define LINEAR_LINE 32u /* the ARM11's L1 line */ + +#if defined(__GNUC__) +#define LINEAR_COLD __attribute__((noinline, cold)) +#else +#define LINEAR_COLD +#endif + +/* What a search reads - next, base, size - leads */ +typedef struct linear_block +{ + struct linear_block *next; + u32 base; + u32 size; + struct linear_block *prev; +} linear_block_t; + + +/* addr 0 marks a free slot; the linear heap is never at 0 */ +typedef struct +{ + u32 addr; + u32 size; +} linear_live_t; + +extern u32 __linear_heap, __linear_heap_size; + +/* The free blocks form a ring through end, a block at 0xFFFFFFFF that + * every allocation fits (a padded size and its alignment waste come to + * less than its 0xFFFFFFFF bytes) and that lies past every address, so + * neither a search nor a free needs to test for the end of the list: a + * search stops at end at the latest, a free goes in before it at the + * latest, and no chunk ends where it starts to join it. end.next is the + * first block and end.prev the last, end itself when there is none. */ +static struct +{ + linear_block_t end; + linear_block_t *spare; /* nodes kept for reuse, chained by next */ + void *slabs; /* each slab's first word is the next */ + linear_live_t *live; + u32 live_mask; + u32 live_count; + u32 maxaddr; + bool ready; +} linear; + +#define LINEAR_END (&linear.end) + +u32 ctr_get_linear_free(void); +u32 ctr_get_linear_unused(void); +void ctr_linear_free_pages(u32 pages); +void ctr_linear_get_stats(void); + +/* ---- free list ---- */ + +/* A slab of nodes, starting on a cache line so none straddles two, + * onto the spare list; the slabs last as long as the heap does */ +static LINEAR_COLD linear_block_t *linear_slab_new(void) +{ + u32 i; + linear_block_t *node; + void **slab = (void**)malloc(sizeof(void*) + LINEAR_LINE - 1 + + LINEAR_SLAB * sizeof(linear_block_t)); + if (!slab) + return NULL; + *slab = linear.slabs; + linear.slabs = slab; + node = (linear_block_t*)(((uintptr_t)(slab + 1) + LINEAR_LINE - 1) + & ~(uintptr_t)(LINEAR_LINE - 1)); + for (i = 0; i < LINEAR_SLAB - 1; i++) + node[i].next = &node[i + 1]; + node[LINEAR_SLAB - 1].next = NULL; + return node; +} + +static INLINE linear_block_t *linear_block_new(u32 base, u32 size) +{ + linear_block_t *b = linear.spare; + if (!b && !(b = linear_slab_new())) + return NULL; + linear.spare = b->next; + b->base = base; + b->size = size; + return b; +} + +static INLINE void linear_block_link(linear_block_t *n, linear_block_t *before) +{ + n->next = before; + n->prev = before->prev; + before->prev->next = n; + before->prev = n; +} + +static INLINE void linear_block_del(linear_block_t *b) +{ + b->prev->next = b->next; + b->next->prev = b->prev; + b->next = linear.spare; + linear.spare = b; +} + +/* Takes size bytes aligned to mask + 1 from the first block they fit + * in; 0 when none has room. size is a multiple of mask + 1, so the + * alignment waste added to it cannot overflow. *got is what the + * allocation holds, which is more than size when no node was to be had + * for the tail. */ +static u32 linear_take(u32 size, u32 mask, u32 *got) +{ + linear_block_t *b = linear.end.next; + u32 waste, addr, rest; + + while ((waste = (0u - b->base) & mask) + size > b->size) + b = b->next; + if (b == LINEAR_END) + return 0; + + addr = b->base + waste; + rest = b->size - waste - size; + *got = size; + + if (!waste) + { + b->base += size; + b->size = rest; + if (!rest) + linear_block_del(b); + } + else + { + b->size = waste; + if (rest) + { + /* The tail goes back on the list after b */ + linear_block_t *n = linear_block_new(addr + size, rest); + if (n) + linear_block_link(n, b->next); + else + *got += rest; /* no choice but to waste the space */ + } + } + return addr; +} + +static void linear_give(u32 addr, u32 size) +{ + linear_block_t *b, *n; + + for (b = linear.end.next; ; b = b->next) + { + if (b->base > addr) + { + /* Joins the block it ends at, or goes in before it - before + * end when it lies past the last block */ + if (addr + size == b->base) + { + b->base = addr; + b->size += size; + } + else if ((n = linear_block_new(addr, size))) + linear_block_link(n, b); + return; + } + + if (b->base + b->size == addr) + { + /* Joins the block before it, and then those it now reaches */ + u32 end = addr + size; + b->size += size; + while ((n = b->next)->base == end) + { + b->size += n->size; + end += n->size; + linear_block_del(n); + } + return; + } + } +} + +/* ---- live allocations ---- */ + +static INLINE u32 linear_live_home(u32 addr, u32 mask) +{ + u32 h = (addr >> 4) * 0x9E3779B1u; + return (h ^ (h >> 15)) & mask; +} + +/* The slot holding addr, or the free one it would go in */ +static INLINE u32 linear_live_slot(const linear_live_t *live, u32 mask, + u32 addr) +{ + u32 i = linear_live_home(addr, mask); + while (live[i].addr && live[i].addr != addr) + i = (i + 1) & mask; + return i; +} + +static LINEAR_COLD bool linear_live_grow(void) +{ + u32 i; + u32 old_mask = linear.live_mask; + u32 mask = old_mask * 2 + 1; + linear_live_t *old = linear.live; + linear_live_t *live = (linear_live_t*)calloc(mask + 1, sizeof(*live)); + + if (!live) + return false; + for (i = 0; i <= old_mask; i++) + if (old[i].addr) + live[linear_live_slot(live, mask, old[i].addr)] = old[i]; + free(old); + linear.live = live; + linear.live_mask = mask; + return true; +} + +/* An address already live keeps what it holds */ +static bool linear_live_add(u32 addr, u32 size) +{ + u32 i; + if ((linear.live_count + 1) * 2 > linear.live_mask + 1 + && !linear_live_grow()) + return false; + i = linear_live_slot(linear.live, linear.live_mask, addr); + if (!linear.live[i].addr) + { + linear.live[i].addr = addr; + linear.live[i].size = size; + linear.live_count++; + } + return true; +} + +static void linear_live_del(u32 i) +{ + linear_live_t *live = linear.live; + u32 mask = linear.live_mask; + u32 j = i; + + /* Pulls back each entry after it that would no longer be found */ + for (;;) + { + u32 k; + j = (j + 1) & mask; + if (!live[j].addr) + break; + k = linear_live_home(live[j].addr, mask); + if (i <= j ? (i < k && k <= j) : (i < k || k <= j)) + continue; + live[i] = live[j]; + i = j; + } + live[i].addr = 0; + linear.live_count--; +} + +/* ---- libctru's linear heap interface ---- */ + +static LINEAR_COLD bool linear_init(void) +{ + linear_block_t *b; + + if (!(linear.live = (linear_live_t*)calloc(LINEAR_LIVE_MIN, + sizeof(*linear.live)))) + return false; + if (!(b = linear_block_new(__linear_heap, __linear_heap_size))) + { + free(linear.live); + linear.live = NULL; + return false; + } + linear.end.base = 0xFFFFFFFFu; + linear.end.size = 0xFFFFFFFFu; + linear.end.next = LINEAR_END; + linear.end.prev = LINEAR_END; + linear_block_link(b, LINEAR_END); + linear.live_mask = LINEAR_LIVE_MIN - 1; + linear.maxaddr = __linear_heap; + linear.ready = true; + return true; +} + +void *linearMemAlign(size_t len, size_t alignment) +{ + u32 mask, size, got, addr; + + if (alignment < 16) + alignment = 16; + else if ((alignment & (alignment - 1)) || alignment > 0x80000000u) + return NULL; + mask = (u32)alignment - 1; + + if ((size_t)(u32)len != len) + return NULL; + size = (u32)len; + if (size & mask) + { + if (size > 0xFFFFFFFFu - mask) + return NULL; + size = (size + mask) & ~mask; + } + + if (!linear.ready && !linear_init()) + return NULL; + + if (!(addr = linear_take(size, mask, &got))) + return NULL; + + if (!linear_live_add(addr, got)) + { + linear_give(addr, got); + return NULL; + } + + if (linear.end.prev == LINEAR_END) + linear.maxaddr = __linear_heap + __linear_heap_size; + else if (linear.maxaddr < linear.end.prev->base) + linear.maxaddr = linear.end.prev->base; + + return (void*)(uintptr_t)addr; +} + +void *linearAlloc(size_t len) +{ + return linearMemAlign(len, 0x80); +} + +void *linearRealloc(void *mem, size_t len) +{ + (void)mem; + (void)len; + return NULL; +} + +size_t linearGetSize(void *mem) +{ + u32 i, addr = (u32)(uintptr_t)mem; + if (!linear.ready || !addr) + return 0; + i = linear_live_slot(linear.live, linear.live_mask, addr); + return linear.live[i].addr ? linear.live[i].size : 0; +} + +void linearFree(void *mem) +{ + u32 i, addr = (u32)(uintptr_t)mem; + linear_live_t chunk; + + if (!linear.ready || !addr) + return; + i = linear_live_slot(linear.live, linear.live_mask, addr); + if (!linear.live[i].addr) + return; + chunk = linear.live[i]; + linear_live_del(i); + linear_give(chunk.addr, chunk.size); +} + +u32 linearSpaceFree(void) +{ + u32 total = 0; + const linear_block_t *b; + if (!linear.ready) + return 0; + for (b = linear.end.next; b != LINEAR_END; b = b->next) + total += b->size; + return total; +} + +/* ---- the end of the heap ---- */ + +u32 ctr_get_linear_free(void) +{ + const linear_block_t *b = linear.end.prev; + if (!linear.ready || b == LINEAR_END || b->base + b->size != __linear_heap + __linear_heap_size) + return 0; + return b->size; +} + +u32 ctr_get_linear_unused(void) +{ + return __linear_heap + __linear_heap_size - linear.maxaddr; +} + +void ctr_linear_free_pages(u32 pages) +{ + u32 tmp, size; + linear_block_t *b = linear.end.prev; + + if (!linear.ready || b == LINEAR_END || b->base + b->size != __linear_heap + __linear_heap_size) + return; + + size = pages << 12; + if (size > b->size) + return; + + b->size -= size; + __linear_heap_size -= size; + svcControlMemory(&tmp, __linear_heap + __linear_heap_size, 0x0, size, + MEMOP_FREE, (MemPerm)(MEMPERM_READ | MEMPERM_WRITE)); +} + +void ctr_linear_get_stats(void) +{ + const linear_block_t *b = linear.end.prev; + printf("last:\n"); + if (linear.ready && b != LINEAR_END) + printf("0x%08X --> 0x%08X (0x%08X) \n", (unsigned)b->base, + (unsigned)(b->base + b->size), (unsigned)b->size); + printf("free: 0x%08X unused: 0x%08X \n", + (unsigned)ctr_get_linear_unused(), (unsigned)ctr_get_linear_free()); +} diff --git a/ctr/ctr_memory.c b/os/ctr/ctr_memory.c similarity index 100% rename from ctr/ctr_memory.c rename to os/ctr/ctr_memory.c diff --git a/ctr/ctr_svchax.c b/os/ctr/ctr_svchax.c similarity index 100% rename from ctr/ctr_svchax.c rename to os/ctr/ctr_svchax.c diff --git a/ctr/ctr_system.c b/os/ctr/ctr_system.c similarity index 100% rename from ctr/ctr_system.c rename to os/ctr/ctr_system.c diff --git a/ctr/exec-3dsx/exec_3dsx.c b/os/ctr/exec-3dsx/exec_3dsx.c similarity index 100% rename from ctr/exec-3dsx/exec_3dsx.c rename to os/ctr/exec-3dsx/exec_3dsx.c diff --git a/ctr/exec-3dsx/exec_3dsx.h b/os/ctr/exec-3dsx/exec_3dsx.h similarity index 100% rename from ctr/exec-3dsx/exec_3dsx.h rename to os/ctr/exec-3dsx/exec_3dsx.h diff --git a/ctr/exec-3dsx/exec_cia.c b/os/ctr/exec-3dsx/exec_cia.c similarity index 100% rename from ctr/exec-3dsx/exec_cia.c rename to os/ctr/exec-3dsx/exec_cia.c diff --git a/ctr/exec-3dsx/exec_cia.h b/os/ctr/exec-3dsx/exec_cia.h similarity index 100% rename from ctr/exec-3dsx/exec_cia.h rename to os/ctr/exec-3dsx/exec_cia.h diff --git a/ctr/exec-3dsx/mini-hb-menu/common.h b/os/ctr/exec-3dsx/mini-hb-menu/common.h similarity index 100% rename from ctr/exec-3dsx/mini-hb-menu/common.h rename to os/ctr/exec-3dsx/mini-hb-menu/common.h diff --git a/ctr/exec-3dsx/mini-hb-menu/launch.c b/os/ctr/exec-3dsx/mini-hb-menu/launch.c similarity index 100% rename from ctr/exec-3dsx/mini-hb-menu/launch.c rename to os/ctr/exec-3dsx/mini-hb-menu/launch.c diff --git a/ctr/exec-3dsx/mini-hb-menu/launch.h b/os/ctr/exec-3dsx/mini-hb-menu/launch.h similarity index 100% rename from ctr/exec-3dsx/mini-hb-menu/launch.h rename to os/ctr/exec-3dsx/mini-hb-menu/launch.h diff --git a/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c b/os/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c similarity index 100% rename from ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c rename to os/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c diff --git a/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c b/os/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c similarity index 100% rename from ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c rename to os/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c diff --git a/ctr/gpu_old.c b/os/ctr/gpu_old.c similarity index 100% rename from ctr/gpu_old.c rename to os/ctr/gpu_old.c diff --git a/ctr/gpu_old.h b/os/ctr/gpu_old.h similarity index 100% rename from ctr/gpu_old.h rename to os/ctr/gpu_old.h diff --git a/samples/audio/ctr_audio/3ds/types.h b/samples/audio/ctr_audio/3ds/types.h index 53dda39d40..ab4950834c 100644 --- a/samples/audio/ctr_audio/3ds/types.h +++ b/samples/audio/ctr_audio/3ds/types.h @@ -1,2 +1,2 @@ -/* <3ds/types.h>, as ctr/ctr_debug.h asks for it: the stand-in's types. */ +/* <3ds/types.h>, as os/ctr/ctr_debug.h asks for it: the stand-in's types. */ #include "../3ds.h" diff --git a/samples/frontend/ctr_linear/3ds.h b/samples/frontend/ctr_linear/3ds.h new file mode 100644 index 0000000000..34497a6aa3 --- /dev/null +++ b/samples/frontend/ctr_linear/3ds.h @@ -0,0 +1,15 @@ +/* Stand-in for libctru's <3ds.h>: what os/ctr/ctr_linear.c names. */ +#ifndef CTR_LINEAR_STUB_3DS_H +#define CTR_LINEAR_STUB_3DS_H +#include <3ds/types.h> +typedef enum { MEMOP_FREE = 1 } MemOp; +typedef enum { MEMPERM_READ = 1, MEMPERM_WRITE = 2 } MemPerm; +Result svcControlMemory(u32 *addr_out, u32 addr0, u32 addr1, u32 size, + MemOp op, MemPerm perm); +void *linearAlloc(size_t size); +void *linearMemAlign(size_t size, size_t alignment); +void *linearRealloc(void *mem, size_t size); +size_t linearGetSize(void *mem); +void linearFree(void *mem); +u32 linearSpaceFree(void); +#endif diff --git a/samples/frontend/ctr_linear/3ds/types.h b/samples/frontend/ctr_linear/3ds/types.h new file mode 100644 index 0000000000..2c03cf172e --- /dev/null +++ b/samples/frontend/ctr_linear/3ds/types.h @@ -0,0 +1,11 @@ +/* Stand-in for libctru's <3ds/types.h> */ +#ifndef CTR_LINEAR_STUB_3DS_TYPES_H +#define CTR_LINEAR_STUB_3DS_TYPES_H +#include +#include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef int32_t s32; +typedef int32_t Result; +#endif diff --git a/samples/frontend/ctr_linear/Makefile b/samples/frontend/ctr_linear/Makefile new file mode 100644 index 0000000000..163ebe6c89 --- /dev/null +++ b/samples/frontend/ctr_linear/Makefile @@ -0,0 +1,28 @@ +# os/ctr/ctr_linear.c, the 3DS linear heap allocator, against a stand-in +# <3ds.h> and a linear heap at an address it hands out but never +# touches. Builds for Linux and, with CC=x86_64-w64-mingw32-gcc EXE=.exe +# RUNNER=wine, for Windows. +TARGET := ctr_linear_test$(EXE) +CORE_DIR := ../../.. + +SOURCES := ctr_linear_test.c $(CORE_DIR)/os/ctr/ctr_linear.c + +CFLAGS += -std=c89 -Wall -g1 -O1 -I. -I$(CORE_DIR)/libretro-common/include + +ifneq ($(SANITIZER),) + CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS) + LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS) +endif + +all: $(TARGET) + +$(TARGET): $(SOURCES) 3ds.h 3ds/types.h + $(CC) $(CFLAGS) -o $@ $(SOURCES) $(LDFLAGS) + +check: all + $(RUNNER) ./$(TARGET) + +clean: + rm -f ctr_linear_test ctr_linear_test.exe + +.PHONY: all check clean diff --git a/samples/frontend/ctr_linear/ctr_linear_test.c b/samples/frontend/ctr_linear/ctr_linear_test.c new file mode 100644 index 0000000000..ca0b53b000 --- /dev/null +++ b/samples/frontend/ctr_linear/ctr_linear_test.c @@ -0,0 +1,216 @@ +/* os/ctr/ctr_linear.c, the allocator for the 3DS linear heap that the + * GPU and DSP buffers come from. + * + * Allocations are placed first fit in address order and aligned as + * asked, freed space joins its neighbours until the heap is one block + * again, every live allocation is found by its address however many + * there are, and pages handed back from the end of the heap leave it. + * + * An allocation that fits no free block fails. With the whole heap + * allocated, no free block is left at all: a further + * allocation must fail rather than start the heap over and hand out + * its first address a second time, and the free-space queries must + * answer rather than fault. */ + +#include +#include +#include + +#include <3ds.h> + +#define HEAP 0x14000000ul +#define HEAP_SIZE 0x00400000ul + +u32 __linear_heap = HEAP; +u32 __linear_heap_size = HEAP_SIZE; + +u32 ctr_get_linear_free(void); +u32 ctr_get_linear_unused(void); +void ctr_linear_free_pages(u32 pages); +void ctr_linear_get_stats(void); + +static int failures; +static unsigned long svc_addr, svc_size, svc_calls; + +#define CHECK(c) do { if (!(c)) { \ + fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #c); \ + failures++; } } while (0) + +Result svcControlMemory(u32 *addr_out, u32 addr0, u32 addr1, u32 size, + MemOp op, MemPerm perm) +{ + (void)addr_out; + (void)addr1; + (void)perm; + if (op == MEMOP_FREE) + { + svc_addr = addr0; + svc_size = size; + svc_calls++; + } + return 0; +} + +static unsigned long addr_of(const void *p) +{ + return (unsigned long)(size_t)p; +} + +static unsigned long rng = 1; + +static unsigned long next_rand(void) +{ + rng = (rng * 1103515245ul + 12345ul) & 0xFFFFFFFFul; + return rng >> 8; +} + +/* The whole heap is free as one block at its start */ +static int heap_whole(void) +{ + void *all = linearMemAlign(__linear_heap_size, 16); + int ok = addr_of(all) == HEAP && linearSpaceFree() == 0; + linearFree(all); + return ok && linearSpaceFree() == __linear_heap_size; +} + +#define STRESS 2000 + +static void *live[STRESS]; +static unsigned long live_size[STRESS]; + +static int by_addr(const void *a, const void *b) +{ + unsigned long x = addr_of(*(void* const*)a); + unsigned long y = addr_of(*(void* const*)b); + return x < y ? -1 : x > y; +} + +static void stress(void) +{ + static const size_t aligns[] = { 0, 16, 0x40, 0x80, 0x200, 0x1000 }; + unsigned long used = 0; + void *sorted[STRESS]; + int n = 0, i, overlaps = 0, lost = 0; + + for (i = 0; i < STRESS; i++) + { + size_t size = 1 + next_rand() % 3000; + size_t align = aligns[next_rand() % 6]; + void *p = linearMemAlign(size, align); + if (!p) + continue; + if (align && addr_of(p) % align) + lost++; + live[n] = p; + live_size[n] = linearGetSize(p); + if (live_size[n] < size) + lost++; + n++; + /* Free about one in three as it goes */ + if (next_rand() % 3 == 0) + { + int k = (int)(next_rand() % (unsigned long)n); + linearFree(live[k]); + live[k] = live[n - 1]; + live_size[k] = live_size[n - 1]; + n--; + } + } + CHECK(n > 256); /* well past the table's first size */ + CHECK(lost == 0); + + memcpy(sorted, live, n * sizeof(*sorted)); + qsort(sorted, n, sizeof(*sorted), by_addr); + for (i = 0; i < n; i++) + { + used += linearGetSize(sorted[i]); + if (i && addr_of(sorted[i - 1]) + linearGetSize(sorted[i - 1]) + > addr_of(sorted[i])) + overlaps++; + } + CHECK(overlaps == 0); + CHECK(used + linearSpaceFree() == HEAP_SIZE); + + while (n) + { + int k = (int)(next_rand() % (unsigned long)n); + linearFree(live[k]); + live[k] = live[--n]; + } + CHECK(linearSpaceFree() == HEAP_SIZE); + CHECK(heap_whole()); +} + +int main(void) +{ + void *a, *b, *c, *d, *all; + + /* Before anything is allocated */ + CHECK(linearSpaceFree() == 0); + CHECK(ctr_get_linear_free() == 0); + linearFree(NULL); + linearFree((void*)(size_t)(HEAP + 0x80)); + CHECK(linearMemAlign(64, 0x30) == NULL); + CHECK(linearMemAlign(64, 0x300) == NULL); + + /* First fit, aligned */ + a = linearAlloc(100); /* 0x80-aligned, 0x80 bytes */ + b = linearMemAlign(10, 0); /* at least 16-aligned */ + c = linearMemAlign(0x100, 0x1000); + d = linearAlloc(16); /* into the gap before c */ + CHECK(addr_of(a) == HEAP); + CHECK(addr_of(b) == HEAP + 0x80); + CHECK(addr_of(c) == HEAP + 0x1000); + CHECK(addr_of(d) == HEAP + 0x100); + CHECK(linearGetSize(a) == 0x80); + CHECK(linearGetSize(b) == 0x10); + CHECK(linearGetSize(c) == 0x1000); /* sizes round up to the alignment */ + CHECK(linearGetSize(d) == 0x80); + CHECK(linearGetSize((void*)(size_t)(HEAP + 0x40)) == 0); + CHECK(linearSpaceFree() == HEAP_SIZE - 0x80 - 0x10 - 0x1000 - 0x80); + CHECK(linearRealloc(a, 0x100) == NULL); + CHECK(linearMemAlign(HEAP_SIZE, 16) == NULL); /* no block that big */ + CHECK(linearMemAlign(0xFFFFFF00ul, 0x100) == NULL); + + /* Freed space joins back into one block */ + linearFree(b); + linearFree(c); + linearFree(a); + linearFree(a); /* already free: ignored */ + linearFree(d); + CHECK(linearSpaceFree() == HEAP_SIZE); + CHECK(ctr_get_linear_free() == HEAP_SIZE); + CHECK(heap_whole()); + + /* The whole heap taken: nothing more to give */ + all = linearMemAlign(HEAP_SIZE, 16); + CHECK(addr_of(all) == HEAP); + CHECK(linearAlloc(16) == NULL); + CHECK(linearMemAlign(16, 16) == NULL); + CHECK(linearSpaceFree() == 0); + CHECK(ctr_get_linear_free() == 0); + CHECK(ctr_get_linear_unused() == 0); + ctr_linear_get_stats(); + linearFree(all); + CHECK(heap_whole()); + + stress(); + + /* Pages handed back leave the end of the heap */ + ctr_linear_free_pages(2); + CHECK(svc_calls == 1); + CHECK(__linear_heap_size == HEAP_SIZE - 0x2000); + CHECK(svc_addr == HEAP + HEAP_SIZE - 0x2000 && svc_size == 0x2000); + CHECK(ctr_get_linear_free() == HEAP_SIZE - 0x2000); + ctr_linear_free_pages(HEAP_SIZE >> 12); /* more than there is */ + CHECK(svc_calls == 1); + CHECK(heap_whole()); + + if (failures) + { + fprintf(stderr, "%d failure(s)\n", failures); + return 1; + } + printf("ctr_linear: ok\n"); + return 0; +} diff --git a/tools/overlap_copy_allow.list b/tools/overlap_copy_allow.list index 6dc533914e..fb00599315 100644 --- a/tools/overlap_copy_allow.list +++ b/tools/overlap_copy_allow.list @@ -24,7 +24,7 @@ # fix those with memmove, a staging buffer, or an alias-guarded # helper instead. audio/drivers/wiiu_audio.c|memcpy|offsets|offsets -ctr/ctr_svchax.c|memcpy|flush_buffer|flush_buffer +os/ctr/ctr_svchax.c|memcpy|flush_buffer|flush_buffer frontend/drivers/platform_gx_libogc.c|strlcpy|g_defaults.dirs|g_defaults.dirs gfx/drivers/sdl_rs90_gfx.c|memcpy|target|target gfx/video_filters/ntsc_crt.c|memcpy|v->out|v->out