Professional Documents
Culture Documents
Introduction
This tutorial will guide you through the steps of creating a TrustZone-enabled design using the Xilinx Vivado software. In this
guide, we will show you how to propagate the TrustZone security into the FPGA, how to configure the security signals in the FPGA
and how to separate the design into a secure world and a non-secure world.
Tutorial files:
The monitor.S file contains the SMC (Secure Monitor Call) routine, which is used to switch from a world to another by saving the
context of the initial world and restoring the context of the destination world. For each world, the context is saved on a different
stack. This file also contains the monitor mode initialization function which sets the monitor exception vector and initializes the
secure and non-secure stacks.
The normal.S file includes the link of the non-secure binary code.
The platform.c file includes the TrustZone initialization function.
The boot.S file is a modified Xilinx standalone boot file on which CP15 configurations that would cause an exception in non-secure
worlds have been removed.
The lscript.ld files are linked scripts, these files include modifications of the memory regions conforming to the Xilinx UG1019
TrustZone memory configuration.
For more details about these files, refer to the software implementation section.
1
1. Creating a new project
1-1. Launch Vivado 2017.1
1-2. Click Create Project to start a new project wizard. The Create a New Vivado Project dialog box will appear. Click Next.
1-3. Choose a project name in the Project Name field, then a location folder in Project Location field. Click Next.
1-4. In the Project Type form, select RTL Project, check Do not specify sources at this time and click Next.
1-5. In the Default Part form, select Boards, choose the board you are using (Zybo in this example) and click Next.
2
Figure 3: Board selection
3
Figure 5: Creating a new block design
2-4. Once the IP Catalogue is open, find and select. ZYNQ7 Processing System. Add it to the design by double-clicking on it or
pressing the Enter key.
Figure 6: IP Catalogue
2-5. Notice that a “Designer Assistance available” message appears after creating the Processing System IP (figure 7). Click Run
Block Automation and select /processing_system7_0.
2-6. In the Run Block Automation window, make sure Apply Board Preset is checked and leave the other parameters to their
default values. Click OK.
4
Figure 8 : Block Automation settings
5
Figure 9 : ZYNQ7 Processing System block settings
2-8. Select the Interrupts tab on the page navigator. Expand Fabric Interrupts then expand PL-PS Interrupt Ports. Check Fabric
Interrupts, then check IRQ_F2P[15:0] and Core0_nFIQ. Click OK.
2-9. Click the Add IP icon and find AXI GPIO in the IP catalogue.
6
Figure 11: GPIO IP
2-10. Add the AXI GPIO IP to the design. The block diagram will be updated.
2-11. Select the new AXI GPIO block. Rename the block to Leds in the Block Properties tab.
2-12. A “Design assistance available” message should be appearing. Click Run Connection Automation to open the connection
automation window. Check All Automation then select GPIO. On the Select Board Part Interface parameter, select leds_4bits.
Click OK to run the connection automation.
7
Figure 7 : LEDs Connection Automation
2-13. Notice that two additional blocks have been automatically added to the design: Processor System Reset and AXI
Interconnect. You can rearrange the design by clicking on the Regenerate icon .
2-14. Use Add IP icon + to add create two new instances of the AXI GPIO IP. Name them buttons_FIQ, and buttons_IRQ.
2-15. Double click on the buttons_IRQ block to open its customization window.
2-16. Move to the IP Configuration tab. Under GPIO, check All Inputs and set GPIO Width to 2. Make sure Enable Dual Channel
is unchecked and check Enable Interrupt. Click OK.
8
Figure 9 : GPIO interrupt enabled
2-17. Open the buttons_FIQ customization window and configure it like buttons_IRQ on step 2-16.
2-18. The “Design assistance available” message should be appearing. Click on Run Connection Automation, and check the All
Automation box.
2-19. Select GPIO under buttons_FIQ, and change Select Board Part Interface to Custom. Do the same configuration for GPIO
under buttons_IRQ. Click OK.
2-21. The external port of buttons_FIQ should be named gpio_rtl or gpio_rtl_0. Rename it to but_FIQ. Rename the external
port of buttons_IRQ to but_IRQ.
This parameter will allow non-secure transactions to go through the AXI Bus in the PL system (FPGA part). We will now see how
to extend the TrustZone security to the FPGA by declaring secure IPs.
3-3. Double click on the AXI Interconnect block to open its customization window.
3-4. Check Enable Advanced Configuration Options and move to the Advanced Options tab.
11
Figure 15 : AXI interconnect advanced configuration options enablement
3-5. In our design, M00_AXI is the master controlling the LEDs GPIO peripheral. In order to make the LEDs IP secure, check the
Secure Slave option for M00_AXI. You can also make the button IPs secure. To find out which AXI Bus is used for the IP you want
to make secure, simply check the connections in the block design. For example, buttons_IRQ is connected to the AXI Interconnect
block through the M01_AXI port (figure 26).
12
Figure 27: Enabling LEDs security
3-6. Run the design validation tool (Tools → Validate Design) and make sure there are no errors.
4-1. In the Sources panel, right click on system (system.bd), and select Create HDL Wrapper. Leave the Let Vivado manage
wrapper and auto-update option selected, and click OK.
13
Figure 29: Creating HDL Wrapper validation
4-2. In the Flow Navigator, click Run Synthesis. If prompted, click Save. When the synthesis completes, select Open Synthesized
Design and click OK.
4-3. Select I/O Planning.
4-4. In the I/O ports tab, expand the two GPIO ports then expand but_irq_tri_i and but_fiq_tri_i. You need to configure the I/O
Std and Package Pin parameters conforming to the board you are using. In this example, we are using the ZYBO ZYNQ-7000 board
and setting all IO/Std parameters to LVCMOS33. Refer to the Reference Manual of your board to find the Package Pins that
correspond to the peripherals you want to use.
14
Figure 31: The IP port pin constraints
4-4. Save your modifications (File → Save Constraints or hit Ctrl+S) in constraint file. A save window will appear, type a file name
then click OK.
15
4-7. Click on File → Launch SDK to open XSDK with your project files.
5. Software implementation
5-1. Before creating the software projects, it is recommended to disable the automatic build feature (Project → Build
Automatically). To create a new project, select File → New →Application Project.
5-2. In the Project name field, type secure. Click Next.
5-3. Select Hello World from the available templates and click Finish.
16
Figure 17 : Application templates
5-4. Follow the same steps to create a non_secure application. In the secure application, rename helloworld.c to main_secure.c.
Rename helloworld.c of the non_secure application to main_nonsecure.c.
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
int main()
{
/* Move to the secure world */
SMC;
17
for (unsigned char i = 0; i < 5; i++)
{
/* Print a message then move to the secure world */
xil_printf("Hello from non_secure world !\n");
SMC;
}
return 0;
}
5-6. Replace non_secure/src/lscript.ld with the lscript.ld file you can find on the Annex of this tutorial.
By default, the main memory is divided into 64 Mbit memory regions. XSDK provides a register configuration to make each
memory region either secure or non-secure. The non-secure lscript.ld provided with this tutorial maps the non-secure code in
the second 64Mbit memory region.
5-7. Using the project explorer, move to the non_secure_bsp/ps7_cortexa9_0/libsrc/standalone_v6_2/src/ folder. Replace the
boot.S file with the one you can find on the Annex of this tutorial.
Secure configurations have been removed from the non-secure boot.S to allow the non-secure world to boot without causing
exceptions.
5-8. If you haven’t enabled the automatic build feature, build the non_secure application by selecting it and clicking the build IP
icon .
5-9. Change main_secure.c to the example code of figure 37.
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
int main()
{
init_platform();
cleanup_platform();
return 0;
}
5-10. Replace secure/src/platform.c with the platform.c file provided on the Annex of this tutorial.
18
5-11. Replace secure/src/lscript.ld with the lscript.ld file provided on the Annex of this tutorial.
5-12. Copy monitor.S and normal.S from the Annex of this tutorial to the secure/src/ folder on the project explorer.
5-13. We will now convert the non_secure.elf file in the non_secure/Binaries folder to binary file (.bin). In the next steps, we
will copy this file to the secure/Debug/src/ folder to allow the secure project to build with the normal world binary.
5-14. Go to Window → Show View → Other and find XSCT Console. Click OK to add the XSCT Console to your view.
5-15. In the XSCT Console, move to the .sdk folder of your project. For example, if your project is named trustzone_app, you can
move by using this command :
cd trustzone_app.sdk
To avoid typing these commands after each creation of a new non-secure build, you can create a TCL file (for example,
auto_build.tcl) and put the commands on this file. This file needs to be placed on the .sdk folder of your project. You can execute
it using this command :
source auto_build.tcl
5-16. Build the secure application by selecting it and clicking the build icon .
5-17. Once the compilation finishes, you can debug your application to the destination platform.
19
Annex:
In this annex, some file are not complete. We just present the modification with blue to apply to the file generated
by Xilinx.
Non-secure :
lscript.ld
You need to change only the start address of the non_secure application, and it size.
…………………..
MEMORY
{
ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x4000000, LENGTH = 0x4000000
ps7_qspi_linear_0_S_AXI_BASEADDR : ORIGIN = 0xFC000000, LENGTH = 0x1000000
ps7_ram_0_S_AXI_BASEADDR : ORIGIN = 0x0, LENGTH = 0x30000
ps7_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0xFE00
}
…………………..
boot.S
.globl MMUTable
.global _prestart
.global _boot
.global __stack
.global __irq_stack
.global __supervisor_stack
.global __abort_stack
.global __fiq_stack
.global __undef_stack
.global _vector_table
.section .boot,"ax"
_prestart:
_boot:
21
ldr r13,=SYS_stack /* SYS stack pointer */
/* Write to ACTLR */
mrc p15, 0, r0, c1, c0, 1 /* Read ACTLR*/
orr r0, r0, #(0x01 << 6) /* set SMP bit */
orr r0, r0, #(0x01 ) /* Cache/TLB maintenance broadcast */
mcr p15, 0, r0, c1, c0, 1 /* Write ACTLR*/
.end
Secure:
monitor.S
.section .vector_mon, "ax"
.align 5
.global monitor
monitor:
B . /* Reset - not used by Monitor */
B . /* Undef - not used by Monitor */
B SMC_Handler
B . /* Prefetch - can by used by Monitor */
B . //Data_abort /* can by used by Monitor */
B . /* RESERVED */
B . //IRQ_monitor /* can by used by Monitor */
B . //FIQ_monitor /* can by used by Monitor */
Mode_MON = 0x16
Mode_SVP = 0x13
NS_BIT = 0x1
@ -----------------------------------------------------------
@ Fonction définie au monde sécurisé sur laquelle on rentre
@ après chaque SMC du monde normal
@ ------------------------------------------------------------
.extern SecureWorldEnter
22
@ ------------------------------------------------------------
@ SMC Handler
@
@ - Detect which world executed SMC
@ - Saves state to appropriate stack
@ - Restores other worlds state
@ - Switches world
@ - Performs exception return
@ ------------------------------------------------------------
.global SMC_Handler
SMC_Handler:
@ r2 <-- save to
@ r3 <-- restore from
STR r2, [r0] @ Save updated pointer back, r0 and r2 now free
STR r3, [r1] @ Save updated pointer back, r1 and r3 now free
23
@ Move to SecureWorldEnter if we're going to the secure world
@ --------------------
MRC p15, 0, r0, c1, c1, 0
CMP r0, #NS_BIT
POP {r0-r3}
MOVS pc, lr
@ ------------------------------------------------------------
@ Monitor Initialization
@
@ This is called the first time the Secure world wishes to
@ move to the Normal world.
@ ------------------------------------------------------------
.global monitorInit
.global ns_image
monitorInit:
24
@ Set up exception return information
@ ------------------------------------
@IMPORT ns_image
@ Enable FPU
@ -------------
fmrx r1, FPEXC /* read the exception register */
orr r1,r1, #FPEXC_EN /* set VFP enable bit, leave the others in orig
state */
fmxr FPEXC, r1 /* write back the exception register */
MOVS pc, lr
@ ------------------------------------------------------------
@ Space reserved for stacks
@ ------------------------------------------------------------
NS_STACK_BASE:
.word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
NS_STACK_LIMIT:
S_STACK_BASE:
.word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
S_STACK_LIMIT:
NS_STACK_SP:
.word 0
S_STACK_SP:
.word 0
@.end
25
normal.S
.global ns_image
.align 8
.section .NSECURE_IMAGE, "ax"
ns_image:
.incbin "src/normal.bin"
.end
platform.c
Addition of some macro define for Trustzone registers, and an initialization function for that init the Trustzone
blocks.
………………
void init_TZ()
{
Xil_Out32(SCLR_UNLOCK,SCLR_UNLOCK_VAL);
Xil_Out32(SECURITY6_APB_SLAVES,0x80);
Xil_Out32(TZ_DDR_RAM,0x6);
Xil_Out32(SCLR_LOCK,SCLR_LOCK_VAL);
}
………………
26
void
init_platform()
{
/*
* If you want to run this example outside of SDK,
* uncomment one of the following two lines and also #include "ps7_init.h"
* or #include "ps7_init.h" at the top, depending on the target.
* Make sure that the ps7/psu_init.c and ps7/psu_init.h files are included
* along with this example source files for compilation.
*/
/* ps7_init();*/
/* psu_init();*/
init_TZ();
enable_caches();
init_uart();
}
………………
lscript.ld
…………………………………………………………………………………………………
MEMORY
{
ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x100000, LENGTH = 0x4000000
ps7_ddr_1_S_AXI_BASEADDR : ORIGIN = 0x4000000, LENGTH = 0x4000000
ps7_ddr_share : ORIGIN = 0x8000000, LENGTH = 0x4000000
axi_bram_ctrl_0_Mem0 : ORIGIN = 0x40000000, LENGTH = 0x2000
}
ENTRY(_vector_table)
/* ../../secure_bsp/ps7_cortexa9_0/libsrc/standalone_v6_0/src/ */
SECTIONS
{
.text : {
27
KEEP (*(.vectors))
*(.boot)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.plt)
*(.gnu_warning)
*(.gcc_execpt_table)
*(.glue_7)
*(.glue_7t)
*(.vfp11_veneer)
*(.ARM.extab)
*(.gnu.linkonce.armextab.*)
} > ps7_ddr_0_S_AXI_BASEADDR
…………………………………………………………………………………………………
__abort_stack = .;
_fiq_stack_end = .;
. += _FIQ_STACK_SIZE;
. = ALIGN(16);
__fiq_stack = .;
_undef_stack_end = .;
. += _UNDEF_STACK_SIZE;
. = ALIGN(16);
__undef_stack = .;
_mon_stack_end = .;
_s_stack_end = .;
. += _TAB_SIZE;
. = ALIGN(16);
__s_stack = .;
. += _MON_STACK_SIZE;
. = ALIGN(16);
_ns_stack_end = .;
. += _TAB_SIZE;
. = ALIGN(16);
__ns_stack = .;
__mon_stack = .;
} > ps7_ddr_0_S_AXI_BASEADDR
.NORMAL_IMAGE : {
__NORMAL_IMAGE_start = .;
KEEP (*(.NSECURE_IMAGE))
__NORMAL_IMAGE_end = .;
} > ps7_ddr_1_S_AXI_BASEADDR
_end = .;
}
28