TFA
Il Trusted Firmware Arm viene caricato da ROM code all’ interno della SYSRAM (pochi kilobytes)
e si deve occupare di inizializzare:
RAM DDRsecure world monitorSSBL (second stage bootloader)
La questione secure world monitor riguarda OPTEE e altri componenti di sicurezza.
Per il momento la saltiamo e ne riparliamo in un articolo a parte.
Il TFA deve quindi fare un bel po’ di cose e operare
con una quantità di RAM molto ristretta.
La filosofia di base è quindi quella di preparargli
una collezioni di file binari contenenti
i componenti che dovrà poi caricare in RAM DDR (svariati megabytes).
Questa collezione prende il nome di FIP (Firmware Image Package ).
Inoltre, TFA si aspetta di trovare tale pacchetto
all’interno di una partizione in flash con GUID type e nome preciso.
Il recipe trusted-firmware-a_2.10.4.bb del layer meta-arm utilizza
la repository ufficiale del progetto, ma questa non contiene
le implementazioni per i processori STM32MP2.
Utilizziamo quindi il nostro .bbappend per fare un override
della repository e utilizziamo quella di ST:
1## STM TFA REPO
2PV := "2.10-stm32mp"
3PR := "r2"
4SRCREV = "v2.10-stm32mp-r2"
5SRC_URI = "git://github.com/STMicroelectronics/arm-trusted-firmware.git;protocol=https;branch=v2.10-stm32mp"per sistemare i nomi dei package che verranno usati da Yocto,
facciamo anche un override di PV e PR.
Per capire come procedere, possiamo consultare:
- il recipe originale
trusted-firmware-a_2.10.4.bb - la documentazione del TFA
- i sorgenti della repository TFA
Diciamo che sono in ordine di difficoltà e che alla fine della fiera, chiunque può ipotizzare ciò che vuole, ma la verità è scritta nei sorgenti. Il capirli però richiede un po’ di tempo.
dal recipe originale
Nel recipe la cosa che ci interessa di più è capire
cosa succede all’ interno del task bitbake do_compile
per vedere che applicativo e che variabili
vengono utilizzate per compilare.
Vediamo che viene utilizzato make
e le variabili vengono passate tramite EXTRA_OEMAKE.
Alcune variabili interessanti sono:
TFA_PLATFORMTFA_DEBUGTFA_BUILD_TARGETTFA_INSTALL_TARGET
dove TFA_PLATFORM guida direttamente il parametro PLAT del Makefile,
mentre TFA_BUILD_TARGET e TFA_INSTALL_TARGET vengono usate
come insieme di stringhe che definiscono i target del Makefile
e ci si itera dentro.
Queste vengono utilizzate per pilotare le variabili associate a make
che poi si occuperà della compilazione del progetto:
103# Add platform parameter
104EXTRA_OEMAKE += "BUILD_BASE=${B} PLAT=${TFA_PLATFORM}"155do_compile() {
156 # This is still needed to have the native tools executing properly by
157 # setting the RPATH
158 sed -i '/^LDOPTS/ s,$, \$\{BUILD_LDFLAGS},' ${S}/tools/fiptool/Makefile
159 sed -i '/^INCLUDE_PATHS/ s,$, \$\{BUILD_CFLAGS},' ${S}/tools/fiptool/Makefile
160 sed -i '/^LIB/ s,$, \$\{BUILD_LDFLAGS},' ${S}/tools/cert_create/Makefile
161
162 # Currently there are races if you build all the targets at once in parallel
163 for T in ${TFA_BUILD_TARGET}; do
164 oe_runmake -C ${S} $T
165 done
166}169do_install() {
170 install -d -m 755 ${D}${FIRMWARE_DIR}
171
172 for atfbin in ${TFA_INSTALL_TARGET}; do
173 processed="0"
174 ...
175 donedalla documentazione
Leggendo la documentazione del TFA
troviamo una sezione relativa al
porting per stm32mp2
che spiega quali variabili make definire.
Innanzitutto dobbiamo selezionare la variante di MPU stm32mp2 corretta
definendo una tra queste:
STM32MP21STM32MP23STM32MP25
Selezionare il tipo di DDR definendo una tra queste:
STM32MP_DDR3_TYPESTM32MP_DDR4_TYPESTM32MP_LPDDR4_TYPE
Definire STM32MP_DDR_FIP_IO_STORAGE per
includere il binario di training della DDR
all’ interno del FIP.
Definire STM32MP_SDMMC se vogliamo
che il FIP venga caricato da sdcard.
All’ interno della repo possiamo quindi definire tali variabili
e utilizzare make per generare
il binario del TFA
e anche il binario del FIP
nel seguente modo:
1export CROSS_COMPILE=aarch64-none-elf-
2export PLAT=stm32mp2
3export STM32MP_DDR4_TYPE=1
4export SPD=opteed
5export DTB_FILE_NAME=stm32mp257f-dk.dtb
6export STM32MP_SDMMC=1
7export STM32MP_DDR_FIP_IO_STORAGE=1
8
9make 1export CROSS_COMPILE=aarch64-none-elf-
2export PLAT=stm32mp2
3export STM32MP_DDR4_TYPE=1
4export SPD=opteed
5export DTB_FILE_NAME=stm32mp257f-ev1.dtb
6export BL33=<u-boot_directory>/u-boot-nodtb.bin
7export BL33_CFG=<u-boot_directory>/u-boot.dtb
8export BL32=<optee_directory>/tee-header_v2.bin
9export BL32_EXTRA1=<optee_directory>/tee-pager_v2.bin
10STM32MP_DDR_FW_PATH=drivers/st/ddr/phy/firmware/bin
11
12make fipQuesto è utile nel caso volessimo compilare manualmente il TFA,
bypassando Yocto oppure utilizzando devshell sul recipe.
Soprattutto, però, ci aiuta a capire dove focalizzare la nostra attenzione
per riuscire a far funzionare sto benedetto TFA.
Da notare inoltre che, siccome il TFA ha il compito
di inizializzare UBOOT e OPTEE,
è necessario disporre dei risultati della compilazione
di questi altri due progetti
per poterli inglobare all’ interno del FIP.
Quindi dovremmo capire prima come fare
almeno un setup grezzo di questi,
ma per il momento amen.
dai sorgenti
Passando poi all’ analisi dei sorgenti, notiamo
un Makefile principale che orchestra la build
e che richiama un platform.mk specifico per
la piattaforma definita da PLAT.
Nel nostro caso PLAT=stm32mp2
e i makefiles che ci interessano sono:
Makefileplat/st/stm32mp2/platform.mkplat/st/common/common.mk
arm-trusted-firmware
├── Makefile
├── plat
│ ├── arm
│ ├── common
│ ├── rpi
│ ├── ...
│ └── st
│ ├── common
│ │ ├── ...
│ │ └── common.mk
│ ├── stm32mp1
│ │ └── ...
│ └── stm32mp2
│ ├── ...
│ └── platform.mk
│
├── drivers
├── fdts
└── ...in questi troviamo variabili che ci possono tornare utili come:
7RESET_TO_BL2 := 1
8
9STM32MP_EARLY_CONSOLE ?= 0
10STM32MP_RECONFIGURE_CONSOLE ?= 0
11STM32MP_UART_BAUDRATE ?= 11520032# Boot devices
33STM32MP_EMMC ?= 0
34STM32MP_SDMMC ?= 0
35STM32MP_RAW_NAND ?= 0
36STM32MP_SPI_NAND ?= 0
37STM32MP_SPI_NOR ?= 0
38
39# Put both BL2 and FIP in eMMC boot partition
40STM32MP_EMMC_BOOT ?= 0
41
42# Serial boot devices
43STM32MP_UART_PROGRAMMER ?= 0
44STM32MP_USB_PROGRAMMER ?= 047# Default Device tree
48DTB_FILE_NAME ?= stm32mp257f-ev1.dtb
49
50STM32MP21 ?= 0
51STM32MP23 ?= 0
52STM32MP25 ?= 0
53
54STM32MP2X := 1137# DDR types
138STM32MP_DDR3_TYPE ?= 0
139STM32MP_DDR4_TYPE ?= 0
140STM32MP_LPDDR4_TYPE ?= 032# Default Device tree
33DTB_FILE_NAME ?= stm32mp257f-ev1.dtb
34
35STM32MP25 := 1e, ancora più interessante, i target make:
67tf-a-%.bin: tf-a-%.elf
68 ${Q}${OC} -O binary $< $@
69 @echo
70 @echo "Built $@ successfully"
71 @echo
72
73tf-a-%.stm32: tf-a-%.bin ${STM32_DEPS}
74 @echo
75 @echo "Generate $@"
76 $(eval LOADADDR = $(shell cat $(@:.stm32=.map) | grep '^RAM' | awk '{print $$2}'))
77 $(eval ENTRY = $(shell cat $(@:.stm32=.map) | grep "__BL2_IMAGE_START" | awk '{print $$1}'))
78 ${Q}${STM32IMAGE} -s $< -d $@ \
79 -l $(LOADADDR) -e ${ENTRY} \
80 -v ${STM32_TF_VERSION} \
81 -m ${STM32_HEADER_VERSION_MAJOR} \
82 -n ${STM32_HEADER_VERSION_MINOR} \
83 -b ${STM32_HEADER_BL2_BINARY_TYPE}
84 @echoQuindi, dopo aver analizzato la repo del TFA in lurgo e in largo,
possiamo aggiungere al nostro .bbappend
le istruzioni per generare i binari che ci interessano.
Dobbiamo dire a make di fare il build dei target:
tf-a-%.stm32fip
dai makefile si vede che % prende il valore DTB_FILE_NAME senza l’ estensione .dtb.
Possiamo usare il target all,
ma rimane necessario esplicitare comunque il target fip.
Nella cartella di build verranno generati i file:
tf-a-%.stm32fip.bin
e questi sono i binari di TFA e FIP che ci servono.
Nel nostro .bbappend queste operazioni sono pilotate
dalle variabili TFA_BUILD_TARGET e TFA_INSTALL_TARGET
perciò possiamo usare:
7TFA_BUILD_TARGET = "all fip"
8
9TFA_INSTALL_TARGET = "\
10 tf-a-stm32mp257f-dk.stm32 \
11 fip.bin \
12 "Per configurare la compilazione come descritto
nella documentazione TFA invece possiamo
agire direttamente su OEMAKE:
7EXTRA_OEMAKE:append = " \
8 PLAT=stm32mp2 \
9 STM32MP_SDMMC=1 \
10 STM32MP_LPDDR4_TYPE=1 \
11 STM32MP_DDR_FIP_IO_STORAGE=1 \
12 SPD=opteed \
13 STM32MP25=1 \
14 DTB_FILE_NAME=stm32mp257f-dk.dtb \
15 "E per definire la dipendenza tra la generazione del FIP, la compilazione di UBOOT e OPTEE e il successivo uso di questi binari:
7DEPENDS:append = " optee-os"
8EXTRA_OEMAKE:append = " \
9 BL32=${STAGING_DIR_TARGET}${nonarch_base_libdir}/firmware/tee-header_v2.bin \
10 BL32_EXTRA1=${STAGING_DIR_TARGET}${nonarch_base_libdir}/firmware/tee-pager_v2.bin \
11 "
12
13
14DEPENDS:append = " u-boot"
15do_compile[depends] += "u-boot:do_deploy"
16EXTRA_OEMAKE:append = " \
17 BL33=${STAGING_DIR_TARGET}/boot/u-boot-nodtb.bin \
18 BL33_CFG=${STAGING_DIR_TARGET}/boot/u-boot.dtb \
19 "recipe finale
Queste sono a grandi linee le istruzioni
per poter configurare TFA, ma si può fare di meglio.
Ad esempio al momento stiamo forzando il boot da sdcard
e non stiamo dando l’opzione di poterlo fare da emmc o da usb.
Stiamo utilizzando il devicetree disponibile nella repo,
ma nel caso volessimo gestire una board leggermente diversa
dobbiamo capire come modificare il devicetree.
Stiamo usando molti nomi “hardcoded” quando potremmo
gestire molti casi in maniera più parametrica.
Insomma si possono fare un sacco di migliorie,
ma a noi interessava capire il concetto.
Lasciamo qua il nostro .bbappend grezzo, ma funzionale:
7## STM TFA REPO
8PV := "2.10-stm32mp"
9PR := "r2"
10SRCREV = "v2.10-stm32mp-r2"
11SRC_URI = "git://github.com/STMicroelectronics/arm-trusted-firmware.git;protocol=https;branch=v2.10-stm32mp"
12
13TFA_PLATFORM = "stm32mp2"
14TFA_BUILD_TARGET = "all fip"
15TFA_INSTALL_TARGET = "\
16 tf-a-stm32mp257f-dk.stm32 \
17 fip.bin \
18 "
19
20EXTRA_OEMAKE:append = " \
21 PLAT=stm32mp2 \
22 STM32MP_SDMMC=1 \
23 STM32MP_LPDDR4_TYPE=1 \
24 STM32MP_DDR_FIP_IO_STORAGE=1 \
25 SPD=opteed \
26 STM32MP25=1 \
27 DTB_FILE_NAME=stm32mp257f-dk.dtb \
28 "
29
30DEPENDS:append = " optee-os"
31EXTRA_OEMAKE:append = " \
32 BL32=${STAGING_DIR_TARGET}${nonarch_base_libdir}/firmware/tee-header_v2.bin \
33 BL32_EXTRA1=${STAGING_DIR_TARGET}${nonarch_base_libdir}/firmware/tee-pager_v2.bin \
34 "
35
36
37DEPENDS:append = " u-boot"
38do_compile[depends] += "u-boot:do_deploy"
39EXTRA_OEMAKE:append = " \
40 BL33=${STAGING_DIR_TARGET}/boot/u-boot-nodtb.bin \
41 BL33_CFG=${STAGING_DIR_TARGET}/boot/u-boot.dtb \
42 "