Skip to content

Exercises related to Robust Design Patterns - Part 3

MPU Protection

How to handle an exception

Exercice 1

We have seen during the lecture that an exception is raised in case a memory violation occures. Checking the manual contained in the repository (or, though not the source of truth but easier to read)):

  1. can you name what function is called? What does its default implementation?
  2. can you infer what thread triggered the violation when the exception handler is called?
  3. what other pieces of information may one get?
Solution
  1. The function used is MemManage_Handler(). Its default implementation corresponds to
    void Default_Handler(void)
    {
      while(1);
    }
    
    as MemManage_Handler() is mapped to Default_Handler. The example below is taken from one of the startup_ARMCM*.c contained in CMSIS/RTOS2/RTX/Examples/Blinky/RTE/Device/ARMCM* :
    void MemManage_Handler      (void) __attribute__ ((weak, alias("Default_Handler")));
    
  2. Yes, the thread ID is available.
  3. From the manual:

it is possible to identify the thread that caused the memory access fault, the corresponding zone id and the safety class. This information can be used to define actions for entering a safe state. rtos_process_isolation_faults provides more details on the available system recovery possibilities..

Understanding memory partitioning and zoning

Exercice 2

Look at the following zone to ressource allocation:

  1. Assume the idle task, belonging to Zone_Idle, is run in unpriviledged mode. Is there any issue with the above assignments? Could there be any issue as it can access - at least from the table above - to sensitive areas like ARM_LIB_STACK? (note: ARM_LIB_STACK is reserved for the ARM C library stack)
  2. Take the above ressource allocation and the line of code
    static uint32_t   sensor_val    __attribute__((section("ram_shared"))) = 0U;
    
    What does this line do with regard to where sensor_val is put in memory?
Solution
  1. This does not cause a safety problem since the resources are only available in privileged mode while the idle thread runs unprivileged.
  2. It puts the variable into a section named ram_shared. This section is part of the memory region RAM_SHARED that is enabled for access in all MPU Protected Zones defined in the example (except idle task).

Exercice 3

Look at the following code defining threads attributes:

static uint64_t stack_thrTire[64]; // Stack of Tire Thread
static osThreadId_t tid_thrTire;   // Thread id of Tire Thread

const static osThreadAttr_t attr_thrTire = { // Thread attributes of Tire Thread
.name = "TireThread",
#ifdef SOME_MAGIC
.attr_bits = osThreadUnprivileged | osThreadZone(ZONE_NORMAL_OP) | osSafetyClass(SAFETY_CLASS_NORMAL),
#else
.attr_bits = osThreadUnprivileged,
#endif
.cb_mem = NULL,
.cb_size = 0,
.stack_mem = &stack_thrTire,
.stack_size = sizeof(stack_thrTire),
.priority = 11,
.tz_module = 0,
.reserved = 0};

  1. What does SOME_MAGIC do in your opinion?
  2. Do you see any major differences compared to the way attributes for instantiating tasks have been defined so far?
Solution
  1. It is used to activate Safety Classes and MPU as the corresponding attributes are used.
  2. The stack (stack_thrTire) is explicitly allocated.

Safety Class

Activating the functionality

Exercice 4

If you access RTX_Config.h you will see that OS_SAFETY_CLASS exists. Please answer following questions:

  1. is it sufficient to activate OS_SAFETY_CLASS to get the functionality?
  2. if not, what else should one activate?
  3. what is one cannot do when this is activated?
  4. how many files are modified when this flag is activated?
Solution
  1. No, it is not. Check RTX_Config.h, lines 78-79.
  2. OS_SAFETY_FEATURES needs to be activated as well.
  3. Threads assigned to lower classes cannot modify higher class threads.
  4. 11 (though OS_SAFETY_CLASS becomes RTX_SAFETY_CLASS)

Understanding its way of working

Exercice 5

These are just exemplary questions related to a functionality you are now familiar with: semaphores. The questions related to the code present in the development branch (thus, version > 5.9.1)

  1. what is the check concerning SAFETY_CLASS done when acquiring a semaphore?
  2. what does a call to svcRtxSemaphoreDelete do when destroying a semaphore?
Solution
  1. Here we can see that the correctness of class is checked prior to acquiring the semaphore for real.

    Here the corresponding code :
    #ifdef RTX_SAFETY_CLASS
    // Check running thread safety class
    thread = osRtxThreadGetRunning();
    if ((thread != NULL) &&
       ((thread->attr >> osRtxAttrClass_Pos) < (semaphore->attr >> osRtxAttrClass_Pos))) {
      EvrRtxSemaphoreError(semaphore, (int32_t)osErrorSafetyClass);
      //lint -e{904} "Return statement before end of function" [MISRA Note 1]
      return osErrorSafetyClass;
    }
    #endif
    
  2. There are multiple steps involved. Namely:
    1. the validity of the semaphore object/ID is checked
    2. the safety class is checked, thus ensuring the caller is allowed to run the operation
    3. all tasks waiting on the semaphore are released
    4. finally the semaphore is destroyed from the kernel objects pool