diff options
author | Siarhei Siamashka <siarhei.siamashka@gmail.com> | 2016-05-23 21:25:18 +0300 |
---|---|---|
committer | Andre Przywara <osp@andrep.de> | 2018-07-09 11:16:24 +0300 |
commit | 1091f3ac6b0f7e0f4249081a1e9d68f20b3fd01d (patch) | |
tree | 38aa532965c9431979ff64fc079d804f7442ddfc /fel.c | |
parent | d0f2fbb8a1d26888c1741099bd37137890ada4a1 (diff) |
fel: Support uploading & executing ARM AAPCS functions on the device
This patch adds a wrapper script, which can automatically compile
and wrap a small C function, taking care of all the necessary
function arguments marshalling.
The functions 'aw_fel_remotefunc_prepare/aw_fel_remotefunc_execute'
allow using such functions in the sunxi-fel tool to get this code
executed remotely on the device.
Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
[Andre: adjust to match upstream changes]
Signed-off-by: Andre Przywara <osp@andrep.de>
Diffstat (limited to 'fel.c')
-rw-r--r-- | fel.c | 105 |
1 files changed, 105 insertions, 0 deletions
@@ -243,6 +243,111 @@ void aw_fel_fill(feldev_handle *dev, uint32_t offset, size_t size, unsigned char } } +/* + * Upload a function (implemented in native ARM code) to the device and + * prepare for executing it. Use a subset of 32-bit ARM AAPCS calling + * conventions: all arguments are integer 32-bit values, and an optional + * return value is a 32-bit integer too. The function code needs to be + * compiled in the ARM mode (Thumb2 is not supported), it also must be + * a position independent leaf function (have no calls to anything else) + * and have no references to any global variables. + * + * 'stack_size' - the required stack size for the function (can be + * calculated using the '-fstack-usage' GCC option) + * 'arm_code' - a pointer to the memory buffer with the function code + * 'arm_code_size' - the size of the function code + * 'num_args' - the number of 32-bit function arguments + * 'args' - an array with the function argument values + * + * Note: once uploaded, the function can be executed multiple times with + * exactly the same arguments. If some internal state needs to be + * updated between function calls, then it's best to pass a pointer + * to some state structure located elsewhere in SRAM as one of the + * function arguments. + */ + +bool aw_fel_remotefunc_prepare(feldev_handle *dev, + size_t stack_size, + void *arm_code, + size_t arm_code_size, + size_t num_args, + uint32_t *args) +{ + size_t idx, i; + size_t tmp_buf_size; + soc_info_t *soc_info = dev->soc_info; + uint32_t *tmp_buf; + uint32_t new_sp, num_args_on_stack = (num_args <= 4 ? 0 : num_args - 4); + uint32_t entry_code[] = { + htole32(0xe58fe040), /* 0: str lr, [pc, #64] */ + htole32(0xe58fd040), /* 4: str sp, [pc, #64] */ + htole32(0xe59fd040), /* 8: ldr sp, [pc, #64] */ + htole32(0xe28fc040), /* c: add ip, pc, #64 */ + htole32(0xe1a0200d), /* 10: mov r2, sp */ + htole32(0xe49c0004), /* 14: ldr r0, [ip], #4 */ + htole32(0xe3500000), /* 18: cmp r0, #0 */ + htole32(0x0a000003), /* 1c: beq 30 <entry+0x30> */ + htole32(0xe49c1004), /* 20: ldr r1, [ip], #4 */ + htole32(0xe4821004), /* 24: str r1, [r2], #4 */ + htole32(0xe2500001), /* 28: subs r0, r0, #1 */ + htole32(0x1afffffb), /* 2c: bne 20 <entry+0x20> */ + htole32(0xe8bc000f), /* 30: ldm ip!, {r0, r1, r2, r3} */ + htole32(0xe12fff3c), /* 34: blx ip */ + htole32(0xe59fe008), /* 38: ldr lr, [pc, #8] */ + htole32(0xe59fd008), /* 3c: ldr sp, [pc, #8] */ + htole32(0xe58f0000), /* 40: str r0, [pc] */ + htole32(0xe12fff1e), /* 44: bx lr */ + htole32(0x00000000), /* 48: .word 0x00000000 */ + htole32(0x00000000), /* 4c: .word 0x00000000 */ + }; + + if (!soc_info) + return false; + + /* Calculate the stack location */ + new_sp = soc_info->scratch_addr + + sizeof(entry_code) + + 2 * 4 + + num_args_on_stack * 4 + + 4 * 4 + + arm_code_size + + stack_size; + new_sp = (new_sp + 7) & ~7; + + tmp_buf_size = new_sp - soc_info->scratch_addr; + tmp_buf = calloc(tmp_buf_size, 1); + memcpy(tmp_buf, entry_code, sizeof(entry_code)); + idx = sizeof(entry_code) / 4; + tmp_buf[idx++] = htole32(new_sp); + tmp_buf[idx++] = htole32(num_args_on_stack); + for (i = num_args - num_args_on_stack; i < num_args; i++) + tmp_buf[idx++] = htole32(args[i]); + for (i = 0; i < 4; i++) + tmp_buf[idx++] = (i < num_args ? htole32(args[i]) : 0); + memcpy(tmp_buf + idx, arm_code, arm_code_size); + + aw_fel_write(dev, tmp_buf, soc_info->scratch_addr, tmp_buf_size); + free(tmp_buf); + return true; +} + +/* + * Execute the previously uploaded function. The 'result' pointer allows to + * retrieve the return value. + */ +bool aw_fel_remotefunc_execute(feldev_handle *dev, uint32_t *result) +{ + soc_info_t *soc_info = dev->soc_info; + if (!soc_info) + return false; + aw_fel_execute(dev, soc_info->scratch_addr); + if (result) { + aw_fel_read(dev, soc_info->scratch_addr + 0x48, result, sizeof(uint32_t)); + *result = le32toh(*result); + } + return true; +} + static uint32_t fel_to_spl_thunk[] = { #include "thunks/fel-to-spl-thunk.h" }; |