Files
tactility/TactilityKernel/source/memory_esp32.cpp
Ken Van Hoeylandt 92ca046681 app-module events & callstack config, crash diagnostics (#630)
- Apps can specify task stack depth and preferred memory placement in their manifests.
- App identifiers are validated against length and character requirements.
- Crash diagnostics now show the crash cause, reason, fault address, call stack, and program-counter details, with logs saved for review.
- App closing is more consistent across built-in screens. There's now a dedicated function, and the old _emit() function is made private.
- Crash diagnostics no longer display a QR code without a call stack.
- Improved memory allocation for unrestricted requests.
2026-08-27 21:50:12 +02:00

112 lines
4.4 KiB
C++

// SPDX-License-Identifier: Apache-2.0
#ifdef ESP_PLATFORM
#include <tactility/memory.h>
#include <esp_heap_caps.h>
namespace {
uint32_t toHeapCaps(uint16_t capabilityFlags) {
uint32_t caps = 0;
if (capabilityFlags & MEMORY_CAPABILITY_INTERNAL) {
caps |= MALLOC_CAP_INTERNAL;
// MALLOC_CAP_INTERNAL alone can be satisfied by IRAM (tagged INTERNAL on ESP32's heap
// layout), which is word-only and fails FreeRTOS's byte-accessibility checks for things
// like a static task's TCB. 8BIT keeps this capability meaning genuinely byte-accessible
// internal RAM - but skip it when EXECUTABLE is also requested, since executable IRAM
// isn't 8-bit accessible on some ESP32 targets and combining both caps could make an
// otherwise-satisfiable request (plain executable internal memory) fail outright.
if (!(capabilityFlags & MEMORY_CAPABILITY_EXECUTABLE)) caps |= MALLOC_CAP_8BIT;
}
if (capabilityFlags & MEMORY_CAPABILITY_EXTERNAL) caps |= MALLOC_CAP_SPIRAM;
if (capabilityFlags & MEMORY_CAPABILITY_EXECUTABLE) caps |= MALLOC_CAP_EXEC;
if (capabilityFlags & MEMORY_CAPABILITY_DMA) caps |= MALLOC_CAP_DMA;
if (capabilityFlags & MEMORY_CAPABILITY_SIMD) caps |= MALLOC_CAP_SIMD;
return caps;
}
} // namespace
extern "C" {
void* memory_alloc_with_policy(size_t size, const struct MemoryPolicy* policy) {
uint32_t required_caps = toHeapCaps(policy->required);
uint32_t desired_caps = toHeapCaps(policy->desired);
// heap_caps_match() tests (heap->caps & caps) == caps - a caps value of 0 is trivially true
// for every heap, not none, so an unconstrained request must be steered to MALLOC_CAP_DEFAULT
// explicitly (same as ESP-IDF's own heap_caps_malloc_default()) or it can land on a heap
// that's unsuitable for the caller's actual use (e.g. not valid as a FreeRTOS task stack).
uint32_t combined_caps = required_caps | desired_caps;
if (combined_caps == 0) {
combined_caps = MALLOC_CAP_DEFAULT;
}
void* ptr;
if (policy->alignment > 0) {
ptr = heap_caps_aligned_alloc(policy->alignment, size, combined_caps);
if (ptr == nullptr && desired_caps != 0) {
// Desired caps couldn't be satisfied alongside the required ones - retry with
// required only, since desired is explicitly optional.
ptr = heap_caps_aligned_alloc(policy->alignment, size, required_caps | MALLOC_CAP_DEFAULT);
}
} else {
ptr = heap_caps_malloc(size, combined_caps);
if (ptr == nullptr && desired_caps != 0) {
ptr = heap_caps_malloc(size, required_caps | MALLOC_CAP_DEFAULT);
}
}
return ptr;
}
void* memory_realloc_with_policy(void* ptr, size_t size, const struct MemoryPolicy* policy) {
uint32_t required_caps = toHeapCaps(policy->required);
uint32_t desired_caps = toHeapCaps(policy->desired);
uint32_t combined_caps = required_caps | desired_caps;
if (combined_caps == 0) {
combined_caps = MALLOC_CAP_DEFAULT;
}
// No aligned-realloc counterpart in the heap_caps API - policy->alignment is only honored
// on fresh allocations (memory_alloc_with_policy/memory_calloc_with_policy).
void* result = heap_caps_realloc(ptr, size, combined_caps);
if (result == nullptr && desired_caps != 0) {
result = heap_caps_realloc(ptr, size, required_caps | MALLOC_CAP_DEFAULT);
}
return result;
}
void* memory_calloc_with_policy(size_t count, size_t size, const struct MemoryPolicy* policy) {
uint32_t required_caps = toHeapCaps(policy->required);
uint32_t desired_caps = toHeapCaps(policy->desired);
uint32_t combined_caps = required_caps | desired_caps;
if (combined_caps == 0) {
combined_caps = MALLOC_CAP_DEFAULT;
}
void* ptr;
if (policy->alignment > 0) {
ptr = heap_caps_aligned_calloc(policy->alignment, count, size, combined_caps);
if (ptr == nullptr && desired_caps != 0) {
ptr = heap_caps_aligned_calloc(policy->alignment, count, size, required_caps | MALLOC_CAP_DEFAULT);
}
} else {
ptr = heap_caps_calloc(count, size, combined_caps);
if (ptr == nullptr && desired_caps != 0) {
ptr = heap_caps_calloc(count, size, required_caps | MALLOC_CAP_DEFAULT);
}
}
return ptr;
}
void memory_free(void* ptr) {
heap_caps_free(ptr);
}
} // extern "C"
#endif // ESP_PLATFORM