/* Simple C++ startup routine to setup CRT SPDX-License-Identifier: Unlicense (https://five-embeddev.com/ | http://www.shincbm.com/) */ #include #include #include #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C extern #endif // Generic C function pointer. typedef void(*function_t)(void) ; // These symbols are defined by the linker script. // See linker.lds EXTERN_C uint8_t __bss_start; EXTERN_C uint8_t __bss_end; EXTERN_C const uint8_t __data_source; EXTERN_C uint8_t __data_start; EXTERN_C uint8_t __data_end; EXTERN_C const uint8_t __data_source; EXTERN_C uint8_t __tbss_start; EXTERN_C uintptr_t __tbss_size; EXTERN_C const uint8_t __tdata_source; EXTERN_C uint8_t __tls_base; EXTERN_C uintptr_t __tdata_size; EXTERN_C function_t __init_array_start; EXTERN_C function_t __init_array_end; EXTERN_C function_t __fini_array_start; EXTERN_C function_t __fini_array_end; // This function will be placed by the linker script according to the section // Raw function 'called' by the CPU with no runtime. EXTERN_C void _start(void) __attribute__ ((naked,section(".text.boot"))); // Entry and exit points as C functions. EXTERN_C void _initialize(void) __attribute__ ((noreturn,section(".text.boot"))); EXTERN_C void _exit(int exit_code) __attribute__ ((noreturn,noinline,weak)); // Standard entry point, no arguments. extern int main(void); // The linker script will place this in the reset entry point. // It will be 'called' with no stack or C runtime configuration. // NOTE - this only supports a single hart. // tp will not be initialized void _start(void) { // Setup SP and GP // The locations are defined in the linker script __asm__ volatile ( ".option push;" // The 'norelax' option is critical here. // Without 'norelax' the global pointer will // be loaded relative to the global pointer! ".option norelax;" "la gp, __global_pointer$;" ".option pop;" "la sp, _sp;" "jal zero, _initialize;" : /* output: none %0 */ : /* input: none */ : /* clobbers: none */); // This point will not be executed, _initialize() will be called with no return. } // At this point we have a stack and global poiner, but no access to global variables. void _initialize(void) { // Init memory regions // Clear the .bss section (global variables with no initial values) memset((void*) &__bss_start, 0, (&__bss_end - &__bss_start)); // Initialize the .data section (global variables with initial values) memcpy((void*)&__data_start, (const void*)&__data_source, (&__data_end - &__data_start)); // Clear the .tbss section (thread local variables with no initial values) memset((void*) &__tbss_start, 0, __tbss_size); // Initialize the .tls section (thread local variables with initial values) memcpy((void*) &__tls_base, (const void*)&__tdata_source, __tdata_size); // Call constructors for (const function_t* entry=&__init_array_start; entry < &__init_array_end; ++entry) { (*entry)(); } #ifdef __THREAD_LOCAL_STORAGE _set_tls(__tls_base) #endif int rc = main(); // Call destructors for (const function_t* entry=&__fini_array_start; entry < &__fini_array_end; ++entry) { (*entry)(); } _exit(rc); } // This should never be called. Busy loop with the CPU in idle state. void _exit(int exit_code) { (void)exit_code; // Halt while (1) { __asm__ volatile ("wfi"); } }