Skip to content
Snippets Groups Projects
Select Git revision
  • 01dab5065f6b71978b99486ab4c9eef04c3d1123
  • master default protected
  • 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
16 results

lifecycle.c

Blame
  • Forked from card10 / firmware
    Source project has a limited visibility.
    • rahix's avatar
      7fb48178
      feat(lifecycle): Allow core 1 to disable interrupts during API calls · 7fb48178
      rahix authored
      When interrupts are disabled during API calls on core 1, we can only
      trigger a core 1 reset when there is no API call ongoing.  This means
      that the lifecycle machinery needs to allow any running API calls to
      complete before it has a chance to see its reset interrupt delivered.
      
      This is complicated because we cannot synchronize on core 1 triggering
      an API call - the best we can do is stop the dispatcher, check whether
      our reset interrupt was delivered, and if not, give it another chance to
      process an API call.
      
      Additionally, "give it another chance to process an API call" means that
      the IDLE task must run, because this is currently a prerequisite to
      scheduling the dispatcher (see [1]).  The only way to facilitate this is
      with a sleep that is long enough that it will eventually let core 0 go
      idle.  It seems that an 8 tick delay does the job quite fine.
      
      Thus, implement a busy loop which provides the above requirements and
      with that makes Epicardium prepared for payloads which perform API calls
      with interrupts disabled.
      
      [1]: https://firmware.card10.badge.events.ccc.de/epicardium/overview.html#internals
      7fb48178
      History
      feat(lifecycle): Allow core 1 to disable interrupts during API calls
      rahix authored
      When interrupts are disabled during API calls on core 1, we can only
      trigger a core 1 reset when there is no API call ongoing.  This means
      that the lifecycle machinery needs to allow any running API calls to
      complete before it has a chance to see its reset interrupt delivered.
      
      This is complicated because we cannot synchronize on core 1 triggering
      an API call - the best we can do is stop the dispatcher, check whether
      our reset interrupt was delivered, and if not, give it another chance to
      process an API call.
      
      Additionally, "give it another chance to process an API call" means that
      the IDLE task must run, because this is currently a prerequisite to
      scheduling the dispatcher (see [1]).  The only way to facilitate this is
      with a sleep that is long enough that it will eventually let core 0 go
      idle.  It seems that an 8 tick delay does the job quite fine.
      
      Thus, implement a busy loop which provides the above requirements and
      with that makes Epicardium prepared for payloads which perform API calls
      with interrupts disabled.
      
      [1]: https://firmware.card10.badge.events.ccc.de/epicardium/overview.html#internals
    lifecycle.c 8.90 KiB
    #include "epicardium.h"
    #include "os/core.h"
    #include "modules/modules.h"
    #include "os/config.h"
    #include "os/mutex.h"
    #include "user_core/user_core.h"
    #include "api/dispatcher.h"
    #include "l0der/l0der.h"
    
    #include "card10.h"
    
    #include "FreeRTOS.h"
    #include "task.h"
    
    #include <assert.h>
    #include <string.h>
    #include <stdbool.h>
    #include <stdbool.h>
    
    #define PYCARDIUM_IVT (void *)0x100a0000
    #define BLOCK_WAIT pdMS_TO_TICKS(1000)
    /*
     * Loading an empty filename into Pycardium will drop straight into the
     * interpreter.  This define is used to make it more clear when we intend
     * to go into the interpreter.
     */
    #define PYINTERPRETER ""
    
    static TaskHandle_t lifecycle_task = NULL;
    static struct mutex core1_mutex    = { 0 };
    
    enum payload_type {
    	PL_INVALID       = 0,
    	PL_PYTHON_SCRIPT = 1,
    	PL_PYTHON_DIR    = 2,
    	PL_PYTHON_INTERP = 3,
    	PL_L0DABLE       = 4,
    };
    
    struct load_info {
    	bool do_reset;
    	enum payload_type type;
    	char name[256];
    };
    static volatile struct load_info async_load = {
    	.do_reset = false,
    	.name     = { 0 },
    	.type     = PL_INVALID,
    };
    
    /* Whether to write the menu script before attempting to load. */
    static volatile bool write_menu = false;
    static bool execute_elfs        = false;
    
    /* Helpers {{{ */
    
    /*
     * Check if the payload is a valid file (or module) and if so, return its type.
     */
    static int load_stat(char *name)
    {
    	size_t name_len = strlen(name);
    
    	if (name_len == 0) {
    		return PL_PYTHON_INTERP;
    	}
    
    	struct epic_stat stat;
    	if (epic_file_stat(name, &stat) < 0) {
    		return -ENOENT;