Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • fix/macos-meta-files-in-menu
  • koalo/bhi160
  • genofire/ble-rewrite
  • rahix/simple_menu
  • ch3/splashscreen
  • koalo/bhi160-works-but-dirty
  • ios-workarounds
  • koalo/wip/i2c-for-python
  • renze/safe_mode
  • renze/hatchery_apps
  • schneider/fundamental-test
  • koalo/factory-reset
  • msgctl/gfx_rle
  • msgctl/faultscreen
  • msgctl/textbuffer_api
  • schneider/bonding
  • schneider/bootloader-update-9a0d158
  • schneider/bsec
  • rahix/bma
  • v1.12
  • v1.11
  • v1.10
  • v1.9
  • v1.8
  • v1.7
  • v1.6
  • v1.5
  • v1.4
  • v1.3
  • v1.2
  • v1.1
  • v1.0
  • release-1
  • bootloader-v1
  • v0.0
36 results

control.c

Blame
  • Forked from card10 / firmware
    1359 commits behind the upstream repository.
    • rahix's avatar
      e924ecee
      fix(api): Reset FPU before loading a new payload · e924ecee
      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: default avatarRahix <rahix@rahix.de>
      Verified
      e924ecee
      History
      fix(api): Reset FPU before loading a new payload
      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: default avatarRahix <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();
    }