Select Git revision
Forked from
card10 / firmware
1359 commits behind the upstream repository.
-
rahix authored
Without resetting the FPU we get spurious failures in Pycardium. These manifest in many different forms, ranging from HardFaults and triggered assertions to "syntax errors" in loaded scripts while said script does not contain any. These failures stem from the FPU still containing state from the last loaded payload and thus sometimes corrupting stack locations where the previous payload had floating point state. Closes #72. Signed-off-by:
Rahix <rahix@rahix.de>
rahix authoredWithout resetting the FPU we get spurious failures in Pycardium. These manifest in many different forms, ranging from HardFaults and triggered assertions to "syntax errors" in loaded scripts while said script does not contain any. These failures stem from the FPU still containing state from the last loaded payload and thus sometimes corrupting stack locations where the previous payload had floating point state. Closes #72. Signed-off-by:
Rahix <rahix@rahix.de>
control.c 5.35 KiB
#include "epicardium.h"
#include "api/dispatcher.h"
#include "api/interrupt-sender.h"
#include "modules/log.h"
#include "card10.h"
#include "max32665.h"
#include "sema.h"
#include "tmr.h"
static void __core1_init(void);
struct core1_info {
/* Location of core1's interrupt vector table */
volatile uintptr_t ivt_addr;
/* Whether core 1 is ready for a new IVT */
volatile bool ready;
};
/*
* Information passing structure for controlling core 1.
*/
static volatile struct core1_info core1_info = {
.ivt_addr = 0x00,
.ready = false,
};
/*
* Minimal IVT needed for initial startup. This IVT only contains the initial
* stack pointer and reset-handler and is used to startup core 1. Afterwards,
* the payload's IVT is loaded into VTOR and used from then on.
*/
static uintptr_t core1_initial_ivt[] = {
/* Initial Stack Pointer */
0x20080000,
/* Reset Handler */
(uintptr_t)__core1_reset,
};
/*
* Reset Handler
*
* Calls __core1_init() to reset & prepare the core for loading a new payload.
*/
__attribute__((naked)) void __core1_reset(void)
{
/* Reset stack to MSP and set it to 0x20080000 */
__asm volatile(
"mov r0, #0\n\t"
"msr control, r0\n\t"
"mov sp, %0\n\t"
: /* No Outputs */
: "r"(core1_initial_ivt[0])
: "r0");
/* Reset FPU */
SCB->CPACR = 0x00000000;
FPU->FPDSCR = 0x00000000;
FPU->FPCCR = 0x00000000;
__DSB();
__ISB();
__core1_init();
}
/*
* Init core 1. This function will reset the core and wait for a new IVT
* address from Epicardium. Once this address is received, it will start
* execution with the supplied reset handler.
*/
void __core1_init(void)
{
/*
* Clear any pending API interrupts.
*/
TMR_IntClear(MXC_TMR5);
/*
* Reset Interrupts
*
* To ensure proper operation of the new payload, disable all interrupts
* and clear all pending ones.
*/
for (int i = 0; i < MXC_IRQ_EXT_COUNT; i++) {
NVIC_DisableIRQ(i);
NVIC_ClearPendingIRQ(i);
NVIC_SetPriority(i, 0);
}
/*
* Check whether we catched the core during an interrupt. If this is
* the case, try returning from the exception handler first and call
* __core1_reset() again in thread context.
*/
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0) {
/*
* Construct an exception frame so the CPU will jump back to our
* __core1_reset() function once we exit from the exception
* handler.
*
* To exit the exception, a special "EXC_RETURN" value is loaded
* into the link register and then branched to.
*/
__asm volatile(
"ldr r0, =0x41000000\n\t"
"ldr r1, =0\n\t"
"push { r0 }\n\t" /* xPSR */
"push { %0 }\n\t" /* PC */
"push { %0 }\n\t" /* LR */
"push { r1 }\n\t" /* R12 */
"push { r1 }\n\t" /* R3 */
"push { r1 }\n\t" /* R2 */
"push { r1 }\n\t" /* R1 */
"push { r1 }\n\t" /* R0 */
"ldr lr, =0xFFFFFFF9\n\t"
"bx lr\n\t"
: /* No Outputs */
: "r"((uintptr_t)__core1_reset)
: "pc", "lr");
/* unreachable */
while (1)
;
}
/* Wait for the IVT address */
while (1) {
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
__DMB();
__ISB();
/*
* The IVT address is reset to 0 by Epicardium before execution
* gets here. Once a new address has been set, core 1 can use
* the new IVT.
*/
if (core1_info.ivt_addr != 0x00) {
break;
}
/* Signal that we are ready for an IVT address */
core1_info.ready = true;
/*
* Reset the API interrupt so we never block Epicardium when it
* attempts to trigger an interrupt.
*/
API_CALL_MEM->int_id = (-1);
SEMA_FreeSema(_CONTROL_SEMAPHORE);
__WFE();
}
uintptr_t *ivt = (uintptr_t *)core1_info.ivt_addr;
core1_info.ivt_addr = 0x00;
SEMA_FreeSema(_CONTROL_SEMAPHORE);
/*
* Reset the call-flag before entering the payload so API calls behave
* properly. This is necessary because epic_exec() will set the flag
* to "returning" on exit.
*/
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
/*
* Set the IVT
*/
SCB->VTOR = (uintptr_t)ivt;
/*
* Clear any pending API interrupts.
*/
TMR_IntClear(MXC_TMR5);
NVIC_ClearPendingIRQ(TMR5_IRQn);
/*
* Jump to payload's reset handler
*/
__asm volatile(
"ldr r0, %0\n\t"
"blx r0\n\r"
: /* No Outputs */
: "m"(*(ivt + 1))
: "r0");
}
void core1_boot(void)
{
/*
* Boot using the initial IVT. This will place core 1 into a loop,
* waiting for a payload.
*/
core1_start(&core1_initial_ivt);
}
void core1_reset(void)
{
/* Signal core 1 that we intend to load a new payload. */
api_interrupt_trigger(EPIC_INT_RESET);
/* Wait for the core to accept */
while (1) {
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
/*
* core 1 will set the ready flag once it is spinning in the
* above loop, waiting for a new IVT.
*/
if (core1_info.ready) {
break;
}
SEMA_FreeSema(_CONTROL_SEMAPHORE);
for (int i = 0; i < 10000; i++) {
}
}
/*
* TODO: If the other core does not respond within a certain grace
* period, we need to force it into our desired state by overwriting
* all of its memory. Yes, I don't like this method either ...
*/
}
void core1_load(void *ivt, char *args)
{
/* If the core is currently in an API call, reset it. */
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
API_CALL_MEM->id = 0;
API_CALL_MEM->int_id = (-1);
api_prepare_args(args);
core1_info.ivt_addr = (uintptr_t)ivt;
core1_info.ready = false;
__DMB();
__ISB();
SEMA_FreeSema(_CONTROL_SEMAPHORE);
__SEV();
__WFE();
}