diff --git a/cpu/esp32/include/syscalls.h b/cpu/esp32/include/syscalls.h index a9f7af0242..4b0243751d 100644 --- a/cpu/esp32/include/syscalls.h +++ b/cpu/esp32/include/syscalls.h @@ -21,32 +21,15 @@ #ifndef SYSCALLS_H #define SYSCALLS_H -#include -#include -#include -#include - -#include "esp_common.h" +#include "syscalls_common.h" #ifdef __cplusplus extern "C" { #endif -/** Necessary initializations of system call functions */ -void syscalls_init (void); - -/** Determine free heap size */ -unsigned int get_free_heap_size (void); - -/** Time since boot in us (32bit version) */ -uint32_t system_get_time (void); - /** Time since boot in us (64bit version) */ uint64_t system_get_time_64 (void); -/** Time since boot in ms (32bit version) */ -uint32_t system_get_time_ms (void); - /** initialize system watchdog timer and start it */ void system_wdt_init (void); @@ -59,9 +42,6 @@ void system_wdt_stop (void); /** reset the system watchdog timer */ void system_wdt_feed (void); -/** memset version that the compiler should not be allowed to optimize this */ -void *system_secure_memset(void *s, int c, size_t n); - #ifdef __cplusplus } #endif diff --git a/cpu/esp32/startup.c b/cpu/esp32/startup.c index 95624152ef..3eef6393d9 100644 --- a/cpu/esp32/startup.c +++ b/cpu/esp32/startup.c @@ -293,13 +293,6 @@ static NORETURN void IRAM system_init (void) /* Disable the hold flag of all RTC GPIO pins */ RTCCNTL.hold_force.val = 0; - /* - * initialization of newlib, includes the ctors initialization and - * and the execution of stdio_init in _init of newlib_syscalls_default - */ - extern void __libc_init_array(void); - __libc_init_array(); - /* set log levels for SDK library outputs */ extern void esp_log_level_set(const char* tag, esp_log_level_t level); esp_log_level_set("wifi", LOG_DEBUG); diff --git a/cpu/esp32/syscalls.c b/cpu/esp32/syscalls.c index 267707e9ca..fcbe69df16 100644 --- a/cpu/esp32/syscalls.c +++ b/cpu/esp32/syscalls.c @@ -18,35 +18,15 @@ * @} */ -#define ENABLE_DEBUG (0) -#include "debug.h" - -#include -#include -#include -#include -#include -#include -#include -#include #include -#include "esp_common.h" -#include "cpu_conf.h" -#include "irq.h" +#include "esp/common_macros.h" #include "irq_arch.h" -#include "kernel_defines.h" -#include "log.h" -#include "mutex.h" -#include "rmutex.h" -#include "sched.h" +#include "periph_cpu.h" #include "periph/pm.h" - +#include "syscalls.h" #include "timex.h" -#include "esp_attr.h" -#include "esp/xtensa_ops.h" -#include "esp/common_macros.h" #include "rom/ets_sys.h" #include "rom/libc_stubs.h" #include "soc/rtc.h" @@ -55,231 +35,45 @@ #include "soc/timer_group_struct.h" #include "xtensa/xtensa_api.h" -#include "periph_cpu.h" -#include "syscalls.h" - #ifdef MODULE_ESP_IDF_HEAP -#include "heap/esp_heap_caps.h" -#include "heap/include/multi_heap.h" -#else -#include "malloc.h" +#include "esp_heap_caps.h" #endif +#define ENABLE_DEBUG (0) +#include "debug.h" + #define MHZ 1000000UL -#ifndef MODULE_PTHREAD - -#define PTHREAD_CANCEL_DISABLE 1 -/* - * This is a dummy function to avoid undefined references when linking - * against newlib and module pthread is not used. - */ -int pthread_setcancelstate(int state, int *oldstate) -{ - if (oldstate) { - *oldstate = PTHREAD_CANCEL_DISABLE; - } - return 0; -} -#endif /* MODULE_PTHREAD*/ - -/** - * @name Locking functions - * - * Following function implements the lock mechanism in newlib. The only static - * mutex defined here is the _malloc_rmtx to avoid that memory management - * functions try to lock before RIOT's threads are running. All other mutexes - * are allocated dynamically. - */ - -static rmutex_t _malloc_rmtx = RMUTEX_INIT; - -void IRAM _lock_init(_lock_t *lock) -{ - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL); - CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx)); - - mutex_t* mtx = malloc (sizeof(mutex_t)); - - if (mtx) { - memset (mtx, 0, sizeof(mutex_t)); - *lock = (_lock_t)mtx; - } -} - -void IRAM _lock_init_recursive(_lock_t *lock) -{ - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL); - CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx)); - - rmutex_t* rmtx = malloc (sizeof(rmutex_t)); - - if (rmtx) { - memset (rmtx, 0, sizeof(rmutex_t)); - *lock = (_lock_t)rmtx; - } -} - -void IRAM _lock_close(_lock_t *lock) -{ - CHECK_PARAM (lock != NULL); - CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx)); - - free ((void*)*lock); - *lock = 0; -} - -void IRAM _lock_close_recursive(_lock_t *lock) -{ - CHECK_PARAM (lock != NULL); - CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx)); - - free ((void*)*lock); - *lock = 0; -} - -void IRAM _lock_acquire(_lock_t *lock) -{ - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL && *lock != 0); - - mutex_lock ((mutex_t*)*lock); -} - -void IRAM _lock_acquire_recursive(_lock_t *lock) -{ - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL && *lock != 0); - - rmutex_lock ((rmutex_t*)*lock); -} - -int IRAM _lock_try_acquire(_lock_t *lock) -{ - CHECK_PARAM_RET (sched_active_thread != 0, 0); - CHECK_PARAM_RET (lock != NULL && *lock != 0, 0); - - return rmutex_trylock ((rmutex_t*)*lock); -} - -int IRAM _lock_try_acquire_recursive(_lock_t *lock) -{ - CHECK_PARAM_RET (sched_active_thread != 0, 0); - CHECK_PARAM_RET (lock != NULL && *lock != 0, 0); - - return mutex_trylock ((mutex_t*)*lock); -} - -void IRAM _lock_release(_lock_t *lock) -{ - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL && *lock != 0); - - mutex_unlock ((mutex_t*)*lock); -} - -void IRAM _lock_release_recursive(_lock_t *lock) -{ - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL && *lock != 0); - - rmutex_unlock ((rmutex_t*)*lock); -} - -/** - * @name Memory allocation functions - */ - #ifdef MODULE_ESP_IDF_HEAP -extern void *heap_caps_malloc_default( size_t size ); -extern void *heap_caps_realloc_default( void *ptr, size_t size ); - -void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size) +/* if module esp_idf_heap is used, this function has to be defined for ESP32 */ +unsigned int get_free_heap_size(void) { - return heap_caps_malloc_default( size ); + return heap_caps_get_free_size(MALLOC_CAP_DEFAULT); } -void IRAM_ATTR __wrap__free_r(struct _reent *r, void* ptr) -{ - heap_caps_free( ptr ); -} - -void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size) -{ - return heap_caps_realloc_default( ptr, size ); -} - -void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size) -{ - void* result = heap_caps_malloc_default(count * size); - if (result) { - bzero(result, count * size); - } - return result; -} - -unsigned int IRAM get_free_heap_size (void) -{ - return heap_caps_get_free_size( MALLOC_CAP_DEFAULT ); -} +/* alias for compatibility with espressif/wifi_libs */ +uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); +/* this function is platform specific if module esp_idf_heap is used */ void heap_stats(void) { + size_t _free = 0; + size_t _alloc = 0; + multi_heap_info_t hinfo; heap_caps_get_info(&hinfo, MALLOC_CAP_DEFAULT); - size_t _free = hinfo.total_free_bytes; - size_t _alloc = hinfo.total_allocated_bytes; + _free = hinfo.total_free_bytes; + _alloc = hinfo.total_allocated_bytes; - printf("heap: %u (used %u free %u) [bytes]\n", - (unsigned)(_free + _alloc), (unsigned)_alloc, (unsigned)_free); -} - -#else /* MODULE_ESP_IDF_HEAP */ - -/* for compatibility with ESP-IDF heap functions */ -void* IRAM heap_caps_malloc( size_t size, uint32_t caps ) -{ - (void)caps; - return malloc(size); -} - -void* IRAM heap_caps_calloc( size_t n, size_t size, uint32_t caps) -{ - (void)caps; - return calloc(n, size); -} - -void* IRAM heap_caps_realloc( void *ptr, size_t size ) -{ - return realloc(ptr, size); -} - -extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */ -extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */ -extern uint8_t *heap_top; /* current top of heap as defined in newlib_syscalls_default */ - -unsigned int IRAM get_free_heap_size (void) -{ - struct mallinfo minfo = mallinfo(); - return &_eheap - &_sheap - minfo.uordblks; -} - -void heap_stats(void) -{ - printf("heap: %u (used %u, free %u) [bytes]\n", (unsigned)(&_eheap - &_sheap), - &_eheap - &_sheap - get_free_heap_size(), get_free_heap_size()); + ets_printf("heap: %u (used %u, free %u) [bytes]\n", + _alloc + _free, _alloc, _free); } #endif /* MODULE_ESP_IDF_HEAP */ -/* alias for compatibility with espressif/wifi_libs */ -uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); - /** * @name Other system functions */ @@ -288,7 +82,7 @@ void _abort(void) { ets_printf("#! abort called: powering off\n"); pm_off(); - while(1); + while (1) { }; } void _exit_r(struct _reent *r, int status) @@ -296,19 +90,13 @@ void _exit_r(struct _reent *r, int status) _exit(status); } -struct _reent* __getreent(void) { - return _GLOBAL_REENT; -} - -static int _no_sys_func (struct _reent *r) +static int _no_sys_func(struct _reent *r) { DEBUG("%s: system function does not exist\n", __func__); r->_errno = ENOSYS; return -1; } -static struct _reent s_reent; - static struct syscall_stub_table s_stub_table = { .__getreent = &__getreent, @@ -360,42 +148,35 @@ static struct syscall_stub_table s_stub_table = #endif /* CONFIG_NEWLIB_NANO_FORMAT */ }; -void IRAM syscalls_init (void) +void IRAM syscalls_init_arch(void) { /* enable the system timer in us (TMG0 is enabled by default) */ TIMER_SYSTEM.config.divider = rtc_clk_apb_freq_get()/MHZ; TIMER_SYSTEM.config.autoreload = 0; TIMER_SYSTEM.config.enable = 1; + syscall_table_ptr_pro = &s_stub_table; syscall_table_ptr_app = &s_stub_table; - - _GLOBAL_REENT = &s_reent; - - environ = malloc(sizeof(char*)); - environ[0] = NULL; } -uint32_t system_get_time (void) +uint32_t system_get_time(void) { /* latch 64 bit timer value before read */ TIMER_SYSTEM.update = 0; - /* wait until instructions have been finished */ - __asm__ volatile ("isync"); + /* read the current timer value */ return TIMER_SYSTEM.cnt_low; } -uint32_t system_get_time_ms (void) +uint32_t system_get_time_ms(void) { return system_get_time_64() / USEC_PER_MSEC; } -uint64_t system_get_time_64 (void) +uint64_t system_get_time_64(void) { uint64_t ret; /* latch 64 bit timer value before read */ TIMER_SYSTEM.update = 0; - /* wait until instructions have been finished */ - __asm__ volatile ("isync"); /* read the current timer value */ ret = TIMER_SYSTEM.cnt_low; ret += ((uint64_t)TIMER_SYSTEM.cnt_high) << 32; @@ -411,7 +192,7 @@ static IRAM void system_wdt_int_handler(void *arg) system_wdt_feed(); } -void IRAM system_wdt_feed (void) +void IRAM system_wdt_feed(void) { DEBUG("%s\n", __func__); TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ @@ -419,7 +200,7 @@ void IRAM system_wdt_feed (void) TIMERG0.wdt_wprotect=0; /* enable write protection */ } -void system_wdt_init (void) +void system_wdt_init(void) { /* disable boot watchdogs */ TIMERG0.wdt_config0.flashboot_mod_en = 0; @@ -456,7 +237,7 @@ void system_wdt_init (void) xt_ints_on(BIT(CPU_INUM_WDT)); } -void system_wdt_stop (void) +void system_wdt_stop(void) { xt_ints_off(BIT(CPU_INUM_WDT)); TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ @@ -465,7 +246,7 @@ void system_wdt_stop (void) TIMERG0.wdt_wprotect = 0; /* enable write protection */ } -void system_wdt_start (void) +void system_wdt_start(void) { TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ TIMERG0.wdt_config0.en = 1; /* disable MWDT */ @@ -473,18 +254,3 @@ void system_wdt_start (void) TIMERG0.wdt_wprotect = 0; /* enable write protection */ xt_ints_on(BIT(CPU_INUM_WDT)); } - -__attribute__((weak)) void -_system_prevent_memset_lto(void *const s, int c, const size_t n) -{ - (void) s; - (void) c; - (void) n; -} - -void *system_secure_memset(void *s, int c, size_t n) -{ - memset(s, c, n); - _system_prevent_memset_lto(s, c, n); - return s; -} diff --git a/cpu/esp8266/include/syscalls.h b/cpu/esp8266/include/syscalls.h index 21185ec36e..77d3abca1f 100644 --- a/cpu/esp8266/include/syscalls.h +++ b/cpu/esp8266/include/syscalls.h @@ -21,38 +21,17 @@ #ifndef SYSCALLS_H #define SYSCALLS_H -#include -#include -#include -#include +#include "syscalls_common.h" #ifdef __cplusplus extern "C" { #endif -/** Necessary initializations of system call functions */ -extern void syscalls_init (void); +#ifndef DOXYGEN -/** System standard printf function */ -extern int printf(const char* format, ...); +#define MALLOC_CAP_DEFAULT MALLOC_CAP_8BIT -/** Determine free heap size */ -unsigned int get_free_heap_size (void); - -/** System standard puts function */ -extern int puts(const char * str); - -/** Determine free heap size */ -extern unsigned int get_free_heap_size (void); - -/** Time since boot in us (32bit version) */ -uint32_t system_get_time (void); - -/** Time since boot in ms (32bit version) */ -uint32_t system_get_time_ms (void); - -/** memset version that the compiler should not be allowed to optimize this */ -void *system_secure_memset(void *s, int c, size_t n); +#endif /* DOXYGEN */ #ifdef __cplusplus } diff --git a/cpu/esp8266/syscalls.c b/cpu/esp8266/syscalls.c index b8486903c1..40e3032c47 100644 --- a/cpu/esp8266/syscalls.c +++ b/cpu/esp8266/syscalls.c @@ -18,368 +18,36 @@ * @} */ -#include "periph/pm.h" -#include "timex.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mutex.h" -#include "rmutex.h" - -#include "esp_common.h" -#include "esp/common_macros.h" #include "esp_attr.h" +#include "esp_common.h" #include "sdk/sdk.h" -#include "syscalls.h" - -#ifdef MODULE_STDIO_UART -#include "stdio_uart.h" -#endif #ifdef MODULE_ESP_IDF_HEAP #include "esp_heap_caps.h" -#else -#include "malloc.h" #endif #define ENABLE_DEBUG (0) #include "debug.h" -#define MHZ 1000000UL - -#ifndef MODULE_PTHREAD - -#define PTHREAD_CANCEL_DISABLE 1 -/* - * This is a dummy function to avoid undefined references when linking - * against newlib and module pthread is not used. - */ -int pthread_setcancelstate(int state, int *oldstate) -{ - if (oldstate) { - *oldstate = PTHREAD_CANCEL_DISABLE; - } - return 0; -} -#endif /* MODULE_PTHREAD */ - -/* - * TODO: When the lock functions in this section are enabled, an application - * crashes when an ISR calls a `newlib` function that uses `_lock_acquire` - * or `_log_acquire_recursive` to be thread-safe, for example, `puts` in - * `tests/isr_yield_higher`. The reason is that the implementation of these - * functions uses `mutex` and `rmutex` that do not work in the interrupt - * context. Therefore, the lock functions are disabled for the moment, and - * instead `newlib`'s dummy lock functions are used which do not guarantee - * thread safety. - */ - -#if 0 - -/** - * @name Locking functions - * - * Following functions implement the lock mechanism for newlib. - */ - -/** - * _malloc_rmtx is defined as static variable to avoid recursive calls of - * malloc when _malloc_r tries to lock __malloc_lock_object the first - * time. All other mutexes that are used for the lock mechanism are allocated - * dynamically. - */ -static rmutex_t _malloc_rmtx = RMUTEX_INIT; - -/** - * To properly handle the static rmutex _malloc_rmtx, we have to know - * the address of newlib's static variable __malloc_lock_object. - */ -static _lock_t *__malloc_static_object = NULL; - -void IRAM _lock_init(_lock_t *lock) -{ - assert(lock != NULL); - - mutex_t* mtx = malloc(sizeof(mutex_t)); - - if (mtx) { - memset(mtx, 0, sizeof(mutex_t)); - *lock = (_lock_t)mtx; - } -} - -void IRAM _lock_init_recursive(_lock_t *lock) -{ - assert(lock != NULL); - - /** - * Since we don't have direct access to newlib's static variable - * __malloc_lock_object, we have to rely on the fact that function - * _lock_aqcuire_recursive, and thus function _lock_init_recursive - * is called for the first time with newlib's static variable - * __malloc_lock_object as parameter. This is ensured by calling - * malloc in the function syscalls_init. - */ - if (__malloc_static_object == NULL) { - *lock = (_lock_t)&_malloc_rmtx; - __malloc_static_object = lock; - return; - } - - /* _malloc_rmtx is static and has not to be allocated */ - if (lock == __malloc_static_object) { - return; - } - - rmutex_t* rmtx = malloc(sizeof(rmutex_t)); - - if (rmtx) { - memset(rmtx, 0, sizeof(rmutex_t)); - *lock = (_lock_t)rmtx; - } -} - -void IRAM _lock_close(_lock_t *lock) -{ - assert(lock != NULL); - assert(lock != __malloc_static_object); - - free((void*)*lock); - *lock = 0; -} - -void IRAM _lock_close_recursive(_lock_t *lock) -{ - assert(lock != NULL); - assert(lock != __malloc_static_object); - - free((void*)*lock); - *lock = 0; -} - -void IRAM _lock_acquire(_lock_t *lock) -{ - assert(lock != NULL); - - /* if the lock data structure is still not allocated, initialize it first */ - if (*lock == 0) { - _lock_init(lock); - } - - /* if scheduler is not running, we have not to lock the mutex */ - if (sched_active_thread == NULL) { - return; - } - - assert(!irq_is_in()); - mutex_lock((mutex_t*)*lock); -} - -void IRAM _lock_acquire_recursive(_lock_t *lock) -{ - assert(lock != NULL); - - /* if the lock data structure is still not allocated, initialize it first */ - if (*lock == 0) { - _lock_init_recursive(lock); - } - - /* if scheduler is not running, we have not to lock the rmutex */ - if (sched_active_thread == NULL) { - return; - } - - assert(!irq_is_in()); - rmutex_lock((rmutex_t*)*lock); -} - -int IRAM _lock_try_acquire(_lock_t *lock) -{ - assert(lock != NULL); - - /* if the lock data structure is still not allocated, initialize it first */ - if (*lock == 0) { - _lock_init(lock); - } - - /* if scheduler is not running, we have not to lock the mutex */ - if (sched_active_thread == NULL) { - return 0; - } - - if (irq_is_in()) { - return 0; - } - - return mutex_trylock((mutex_t*)*lock); -} - -int IRAM _lock_try_acquire_recursive(_lock_t *lock) -{ - assert(lock != NULL); - - /* if the lock data structure is still not allocated, initialize it first */ - if (*lock == 0) { - _lock_init_recursive(lock); - } - - /* if scheduler is not running, we have not to lock the rmutex */ - if (sched_active_thread == NULL) { - return 0; - } - - if (irq_is_in()) { - return 0; - } - - return rmutex_trylock((rmutex_t*)*lock); -} - -void IRAM _lock_release(_lock_t *lock) -{ - assert(lock != NULL && *lock != 0); - - /* if scheduler is not running, we have not to unlock the mutex */ - if (sched_active_thread == NULL) { - return; - } - - mutex_unlock((mutex_t*)*lock); -} - -void IRAM _lock_release_recursive(_lock_t *lock) -{ - assert(lock != NULL && *lock != 0); - - /* if scheduler is not running, we have not to unlock the rmutex */ - if (sched_active_thread == NULL) { - return; - } - - rmutex_unlock((rmutex_t*)*lock); -} - -#endif - -/** - * @name Memory allocation functions - */ - #ifdef MODULE_ESP_IDF_HEAP -#define MALLOC_CAP_DEFAULT MALLOC_CAP_8BIT -#define heap_caps_malloc_default(s) heap_caps_malloc(s, MALLOC_CAP_DEFAULT) -#define heap_caps_realloc_default(p,s) heap_caps_realloc(p, s, MALLOC_CAP_DEFAULT) - -void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size) -{ - return heap_caps_malloc_default( size ); -} - -void IRAM_ATTR __wrap__free_r(struct _reent *r, void *ptr) -{ - heap_caps_free( ptr ); -} - -void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size) -{ - return heap_caps_realloc_default( ptr, size ); -} - -void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size) -{ - void *result = heap_caps_malloc_default(count * size); - if (result) { - bzero(result, count * size); - } - return result; -} - -unsigned int get_free_heap_size(void) -{ - return heap_caps_get_free_size(MALLOC_CAP_DEFAULT); -} - +/* this function is platform specific if module esp_idf_heap is used */ void heap_stats(void) { + size_t _free = 0; + size_t _alloc = 0; + extern heap_region_t g_heap_region[HEAP_REGIONS_MAX]; for (int i = 0; i < HEAP_REGIONS_MAX; i++) { - ets_printf("Heap region %u @%p: %u (used %u, free %u) [bytes]\n", i, - g_heap_region[i].start_addr, - g_heap_region[i].total_size, - g_heap_region[i].total_size - g_heap_region[i].free_bytes, - g_heap_region[i].free_bytes); + _free += g_heap_region[i].free_bytes; + _alloc += g_heap_region[i].total_size - g_heap_region[i].free_bytes; } -} -#else /* MODULE_ESP_IDF_HEAP */ - -/* for compatibility with ESP-IDF heap functions */ - -void* _heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line) -{ - (void)caps; - return malloc(size); -} - -void* _heap_caps_calloc(size_t n, size_t size, uint32_t caps, const char *file, size_t line) -{ - (void)caps; - return calloc(n, size); -} - -void* _heap_caps_realloc(void *ptr, size_t size, uint32_t caps, const char *file, size_t line) -{ - return realloc(ptr, size); -} - -void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line) -{ - void *ptr = malloc(size); - if (ptr) { - memset(ptr, 0, size); - } - return ptr; -} - -void _heap_caps_free(void *ptr, const char *file, size_t line) -{ - (void)file; - (void)line; - free(ptr); -} - -void heap_caps_init(void) -{ -} - -extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */ -extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */ - -unsigned int IRAM get_free_heap_size(void) -{ - struct mallinfo minfo = mallinfo(); - return &_eheap - &_sheap - minfo.uordblks; -} - -void heap_stats(void) -{ ets_printf("heap: %u (used %u, free %u) [bytes]\n", - &_eheap - &_sheap, &_eheap - &_sheap - get_free_heap_size(), - get_free_heap_size()); + _alloc + _free, _alloc, _free); } -/* alias for compatibility with espressif/wifi_libs */ -uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); - #endif /* MODULE_ESP_IDF_HEAP */ /** @@ -391,108 +59,6 @@ int _rename_r(struct _reent *r, const char *from, const char *to) return 0; } -void _abort(void) -{ - ets_printf("#! abort called: powering off\n"); - pm_off(); - while (1) { }; -} - -void _exit_r(struct _reent *r, int status) -{ - _exit(status); -} - -struct _reent* __getreent(void) { - return _GLOBAL_REENT; -} - -#ifdef MCU_ESP32 -static int _no_sys_func(struct _reent *r) -{ - DEBUG("%s: system function does not exist\n", __func__); - r->_errno = ENOSYS; - return -1; -} -#endif - -static struct _reent s_reent; - -#ifdef MCU_ESP32 -static struct syscall_stub_table s_stub_table = -{ - .__getreent = &__getreent, - - ._malloc_r = &_malloc_r, - ._free_r = &_free_r, - ._realloc_r = &_realloc_r, - ._calloc_r = &_calloc_r, - ._sbrk_r = &_sbrk_r, - - ._system_r = (int (*)(struct _reent *, const char*))&_no_sys_func, - ._raise_r = (void (*)(struct _reent *))&_no_sys_func, - ._abort = &_abort, - ._exit_r = &_exit_r, - ._getpid_r = &_getpid_r, - ._kill_r = &_kill_r, - - ._times_r = &_times_r, - ._gettimeofday_r = _gettimeofday_r, - - ._open_r = &_open_r, - ._close_r = &_close_r, - ._lseek_r = (int (*)(struct _reent *r, int, int, int))&_lseek_r, - ._fstat_r = &_fstat_r, - ._stat_r = &_stat_r, - ._write_r = (int (*)(struct _reent *r, int, const void *, int))&_write_r, - ._read_r = (int (*)(struct _reent *r, int, void *, int))&_read_r, - ._unlink_r = &_unlink_r, - ._link_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func, - ._rename_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func, - - ._lock_init = &_lock_init, - ._lock_init_recursive = &_lock_init_recursive, - ._lock_close = &_lock_close, - ._lock_close_recursive = &_lock_close_recursive, - ._lock_acquire = &_lock_acquire, - ._lock_acquire_recursive = &_lock_acquire_recursive, - ._lock_try_acquire = &_lock_try_acquire, - ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, - ._lock_release = &_lock_release, - ._lock_release_recursive = &_lock_release_recursive, - - #if CONFIG_NEWLIB_NANO_FORMAT - ._printf_float = &_printf_float, - ._scanf_float = &_scanf_float, - #else /* CONFIG_NEWLIB_NANO_FORMAT */ - ._printf_float = NULL, - ._scanf_float = NULL, - #endif /* CONFIG_NEWLIB_NANO_FORMAT */ -}; -#endif - -void syscalls_init(void) -{ -#ifdef MCU_ESP32 - /* enable the system timer in us (TMG0 is enabled by default) */ - TIMER_SYSTEM.config.divider = rtc_clk_apb_freq_get()/MHZ; - TIMER_SYSTEM.config.autoreload = 0; - TIMER_SYSTEM.config.enable = 1; - syscall_table_ptr_pro = &s_stub_table; - syscall_table_ptr_app = &s_stub_table; -#endif - _GLOBAL_REENT = &s_reent; - - environ = malloc(sizeof(char*)); - environ[0] = NULL; - - /* - * initialization of newlib, includes the ctors initialization - */ - extern void __libc_init_array(void); - __libc_init_array(); -} - uint32_t system_get_time(void) { return phy_get_mactime(); @@ -500,107 +66,9 @@ uint32_t system_get_time(void) uint32_t system_get_time_ms(void) { - return phy_get_mactime() / US_PER_MS; + return system_get_time() / USEC_PER_MSEC; } -#ifdef MCU_ESP32 -uint64_t system_get_time_64(void) +void IRAM_ATTR syscalls_init_arch(void) { - uint64_t ret; - /* latch 64 bit timer value before read */ - TIMER_SYSTEM.update = 0; - /* wait until instructions have been finished */ - __asm__ volatile ("isync"); - /* read the current timer value */ - ret = TIMER_SYSTEM.cnt_low; - ret += ((uint64_t)TIMER_SYSTEM.cnt_high) << 32; - return ret; -} - -/* alias for compatibility with espressif/wifi_libs */ -int64_t esp_timer_get_time(void) __attribute__((alias("system_get_time_64"))); - -static IRAM void system_wdt_int_handler(void *arg) -{ - TIMERG0.int_clr_timers.wdt=1; /* clear interrupt */ - system_wdt_feed(); -} - -void IRAM system_wdt_feed(void) -{ - DEBUG("%s\n", __func__); - TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ - TIMERG0.wdt_feed=1; /* reset MWDT */ - TIMERG0.wdt_wprotect=0; /* enable write protection */ -} - -void system_wdt_init(void) -{ - /* disable boot watchdogs */ - TIMERG0.wdt_config0.flashboot_mod_en = 0; - RTCCNTL.wdt_config0.flashboot_mod_en = 0; - - /* enable system watchdog */ - TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ - TIMERG0.wdt_config0.stg0 = TIMG_WDT_STG_SEL_INT; /* stage0 timeout: interrupt */ - TIMERG0.wdt_config0.stg1 = TIMG_WDT_STG_SEL_RESET_SYSTEM; /* stage1 timeout: sys reset */ - TIMERG0.wdt_config0.sys_reset_length = 7; /* sys reset signal length: 3.2 us */ - TIMERG0.wdt_config0.cpu_reset_length = 7; /* sys reset signal length: 3.2 us */ - TIMERG0.wdt_config0.edge_int_en = 0; - TIMERG0.wdt_config0.level_int_en = 1; - - /* MWDT clock = 80 * 12,5 ns = 1 us */ - TIMERG0.wdt_config1.clk_prescale = 80; - - /* define stage timeouts */ - TIMERG0.wdt_config2 = 2 * US_PER_SEC; /* stage 0: 2 s (interrupt) */ - TIMERG0.wdt_config3 = 4 * US_PER_SEC; /* stage 1: 4 s (sys reset) */ - - TIMERG0.wdt_config0.en = 1; /* enable MWDT */ - TIMERG0.wdt_feed = 1; /* reset MWDT */ - TIMERG0.wdt_wprotect = 0; /* enable write protection */ - - DEBUG("%s TIMERG0 wdt_config0=%08x wdt_config1=%08x wdt_config2=%08x\n", - __func__, TIMERG0.wdt_config0.val, TIMERG0.wdt_config1.val, - TIMERG0.wdt_config2); - - /* route WDT peripheral interrupt source to CPU_INUM_WDT */ - intr_matrix_set(PRO_CPU_NUM, ETS_TG0_WDT_LEVEL_INTR_SOURCE, CPU_INUM_WDT); - /* set the interrupt handler and activate the interrupt */ - xt_set_interrupt_handler(CPU_INUM_WDT, system_wdt_int_handler, NULL); - xt_ints_on(BIT(CPU_INUM_WDT)); -} - -void system_wdt_stop(void) -{ - xt_ints_off(BIT(CPU_INUM_WDT)); - TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ - TIMERG0.wdt_config0.en = 0; /* disable MWDT */ - TIMERG0.wdt_feed = 1; /* reset MWDT */ - TIMERG0.wdt_wprotect = 0; /* enable write protection */ -} - -void system_wdt_start(void) -{ - TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ - TIMERG0.wdt_config0.en = 1; /* disable MWDT */ - TIMERG0.wdt_feed = 1; /* reset MWDT */ - TIMERG0.wdt_wprotect = 0; /* enable write protection */ - xt_ints_on(BIT(CPU_INUM_WDT)); -} -#endif - -__attribute__((weak)) void -_system_prevent_memset_lto(void *const s, int c, const size_t n) -{ - (void)s; - (void)c; - (void)n; -} - -void *system_secure_memset(void *s, int c, size_t n) -{ - memset(s, c, n); - _system_prevent_memset_lto(s, c, n); - return s; } diff --git a/cpu/esp_common/include/syscalls_common.h b/cpu/esp_common/include/syscalls_common.h new file mode 100644 index 0000000000..2198194fac --- /dev/null +++ b/cpu/esp_common/include/syscalls_common.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp_common + * @{ + * + * @file + * @brief Implementation of required system calls for ESP SoCs + * + * @author Gunar Schorcht + * + * @} + */ + +#ifndef SYSCALLS_COMMON_H +#define SYSCALLS_COMMON_H + +#include +#include +#include +#include + +#include "esp_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Necessary initializations of system call functions */ +void syscalls_init(void); + +/** Determine free heap size */ +unsigned int get_free_heap_size(void); + +/** Time since boot in us (32bit version) */ +uint32_t system_get_time(void); + +/** Time since boot in ms (32bit version) */ +uint32_t system_get_time_ms(void); + +/** memset version that the compiler should not be allowed to optimize this */ +void *system_secure_memset(void *s, int c, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* SYSCALLS_COMMON_H */ diff --git a/cpu/esp_common/syscalls.c b/cpu/esp_common/syscalls.c new file mode 100644 index 0000000000..cbb6a48656 --- /dev/null +++ b/cpu/esp_common/syscalls.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2019 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp_common + * @{ + * + * @file + * @brief Implementation of required system calls + * + * @author Gunar Schorcht + * + * @} + */ + +#include +#include +#include + +#include "irq_arch.h" +#include "mutex.h" +#include "rmutex.h" +#include "timex.h" + +#include "esp_attr.h" +#include "syscalls.h" + +#ifdef MODULE_ESP_IDF_HEAP +#include "esp_heap_caps.h" +#else +#include "malloc.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifndef MODULE_PTHREAD + +#define PTHREAD_CANCEL_DISABLE 1 +/* + * This is a dummy function to avoid undefined references when linking + * against newlib and module pthread is not used. + */ +int pthread_setcancelstate(int state, int *oldstate) +{ + if (oldstate) { + *oldstate = PTHREAD_CANCEL_DISABLE; + } + return 0; +} +#endif /* MODULE_PTHREAD */ + +/* + * TODO: When the lock functions in this section are enabled, an application + * crashes when an ISR calls a `newlib` function that uses `_lock_acquire` + * or `_log_acquire_recursive` to be thread-safe, for example, `puts` in + * `tests/isr_yield_higher`. The reason is that the implementation of these + * functions uses `mutex` and `rmutex` that do not work in the interrupt + * context. Therefore, the lock functions are disabled for the moment, and + * instead `newlib`'s dummy lock functions are used which do not guarantee + * thread safety. + */ + +/** + * @name Locking functions + * + * Following functions implement the lock mechanism for newlib. + */ + +/** + * _malloc_rmtx is defined as static variable to avoid recursive calls of + * malloc when _malloc_r tries to lock __malloc_lock_object the first + * time. All other mutexes that are used for the lock mechanism are allocated + * dynamically. + */ +static rmutex_t _malloc_rmtx = RMUTEX_INIT; + +/** + * To properly handle the static rmutex _malloc_rmtx, we have to know + * the address of newlib's static variable __malloc_lock_object. + */ +static _lock_t *__malloc_static_object = NULL; + +void IRAM_ATTR _lock_init(_lock_t *lock) +{ + assert(lock != NULL); + + mutex_t* mtx = malloc(sizeof(mutex_t)); + + if (mtx) { + memset(mtx, 0, sizeof(mutex_t)); + *lock = (_lock_t)mtx; + } +} + +void IRAM_ATTR _lock_init_recursive(_lock_t *lock) +{ + assert(lock != NULL); + + /** + * Since we don't have direct access to newlib's static variable + * __malloc_lock_object, we have to rely on the fact that function + * _lock_aqcuire_recursive, and thus function _lock_init_recursive + * is called for the first time with newlib's static variable + * __malloc_lock_object as parameter. This is ensured by calling + * malloc in the function syscalls_init. + */ + if (__malloc_static_object == NULL) { + *lock = (_lock_t)&_malloc_rmtx; + __malloc_static_object = lock; + return; + } + + /* _malloc_rmtx is static and has not to be allocated */ + if (lock == __malloc_static_object) { + return; + } + + rmutex_t* rmtx = malloc(sizeof(rmutex_t)); + + if (rmtx) { + memset(rmtx, 0, sizeof(rmutex_t)); + *lock = (_lock_t)rmtx; + } +} + +void IRAM_ATTR _lock_close(_lock_t *lock) +{ + assert(lock != NULL); + assert(lock != __malloc_static_object); + + free((void*)*lock); + *lock = 0; +} + +void IRAM_ATTR _lock_close_recursive(_lock_t *lock) +{ + assert(lock != NULL); + assert(lock != __malloc_static_object); + + free((void*)*lock); + *lock = 0; +} + +void IRAM_ATTR _lock_acquire(_lock_t *lock) +{ + assert(lock != NULL); + + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init(lock); + } + + /* if scheduler is not running, we have not to lock the mutex */ + if (sched_active_thread == NULL) { + return; + } + + assert(!irq_is_in()); + mutex_lock((mutex_t*)*lock); +} + +void IRAM_ATTR _lock_acquire_recursive(_lock_t *lock) +{ + assert(lock != NULL); + + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init_recursive(lock); + } + + /* if scheduler is not running, we have not to lock the rmutex */ + if (sched_active_thread == NULL) { + return; + } + + assert(!irq_is_in()); + rmutex_lock((rmutex_t*)*lock); +} + +int IRAM_ATTR _lock_try_acquire(_lock_t *lock) +{ + assert(lock != NULL); + + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init(lock); + } + + /* if scheduler is not running, we have not to lock the mutex */ + if (sched_active_thread == NULL) { + return 0; + } + + if (irq_is_in()) { + return 0; + } + + return mutex_trylock((mutex_t*)*lock); +} + +int IRAM_ATTR _lock_try_acquire_recursive(_lock_t *lock) +{ + assert(lock != NULL); + + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init_recursive(lock); + } + + /* if scheduler is not running, we have not to lock the rmutex */ + if (sched_active_thread == NULL) { + return 0; + } + + if (irq_is_in()) { + return 0; + } + + return rmutex_trylock((rmutex_t*)*lock); +} + +void IRAM_ATTR _lock_release(_lock_t *lock) +{ + assert(lock != NULL && *lock != 0); + + /* if scheduler is not running, we have not to unlock the mutex */ + if (sched_active_thread == NULL) { + return; + } + + mutex_unlock((mutex_t*)*lock); +} + +void IRAM_ATTR _lock_release_recursive(_lock_t *lock) +{ + assert(lock != NULL && *lock != 0); + + /* if scheduler is not running, we have not to unlock the rmutex */ + if (sched_active_thread == NULL) { + return; + } + + rmutex_unlock((rmutex_t*)*lock); +} + +/** + * @name Memory allocation functions + */ + +#ifdef MODULE_ESP_IDF_HEAP + +#define heap_caps_malloc_default(s) heap_caps_malloc(s, MALLOC_CAP_DEFAULT) +#define heap_caps_realloc_default(p,s) heap_caps_realloc(p, s, MALLOC_CAP_DEFAULT) + +void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size) +{ + return heap_caps_malloc_default( size ); +} + +void IRAM_ATTR __wrap__free_r(struct _reent *r, void *ptr) +{ + heap_caps_free( ptr ); +} + +void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size) +{ + return heap_caps_realloc_default( ptr, size ); +} + +void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size) +{ + void *result = heap_caps_malloc_default(count * size); + if (result) { + bzero(result, count * size); + } + return result; +} + +#else /* MODULE_ESP_IDF_HEAP */ + +/* for compatibility with ESP-IDF heap functions */ + +void* _heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line) +{ + (void)caps; + return malloc(size); +} + +void* _heap_caps_calloc(size_t n, size_t size, uint32_t caps, const char *file, size_t line) +{ + (void)caps; + return calloc(n, size); +} + +void* _heap_caps_realloc(void *ptr, size_t size, uint32_t caps, const char *file, size_t line) +{ + return realloc(ptr, size); +} + +void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line) +{ + void *ptr = malloc(size); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +void _heap_caps_free(void *ptr, const char *file, size_t line) +{ + (void)file; + (void)line; + free(ptr); +} + +void heap_caps_init(void) +{ +} + +extern uint8_t _eheap; /* end of heap (defined in ld script) */ +extern uint8_t _sheap; /* start of heap (defined in ld script) */ + +unsigned int IRAM_ATTR get_free_heap_size(void) +{ + struct mallinfo minfo = mallinfo(); + return &_eheap - &_sheap - minfo.uordblks; +} + +void heap_stats(void) +{ + ets_printf("heap: %u (used %u, free %u) [bytes]\n", + &_eheap - &_sheap, &_eheap - &_sheap - get_free_heap_size(), + get_free_heap_size()); +} + +/* alias for compatibility with espressif/wifi_libs */ +uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); + +#endif /* MODULE_ESP_IDF_HEAP */ + +/** + * @name Other system functions + */ + +struct _reent* __getreent(void) { + return _GLOBAL_REENT; +} + +static struct _reent s_reent; + +void syscalls_init(void) +{ + extern void syscalls_init_arch(void); + syscalls_init_arch(); + + _GLOBAL_REENT = &s_reent; + + environ = malloc(sizeof(char*)); + environ[0] = NULL; + + /* initialization of newlib, includes the ctors initialization */ + extern void __libc_init_array(void); + __libc_init_array(); + + /* initialization of global reent data structure */ + _REENT_SMALL_CHECK_INIT(_GLOBAL_REENT); + + /* + * disable the locking for stdout/stderr to avoid rmutex based locking + * when puts/printf are called from an ISR + */ + __fsetlocking(_GLOBAL_REENT->_stdout, FSETLOCKING_BYCALLER); + __fsetlocking(_GLOBAL_REENT->_stderr, FSETLOCKING_BYCALLER); +} + +__attribute__((weak)) void +_system_prevent_memset_lto(void *const s, int c, const size_t n) +{ + (void)s; + (void)c; + (void)n; +} + +void *system_secure_memset(void *s, int c, size_t n) +{ + memset(s, c, n); + _system_prevent_memset_lto(s, c, n); + return s; +}