Merge pull request #8488 from c1728p9/cthunk_overhaul

Rewrite CThunk so it does not execute from ram
pull/8677/head
Cruz Monrreal 2018-11-07 19:15:14 -06:00 committed by GitHub
commit 0fe6369950
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 257 additions and 110 deletions

View File

@ -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;
if (instance && callback) {
(instance->*callback)(context);
}
}
volatile CThunkTrampoline m_thunk;
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;
}
};

144
platform/CThunkBase.cpp Normal file
View File

@ -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");
}
}

77
platform/CThunkBase.h Normal file
View File

@ -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__*/

View File

@ -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": {