Achieving MPU security

January 16, 2018

Ralph Moore-January 16, 2018


click for larger image

Figure 3: MPA Per Task (Source Micro Digital)

As shown in Figure 3, each task has its own Memory Protection Array (MPA). The MPAs are in the same order as the TCBs in the task control table. Each MPA is a replica of the dynamic portion of the MPU when its associated task is running. An MPA is loaded into the MPU by the scheduler when its task is dispatched.

Also, as shown in Figure 3, MPA templates determine the contents of MPAs. A template may be shared among MPAs. This would tend to be the case for tasks in the same partition, although they need not have the same templates and may just share a few regions.

Each MPA is an array of structures consisting of two 32-bit fields named rbar and rasr. These are exact copies of the MPU RBAR and RASR registers in each MPU slot, except that the valid bit is set in rbar, but not in RBAR. The following is an example of a template:

const MPA mpa_tmplt_usbh =
   {RA("ucom_data") | V | 0, RW_DATA  | N57 | RSIC(udsz)   | EN},
   {RA("ucom_code") | V | 1, UCODE    | N7  | RSIC(ucsz)   | EN},
   {RA("usbh_data") | V | 2, RW_DATA        | RSIC(usbdsz) | EN},
   {RA("usbh_code") | V | 3, UCODE    | N67 | RSIC(usbcsz) | EN},
   {RA("syn_csr")   | V | 4, RW_DATA        | RSIC(synsz)  | EN},
   {RA("ur1_csr")   | V | 5, RW_DATA        | RSIC(ur1sz)  | EN},

This corresponds to the umode MPU structure shown in Figure 2. There is a similar template for pmode.

Creating Regions

Templates consist of regions. So how do we create regions? One approach is to start by defining sections in the C source code modules of the application. For example, in each C module containing code for a specific region, start the code[1] with:

#pragma default_function_attributes = @ “.ucom_code”
// Place all ucom functions here.
#pragma default_function_attributes =

“ucom_code” is a section name to identify a section containing common code between utasks. For code that is specific to taskA, use a section name such as “.taskA_code”. The “.” ahead of the section name is chosen for consistency with standard compiler section names, such as .text, .bss, etc. Any number of functions can be enclosed between the pragmas. Also, the above structure can be repeated in other modules and all of the functions will be combined into the single .ucom_code section by the linker.

For data use:

#pragma default_variable_attributes = @ “.ucom_data”
// Place all ucom data here.
#pragma default_variable_attributes =

As with code, any number of variables can be enclosed between the pragmas, and the above structure can be repeated in other modules to create a single .ucom_data section containing all of the ucom variables. This, of course, can also be done for a section containing data for a single task, such as .taskA_data.

In the ILINK linker command file (.icf), for the .ucom_code section use the following:

define exported symbol ucsz = 0x8000;
define block ucom_code with size = 0x7000, alignment = ucsz {ro section .ucom_code};
place in ROM   {block ucom_code, …};

(0x8000 vs. 0x7000 is explained in the next section.) If desired, additional sections can be placed in the ucom_code block and they can be placed in a fixed order, if desired. In this example, only the .ucom_code section is placed in the ucom_code block. (Note that the section and block names differ by a “.”.)

In the C file above where the template is being defined place:

#pragma section="ucom_code"
extern u32 ucsz;

Now, ucom_code and ucsz can be used in the MPA template definition, as shown in the previous section. Thus, linker blocks become MPA regions. This is convenient because their size, alignment, order, and other characteristics can be easily controlled.

Linker blocks

As noted, blocks in the linker command file (.icf) become regions in MPA templates and ultimately MPU regions. Using linker blocks mitigates against assigning wrong sizes and/or alignments. If a block is too small for the section(s) it contains, the linker will complain. Conversely, the linker map file will show if the linker block is too large.

To assign size and alignment to a linker block in the .icf, we distinguish between three sizes: region size, nominal size, and actual size. Suppose, for example that the actual size of ucom_code is 0x6B16. Then the region size must be the next larger power of two = 0x8000[2]. Divide the region size by 8 to get the subregion size = 0x1000. Now find N such that it is the largest value for which 0x8000 – N*0x1000 >= 0x6B16. In this case N = 1, thus the nominal size = 0x7000. So ucsz = 0x8000 and the nominal block size = 0x7000.

The last step is to place N7 (subregion 7 off) in the rasr field of the ucom_code region in the MPA template, as shown in mpa_tmplt_usbh above. If we forget this, the usbh task will be able to access whatever the linker puts in the 0x1000 bytes after the ucom_code region. On the other hand, if we put N67 by mistake (subregions 6 and 7 off) we will get Memory Management Faults (MMFs) when the code attempts accesses in subregion 6.

At last we are ready to start conversion!

Part two of this two-part series will discuss the details of this conversion process.


[1] All code shown in this paper is based upon the IAR EWARM® tool suite and the Micro Digital SMX® RTOS with MPU‑Plus®.

[2] It is much easier and less error-prone to work with hexadecimal numbers. It also helps to have a hexadecimal calculator handy.

Ralph Moore graduated with a degree in Physics from Caltech. He spent his early career in computer research. Then he moved into mainframe design in the 60's and became a consultant in the early 70's. He taught himself programming and became a microprocessor programmer. He founded Micro Digital in 1975, and many years of successful consulting lead into architecting and developing the smx kernel in 1987. For many years he managed the business and sales, but in recent years he has been focused almost solely on development of the smx multitasking kernel v4. He can be reached by email at

< Previous
Page 2 of 2
Next >

Loading comments...