mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #8488 from c1728p9/cthunk_overhaul
Rewrite CThunk so it does not execute from rampull/8677/head
commit
0fe6369950
|
|
@ -38,45 +38,7 @@
|
|||
#ifndef __CTHUNK_H__
|
||||
#define __CTHUNK_H__
|
||||
|
||||
#define CTHUNK_ADDRESS 1
|
||||
#define CTHUNK_VARIABLES volatile uint32_t code[2]
|
||||
|
||||
#if (defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__CORTEX_M7) || defined(__CORTEX_A9) \
|
||||
|| defined(__CORTEX_M33))
|
||||
/**
|
||||
* CTHUNK disassembly for Cortex-M3/M4/M7/A9 (thumb2):
|
||||
* * adr r0, #4
|
||||
* * ldm r0, {r0, r1, r2, pc}
|
||||
*
|
||||
* This instruction loads the arguments for the static thunking function to r0-r2, and
|
||||
* branches to that function by loading its address into PC.
|
||||
*
|
||||
* This is safe for both regular calling and interrupt calling, since it only touches scratch registers
|
||||
* which should be saved by the caller, and are automatically saved as part of the IRQ context switch.
|
||||
*/
|
||||
#define CTHUNK_ASSIGMENT do { \
|
||||
m_thunk.code[0] = 0xE890A001; \
|
||||
m_thunk.code[1] = 0x00008007; \
|
||||
} while (0)
|
||||
|
||||
#elif (defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__CORTEX_M23))
|
||||
/*
|
||||
* CTHUNK disassembly for Cortex M0/M0+ (thumb):
|
||||
* * adr r0, #4
|
||||
* * ldm r0, {r0, r1, r2, r3}
|
||||
* * bx r3
|
||||
*/
|
||||
#define CTHUNK_ASSIGMENT do { \
|
||||
m_thunk.code[0] = 0xC80FA001; \
|
||||
m_thunk.code[1] = 0x00004718; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#error "Target is not currently suported."
|
||||
#endif
|
||||
|
||||
/* IRQ/Exception compatible thunk entry function */
|
||||
typedef void (*CThunkEntry)(void);
|
||||
#include "CThunkBase.h"
|
||||
|
||||
/**
|
||||
* Class for created a pointer with data bound to it
|
||||
|
|
@ -84,7 +46,7 @@ typedef void (*CThunkEntry)(void);
|
|||
* @note Synchronization level: Not protected
|
||||
*/
|
||||
template<class T>
|
||||
class CThunk {
|
||||
class CThunk: public CThunkBase {
|
||||
public:
|
||||
typedef void (T::*CCallbackSimple)(void);
|
||||
typedef void (T::*CCallback)(void *context);
|
||||
|
|
@ -101,7 +63,8 @@ public:
|
|||
|
||||
~CThunk()
|
||||
{
|
||||
|
||||
cthunk_free(_entry);
|
||||
_entry = NULL;
|
||||
}
|
||||
|
||||
inline CThunk(T *instance, CCallbackSimple callback)
|
||||
|
|
@ -126,27 +89,30 @@ public:
|
|||
|
||||
inline void callback(CCallback callback)
|
||||
{
|
||||
m_callback = callback;
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
inline void callback(CCallbackSimple callback)
|
||||
{
|
||||
m_callback = (CCallback)callback;
|
||||
_callback_simple = callback;
|
||||
}
|
||||
|
||||
inline void context(void *context)
|
||||
{
|
||||
m_thunk.context = (uint32_t)context;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
inline void context(uint32_t context)
|
||||
{
|
||||
m_thunk.context = context;
|
||||
_context = (void*)context;
|
||||
}
|
||||
|
||||
inline uint32_t entry(void)
|
||||
{
|
||||
return (((uint32_t)&m_thunk) | CTHUNK_ADDRESS);
|
||||
if (_entry == NULL) {
|
||||
_entry = cthunk_alloc(this);
|
||||
}
|
||||
return (uint32_t)_entry;
|
||||
}
|
||||
|
||||
/* get thunk entry point for connecting rhunk to an IRQ table */
|
||||
|
|
@ -168,78 +134,34 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
T *m_instance;
|
||||
volatile CCallback m_callback;
|
||||
T *_instance;
|
||||
void *_context;
|
||||
union {
|
||||
CCallbackSimple _callback_simple;
|
||||
CCallback _callback;
|
||||
};
|
||||
|
||||
// TODO: this needs proper fix, to refactor toolchain header file and all its use
|
||||
// PACKED there is not defined properly for IAR
|
||||
#if defined (__ICCARM__)
|
||||
typedef __packed struct {
|
||||
CTHUNK_VARIABLES;
|
||||
volatile uint32_t instance;
|
||||
volatile uint32_t context;
|
||||
volatile uint32_t callback;
|
||||
volatile uint32_t trampoline;
|
||||
} CThunkTrampoline;
|
||||
#else
|
||||
typedef struct {
|
||||
CTHUNK_VARIABLES;
|
||||
volatile uint32_t instance;
|
||||
volatile uint32_t context;
|
||||
volatile uint32_t callback;
|
||||
volatile uint32_t trampoline;
|
||||
} __attribute__((__packed__)) CThunkTrampoline;
|
||||
#endif
|
||||
CThunkEntry _entry;
|
||||
|
||||
static void trampoline(T *instance, void *context, CCallback *callback)
|
||||
static void trampoline(CThunkBase *base)
|
||||
{
|
||||
if (instance && *callback) {
|
||||
(static_cast<T *>(instance)->**callback)(context);
|
||||
}
|
||||
}
|
||||
CThunk<T> *self = static_cast<CThunk<T>*>(base);
|
||||
T *instance = self->_instance;
|
||||
void *context = self->_context;
|
||||
CCallback callback = self->_callback;
|
||||
|
||||
volatile CThunkTrampoline m_thunk;
|
||||
if (instance && callback) {
|
||||
(instance->*callback)(context);
|
||||
}
|
||||
}
|
||||
|
||||
inline void init(T *instance, CCallback callback, void *context)
|
||||
{
|
||||
/* remember callback - need to add this level of redirection
|
||||
as pointer size for member functions differs between platforms */
|
||||
m_callback = callback;
|
||||
|
||||
/* populate thunking trampoline */
|
||||
CTHUNK_ASSIGMENT;
|
||||
m_thunk.context = (uint32_t)context;
|
||||
m_thunk.instance = (uint32_t)instance;
|
||||
m_thunk.callback = (uint32_t)&m_callback;
|
||||
m_thunk.trampoline = (uint32_t)&trampoline;
|
||||
|
||||
#if defined(__CORTEX_A9)
|
||||
/* Data cache clean */
|
||||
/* Cache control */
|
||||
{
|
||||
uint32_t start_addr = (uint32_t)&m_thunk & 0xFFFFFFE0;
|
||||
uint32_t end_addr = (uint32_t)&m_thunk + sizeof(m_thunk);
|
||||
uint32_t addr;
|
||||
|
||||
/* Data cache clean and invalid */
|
||||
for (addr = start_addr; addr < end_addr; addr += 0x20) {
|
||||
L1C_CleanInvalidateDCacheMVA((void *)addr);
|
||||
}
|
||||
/* Instruction cache invalid */
|
||||
L1C_InvalidateICacheAll();
|
||||
MMU_InvalidateTLB();
|
||||
L1C_InvalidateBTAC();
|
||||
}
|
||||
#endif
|
||||
#if defined(__CORTEX_M7)
|
||||
/* Data cache clean and invalid */
|
||||
SCB_CleanInvalidateDCache();
|
||||
|
||||
/* Instruction cache invalid */
|
||||
SCB_InvalidateICache();
|
||||
#endif
|
||||
__ISB();
|
||||
__DSB();
|
||||
_instance = instance;
|
||||
_context = context;
|
||||
_callback = callback;
|
||||
_trampoline = &trampoline;
|
||||
_entry = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/mbed_critical.h"
|
||||
#include "platform/mbed_assert.h"
|
||||
#include "platform/mbed_error.h"
|
||||
|
||||
|
||||
#include "CThunkBase.h"
|
||||
|
||||
MBED_STATIC_ASSERT(MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX < 256, "MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX must be less than 256");
|
||||
MBED_STATIC_ASSERT(MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX > 0, "MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX must be greater than 0");
|
||||
|
||||
#define ENABLE_N(N) ((MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX & N) ? 1 : 0)
|
||||
|
||||
#define START_128 0
|
||||
#define START_64 (START_128 + ENABLE_N(128) * 128)
|
||||
#define START_32 (START_64 + ENABLE_N(64) * 64)
|
||||
#define START_16 (START_32 + ENABLE_N(32) * 32)
|
||||
#define START_8 (START_16 + ENABLE_N(16) * 16)
|
||||
#define START_4 (START_8 + ENABLE_N(8) * 8)
|
||||
#define START_2 (START_4 + ENABLE_N(4) * 4)
|
||||
#define START_1 (START_2 + ENABLE_N(2) * 2)
|
||||
|
||||
#define DECLARE_THUNK128(start) \
|
||||
DECLARE_THUNK64(start), \
|
||||
DECLARE_THUNK64(start + 64)
|
||||
#define DECLARE_THUNK64(start) \
|
||||
DECLARE_THUNK32(start), \
|
||||
DECLARE_THUNK32(start + 32)
|
||||
#define DECLARE_THUNK32(start) \
|
||||
DECLARE_THUNK16(start), \
|
||||
DECLARE_THUNK16(start + 16)
|
||||
#define DECLARE_THUNK16(start) \
|
||||
DECLARE_THUNK8(start), \
|
||||
DECLARE_THUNK8(start + 8)
|
||||
#define DECLARE_THUNK8(start) \
|
||||
DECLARE_THUNK4(start), \
|
||||
DECLARE_THUNK4(start + 4)
|
||||
#define DECLARE_THUNK4(start) \
|
||||
DECLARE_THUNK2(start), \
|
||||
DECLARE_THUNK2(start + 2)
|
||||
#define DECLARE_THUNK2(start) \
|
||||
DECLARE_THUNK1(start), \
|
||||
DECLARE_THUNK1(start + 1)
|
||||
#define DECLARE_THUNK1(index) &CThunkBase::thunk_entry<index>
|
||||
|
||||
const CThunkEntry CThunkBase::_thunk_table[MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX] = {
|
||||
#if ENABLE_N(128)
|
||||
DECLARE_THUNK128(START_128),
|
||||
#endif
|
||||
#if ENABLE_N(64)
|
||||
DECLARE_THUNK64(START_64),
|
||||
#endif
|
||||
#if ENABLE_N(32)
|
||||
DECLARE_THUNK32(START_32),
|
||||
#endif
|
||||
#if ENABLE_N(16)
|
||||
DECLARE_THUNK16(START_16),
|
||||
#endif
|
||||
#if ENABLE_N(8)
|
||||
DECLARE_THUNK8(START_8),
|
||||
#endif
|
||||
#if ENABLE_N(4)
|
||||
DECLARE_THUNK4(START_4),
|
||||
#endif
|
||||
#if ENABLE_N(2)
|
||||
DECLARE_THUNK2(START_2),
|
||||
#endif
|
||||
#if ENABLE_N(1)
|
||||
DECLARE_THUNK1(START_1)
|
||||
#endif
|
||||
};
|
||||
|
||||
CThunkBase *CThunkBase::_thunk_storage[MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX];
|
||||
|
||||
CThunkBase::CthunkFree CThunkBase::_cthunk_free_real = NULL;
|
||||
|
||||
CThunkEntry CThunkBase::cthunk_alloc(CThunkBase *cthunk)
|
||||
{
|
||||
// Atomically allocate one entry
|
||||
core_util_critical_section_enter();
|
||||
CThunkEntry entry = NULL;
|
||||
for (int i = 0; i < MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX; i++) {
|
||||
if (_thunk_storage[i] == NULL) {
|
||||
_thunk_storage[i] = cthunk;
|
||||
entry = _thunk_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
core_util_critical_section_exit();
|
||||
|
||||
if (entry == NULL) {
|
||||
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_OUT_OF_RESOURCES), "Ran out of CThunk entries. Increase MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX to fix this error");
|
||||
}
|
||||
|
||||
// Set function pointer on first use. This allows _thunk_table
|
||||
// and _thunk_storage to get removed by the linker if
|
||||
// cthunk_alloc is never used.
|
||||
_cthunk_free_real = &cthunk_free_real;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void CThunkBase::cthunk_free(CThunkEntry item)
|
||||
{
|
||||
if (_cthunk_free_real) {
|
||||
_cthunk_free_real(item);
|
||||
}
|
||||
}
|
||||
|
||||
void CThunkBase::cthunk_free_real(CThunkEntry item)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
core_util_critical_section_enter();
|
||||
for (int i = 0; i < MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX; i++) {
|
||||
if (_thunk_table[i] == item) {
|
||||
_thunk_storage[i] = NULL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
core_util_critical_section_exit();
|
||||
|
||||
if (!found) {
|
||||
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_INVALID_ARGUMENT), "Tried to free invalid CThunkEntry");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __CTHUNK_BASE_H__
|
||||
#define __CTHUNK_BASE_H__
|
||||
|
||||
/* IRQ/Exception compatible thunk entry function */
|
||||
typedef void (*CThunkEntry)(void);
|
||||
|
||||
class CThunkBase {
|
||||
protected:
|
||||
typedef void (*Trampoline)(CThunkBase*);
|
||||
|
||||
Trampoline _trampoline;
|
||||
|
||||
/*
|
||||
* Allocate a CThunkEntry which can be called without arguments
|
||||
*
|
||||
* Calling the CThunkEntry invokes the _trampoline of the
|
||||
* given cthunk. This function traps if there are no more
|
||||
* free thunks.
|
||||
*/
|
||||
static CThunkEntry cthunk_alloc(CThunkBase *cthunk);
|
||||
|
||||
/*
|
||||
* Free a cthunk_entry so it can be reused
|
||||
*/
|
||||
static void cthunk_free(CThunkEntry cthunk_entry);
|
||||
|
||||
private:
|
||||
typedef void (*CthunkFree)(CThunkEntry cthunk_entry);
|
||||
|
||||
/*
|
||||
* Table of thunk functions
|
||||
*/
|
||||
static const CThunkEntry _thunk_table[MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX];
|
||||
|
||||
/*
|
||||
* Table of active CThunk classes
|
||||
*/
|
||||
static CThunkBase *_thunk_storage[MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX];
|
||||
|
||||
/*
|
||||
* Lazily initialized free function pointer
|
||||
*/
|
||||
static CthunkFree _cthunk_free_real;
|
||||
|
||||
/*
|
||||
* Actual free function
|
||||
*/
|
||||
static void cthunk_free_real(CThunkEntry cthunk_entry);
|
||||
|
||||
/*
|
||||
* Template function which stored in the _thunk_table
|
||||
*/
|
||||
template<int N>
|
||||
static void thunk_entry()
|
||||
{
|
||||
_thunk_storage[N]->_trampoline(_thunk_storage[N]);
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__CTHUNK_BASE_H__*/
|
||||
|
||||
|
|
@ -98,6 +98,10 @@
|
|||
"error-decode-http-url-str": {
|
||||
"help": "HTTP URL string for ARM Mbed-OS Error Decode microsite",
|
||||
"value": "\"\\nFor more info, visit: https://armmbed.github.io/mbedos-error/?error=0x%08X\""
|
||||
},
|
||||
"cthunk_count_max": {
|
||||
"help": "The maximum CThunk objects used at the same time. This must be greater than 0 and less 256",
|
||||
"value": 8
|
||||
}
|
||||
},
|
||||
"target_overrides": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue