From 03bc49c03c80d5d1c0913fadb7ffef3d7d1d6592 Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Sat, 19 Dec 2020 01:15:06 +0200 Subject: [PATCH 01/11] Add README and diagram for new measured boot example, on STM32F407-DISCO1 Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/README.md | 102 ++++++++++++++++++ .../measured_boot_diagram.png | Bin 0 -> 90040 bytes 2 files changed, 102 insertions(+) create mode 100644 test-app-STM32F4-measured-boot/README.md create mode 100644 test-app-STM32F4-measured-boot/measured_boot_diagram.png diff --git a/test-app-STM32F4-measured-boot/README.md b/test-app-STM32F4-measured-boot/README.md new file mode 100644 index 0000000..11b12ff --- /dev/null +++ b/test-app-STM32F4-measured-boot/README.md @@ -0,0 +1,102 @@ +# STM32F4-Discovery-Measured-Boot + +Measured Boot example running on STM32F407-Discovery board. + +This project is meant to demonstrate Measured Boot using the [wolfBoot secure bootloader](https://github.com/wolfssl/wolfBoot), powered by wolfSSL. + +The bootloader expects a Trusted Platform Module(TPM) to be available in hardware. + +## Components + - Bootloader: [wolfBoot](https://github.com/wolfssl/wolfBoot) by wolfSSL + - Application: Test app printing the extended PCR value that wolfBoot created + - TPM2.0 stack: [wolfTPM](https://github.com/wolfssl/wolfTPM) by wolfSSL + +## Motivation + +Measured Boot provides the application with a trace of the boot process. This trace(log) is resistant to spoofing and tampering. + +This is achieved by using a TPM which has unique Platform Configuration Registers(PCR) to store hash measurements. + +The application(runtime) then can use this tamper-proof log to determine whether the system is in a good known state (trustworthy) or if the system is infected with malware. + +After a measurement is stored into one of the TPM's PCRs, the user could perform local attestation using a TPM2.0 Quote and report the log to a remote sever for evaluation: + - The remote server can compare the log to golden value(s) and alert system owners in case of mismatch. + - The remote server can directly take action with the device. + +The following diagram is a big picture overview of the different guarantee Secure and Measured Boot provide: + +![Measured boot](measured_boot_diagram.png) + +## Details + +Secure Boot is a way for the system owner to guarantee that the device started with a geniune software. However, this information is not available to the system later on. Therefore, the application runtime must assume any and all software that was run before it is geniune. Measured Boot eliminates the guessing element and provides a way for the application to know the state of the system before it took control. + +Measured Boot could evaluate a single component like firmware or application image, or can evaluate multiple components like system settings and user configuration. Additionally, the golden value could be stored within the system for the application to self-evaluate its state without the need of a remote server. + +This example performs measured boot, stores the measurement of the firmware image into PCR16 and then boots into a test application. The test application prints the result of the measured boot(the value of the PCR). + +This example could be extended to support multiple measurements. + +## Prerequisites + +- `STM32F4-Discovery` board (STM32F407) +with +- `LetsTrust TPM2.0` module or `Infineon SLB9670` module + +Hardware connections must be made between the TPM2.0 module and the STM32F4 board. Here is a wiring table for the Infineon module: + +| STM32F4 | Pin function | TPM2.0 module | +|----------|:-------------:|--------------:| +| PE0 | SPI CS | Pin 26 | +| PB3 | SPI CLK | Pin 23 | +| PB4 | SPI MISO | Pin 21 | +| PB5 | SPI MOSI | Pin 19 | +| 3V | +3V | Pin 1 | +| GND | Ground | Pin 6 | + +UART1 on the STM32F4 is used. The UART Pinout can be found below: + +| STM32F4 | Pin function | +|----------|:------------:| +| PB6 | UART TX | +| PB7 | UART RX | +| GND | Ground | + +Make sure the Ground connection between your USB-UART converter is connected to the STM32F4 board, otherwise UART levels will float and communication will be corrupted. + +## Compiling + +Before compiling make sure the git submodules are initialized and updated correctly. Use the following commands to make sure: + +`wolfboot-examples/$ git submodule --init --update` + +`wolfboot-examples/$ cd wolfBoot` + +`wolfboot-examples/wolfBoot$ git submodule --init --update` + +Enter the project folder for this example: + +`wolfboot-examples/wolfBoot$ cd ../test-app-STM32F4-measured-boot` + +Makefile in the project folder takes care of compiling wolfBoot, compiling the test application and signing the firmware: + +`wolfboot-examples/wolfBoot$ make` + +After a successful operation the following files will be available in the project folder: + +- factory.bin - Test-app and wolfboot combined, ready for flashing +- image.bin - Test-app without signature +- image_v1_signed.bin - Test-app with signature +- image.map - Listing of the Test-app + +## Copyright notice +This example is Copyright (c) 2020 wolfSSL Inc., and distributed under the term of GNU GPL2. + +Some STM/STMicroelectronics specific drivers used in this example are Copyright of ST Microelectronics. Distributed freely as specified by BSD-3-Clause License. + +wolfBoot, wolfSSL (formerly known as CyaSSL) and wolfCrypt are Copyright (c) 2006-2018 wolfSSL Inc., and licensed for use under GPLv2. + +wolfTPM, is Copyright (c) 2018-2020 wolfSSL Inc., and licensed for use under GPLv2. + +See the documentation within each component subdirectory for more information about using and distributing this software. + diff --git a/test-app-STM32F4-measured-boot/measured_boot_diagram.png b/test-app-STM32F4-measured-boot/measured_boot_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b85fd44c79429c36d03dee6f56cf0d218d1ce8 GIT binary patch literal 90040 zcmc$_Wn3Ih@GiPQ5<(zoa7)nOF2NJrgF6IwcPECsI|O%k5AH6(gDkF#!?JsZy#I6W zhjZ_T`{~YalVNvyx~qGttDbtQCgih%NuRtKsYiTJlWf15Y@F(Jrmx#a#FLM1Y z2!!y{QdIP_w5aG?2PbqEX!Ox_<--?2B)qlOj z{Zu|#@_i`Ze=_f#w4>u2T~u@!qdKW>x~r=L?z7+Im^?hR;`nsut$C9a{rr*M9;e<0 zCml#XyizRH2Y9ph8&YI?`{dlzj=X+~aQslZ92#Zcp`t@sKX;7+t)ChRjC7t%Oy%w{ zzIZ0Ksb!YTeg5M7vcT41jW)6>rwls_Co09|;Cc-zQ=Q z+tJVh2?22uHLeWE&+q~IU8ZsE=Hb+vZ5FiM%LLPk8KO^!VVd8bVZADO{|k+nZOK)c zF>g=cWQ*M85xe<~Q$p8Zb-*jm!3uIHhTqNnu%d#8^ak(b-6k)2kH`Y^WYLH5o9Zo2 zg@C^t4*8AH1U}XaX|M8p@u+|`+rOJF`E=f0V06|qlfi-`nUC6^gfQB&rg%)h!gSu& z?Oc_6M$RC=m2a0>10e@>NqwzZ@LgM7U2F8WPJMhpcx&bq*jE;T4m=kW2PrLQ5C|3L z=^p|pHJt$X5!pprUK|;Wi1>=0@l{o(ISBL?BrWz))nn;s)m>L*mk9Z6mx}5~`wga; z_-7HqP#lenzeV1E=lQgCjw@{K2kPA{f*R-4tygm^_zub5Zg$yB7#IZ7Mn>0v(iaej>fisMfF~saj_ChRAngCn zBL6i4N&U~y|J@CGi}2s~|L2?kU$1heZf~2kg#q^lXd-3@UZx&V%1TLjTpulN(JE#M z`6kAvr)MNkzj>vi9Q(6-6WZ2RGG9|w#mUX)=;p@8&D~I6@9OG$WpOy)V5{Tq?#{!* z)3y%uRvsjf=mFW@pD{;~mX_99)au?$?Z;LaXQt`z2kxq{)fHG-6%@df6&d+m#kl;2 zOw!ZSQ*QJs`EcQMt=;|Mx?B>2JTWP0ap?nZYn`K;?bgwB|DWF3?(X5?{=|u0{tvA+ zV!hb1EHF_qM%m(G>73Be^rNzG(3Q>2oZ0QX+4$bN_};Qnw69%VwZ;R?dM$4Io5Tc| zm;?mxF<(w10U~M#LLiXCg~|clPRHHx)#XNeVXs>eYeqv`;}Ml8>R?TEu0%}mwh7*L zdoZ=K3U)0A$1}w5=$WLgF2_rK3q0>%RzG`-P}Ck88hU}xoX+q5j-4GcKSul_FwjpA zammxm_26iCajI2*+0BP6ON}sr%D-*`&H~S^QDUP0tQS*Wgh@>;5b1yU_xB^e;uiT@ zr+aBx-K_B2w^URnB5=QV@7~?t-|xK!L>-eA7w`LtinTj#5F8%f=DF4K>(?*)q=4&8 z{WT%K%*-|0rG~AGD}x`e80ns))5jn^P*iweNxx4tE{&vaxktV{UUvW61$)Gl1z`tR zZSU-m3Ak*ZZSqGJKbeBE!WavNAROX$Pp@WIPz?Rmf?3@>h?!LKZhS+y0s; zmaL0vD6T5%&!x~1wSMA*KCyb+l3wgaBj)ucx8l4!kH1~Njr+q%2=M|n5u-t>*kOG^ zQ6#H%mKVh;v`h6DB%CH5@QimG)BaDE-=9mXl?B z%$|+t1KwZ$tH}!jvR|w3<8VBkiqP#}Uqwa5OI%>r_TTJHjOmIXAdLeI95v`Ku#G=8Gr^I&ITU7dg+7R_Dd(XgY;~L8s@1fhZ#a6E{M% zOg=-$eM^R9>wsb=nGoUK%u>W!hOZiO*zjlNk+%9M%{fTtPJ`yOpNeN&jn((Ga<8l1 z#%1>-XjbKd-5=l0lSf_*j41ogM|JvyoEbni&iBQt_g4$+9v*dJVg63rV%v(=@Xx*2 z^dG-V++~#vy{;pHbXom)ZR6UEP)Y{39wC}_pY1T-h5OXH^_<|`7;Pin zDob}o15O6|6|#gl`1v17PyG&v*{f11uhN#L;A+Pj)%SCIsO#_>Uu5Lva-9YbPtRY6 zPa^z4#vuElm=%+tCeHYg<-@BiSbHv9%`J~dO3Y9d>C#;d^lgbzT>;QTz9xqUkdM!AU=2N>CE@*cv@!JHSHGHksU5; z9&{oBXny{^nFh=}0k8Ruqg5Yd0oY&dURPGkNnt{OPx{PK-8-E>!fn5HslrwLjB5+h z2{8s*9(0no3t7*Xk^A40RX^h#c_u;;6Hdr~vbNglZ?nAIAu5^R3@KW;O}iZOSGBW} z2I<>yY)G#R*pH#1?bVo1kO>M3x=;dh;OocU>W}F3-8~^+zEL zNIFbLlJfJCpnCx>G+?~CcY8?1sABz(|81TtFoF7LNayDGN_rX3ZaB&bS)KQSLNE4Y z^1;&bnveBjedz*Bz(J1VUW0LFB??BHV3PwekhNY`>pTf*ak?8{K<@vG(KueW`c7{8N3t)lbgnx7Mm+-jJ&KOArnOE(LvUy^X^lbf@(@@1Z~$gnUD z9v(@5K-3~N#;Fe9p!`!mPTzT`Rb+QzqU&YnjvvPHVw-KxP1M#FAmi5LBWAXJr_S-w zG>y+r8-0f(WqyG6L%u%){V+?qpy!p0s={~}>K1s%3?Fa%&}V;rB|W{n8@i-_uVE_9 zM5%7vDMS2{l=$TD9^~<6J(mM8%(KG>r=9L@j<2&mDB3D4D8p!tLTYN+ott@ekW z0*4}}%8}#fDC22qr_PB3zI{W~Sl~YNTgRLB&$a`@*XLokhpB4p>NHrgVmYBm1C;Ij zcQuSZ6$!?758)lhpnX+X76*0s!~)!OrlX$@K9Xjq+k6H_M#aX#iD?3`!mLmtfocx~ zPJCf5CZ5^q!=E{U|GxW&qOixIlWK2qSfiCDEb9YZ{KPK20Sv+t^d=HMNH{@FY&+eb zTb!L8?bKlzgTUJ?DLs82vv^MO1A#F#0A~v8BxjV9`NW72@o`{Aft1gucYeIWsn^QG z@=ln8(srz*k|t=v&;1S`tw%SMclYmKDtm9+^X2dZj16mjn7LMJ?U&+)IpPv1OXq+v zZ*Twn3E&K73gktTo}P0E-~^YJ7K1Ly0ojGIBGPz|PjmepgQ*<1kIcR2M{t+p~?|qN38v_6~DMuC}!BkamAXs*=4;^4_iU5Yq>Af-(zwW@aO; z6r;2x7QJ8r|D~=j2aCPrLFdA6-ZvKq+wPD*9}`VrUj<=8HJz-&EJt&6wkv|$%lLTs zLz%mCg;C^2rZ!1kf8z$w-|XxLMW*a+?z~%f8U+vT4RRnNeg7ybX%NawI!oC*v#ha< zV_F}srtVy%vX0ObN%G;_M^wOMrD+;HQ*)*l24iLhYaZaTcg>F*u4gSvhLbSZ3;6Gt z7#jW_)Sw-<*N^D%I2m;m6x9E-heFEdg)tD^dT=D&;y2rcq`3=emuClH!l_H*9 zznEkuzqA;CBh<{)R!3Xg%G6fSVNmK07K@XaA?WHc`oqD{5sRVJ2h%)~+$7`}8`B4} z_Z8S8Sh=*#b(We7nqc?G!;;$*!%*lzTGI9*KHH>%c*8em|iUurgeY%YzueEvf0ayK$Upab^T&^XxQ zG%{i_h0v%vd0-0{A0O6i`xvr^jOhDaaGA#kKIcTc%bfv5#2X{g(~RyM?W+3TSZN3L zzw_sFe+^sbA^#=(w8JOst=SCx1oL;m{uG-|&Bp!mOI0U@;g_8J$dX3;ldx4iq}JA! zmTIxq23sg3&fWX+Bq=7b#dzetbE5Ch2j1hQdv9&4n$Ma?fA?0VMuLLf>>n~Fb}F7h`T>FTa>a7;6cr|-|*;C}szfMvT@wX*~c-gjRA(`rl^RR|wGo9dlj z%kVcbHdLQ z8Z2(11GXPkQ!~w=v>O(Ry6||b_Sqpz7#<0OF1rfb{?WOrEwjAK3LY&#VrOTc%mSQ& z6^X0~FE8I*8p%btB@EU7(bDvbL)NM!i|_r| z68Ze;6q;WN3mhk=KEv3|jE3(8=hQQi_ixly<8?H(EeQ*`-or;+QTE*SOlJR}sO07{ zhoc_Dx6di-&&hAzH`LWpnRItTv1o7(X<#SMWUp(>^YH^2i(|1u`vupXfp}bOJjiSqF$6QGb4Np#waAYA-ez z3{Ff;PfyN==!YTXahE@GNqaWmU%3sLma71w(e6TMr zsTW;I@@B<*GMY6)O3aqey!8sL+TBH{&7T(Q(buC=laY+etz;)(>Ji$im{K1coUXeX zEcKqzNGnznU+|$rnNW|nCAQ&FO$A0v!Qnje3V;YJh2kqHI*TELVWB2zj5)K6nCg2IfPXZLEe4#UM%qth(y?W15%Xmduhdt6A6OO1=fU;56#?sib)IpTLU1Ao8W`75PL zg?N^k`JTAoGFzK9xJlnbR+-0X9PW%clS%RLUlHq<9FVyov>xb9Tz=FRS$GDo=tFO% ztvoq+?^}N96n>p4Nr_Up6|l!@IA)HH05-yS@?5g(<7$guNXH-hzh72|;rQd3;muyf zii6paYZ*}_D`{Ct5A&^AMVS#<9s+)+(O+8|?4)F+HyxU9>DD*T zZ-2KVA1ybt337$>fY&;`|5)YF4y6?qy;pT=wPjVS~{jyyeD5A25h03|9K)Z2v}M`IE1mPIYWeV&wv;$=TWI{?b3cr0VM& z-iJT@&Cjm(ysxI8(;WmOV!I7;~WRTLNZ1!egw ziXkG2Mnn-@MIkpVp>3?BboO+2|7XtLGcq2{4jIc_*aO2t=ImYoDWXyg6*2dg7h3L=ho>F=0!VGF8v42Frm z@LIK8+R^ueBV1e@{&0@`y-&*=(~NK&*(Bz4F#u@gDrg|WOdsbvAcyX=f2TXGlCBbl zP3VR~z^341!FH(;T*xfTW2@`dlA3{Un=ib0+z&y9t!2s7%i4E;T~EjFaoruR)wyMF zZvam(cMw@PPXu~p=#Q5r5f)0>joWgc0uaqID@XOra!2KQTHy#1#cb~TA(b( zi^B7M4?Ai0M!f)noaIiu-B@OM_)*l0&B6qaYuGdRT3LB|v{`WVGOJ|s_45?f7VpFR z=&zzoOm}!f!Mq z$SJFq0!gs2T+bl51O(~)p1k}VzR=m`(uLzP`liaEDLB63k%EGZZi5XWAHeI1lr!oy zFj9Z841&r9q#Abo?+}0LDZ~c|jp>)?aoAZd*Qm+&(1o6*niu8#6zXJpFVV)c_+sM{ z*zAVWPv=j{?xx_h=ynrQvJ!y)NDPPuYy;sWx82GulH`P!t8&M5{Zf)H%R_f0XQ!^p z1NP^~XyPlDqxi%kue7C`Qb!26x^E|a5Jt=rzMEx8lztkY5kPTLN`#A;KLw8O5($L; zpfJh_W0aEXWc{(NAB#)9#0LYHCAPIKk0r?5bG^h%k<{}yqhmWN32ibDX*)R zClDf{;xTKon#)LdS+ys@i$m{yNk`yU9w+del`_SoG5xA92oj8dfjOz}{q`JnS8hm3 zQ7y%SxE)%_gjQsE*SYk!um$3;V0NEGJ_?Xv5oHSNAj}v!nlyIPgZkvdR*dwj>UwKI zu)pEQ@#V^h*RRl4s*QvCwZs6hmyK85J6o($*{8ei`u9gfgx(p4!H>HNS(DhT zZAaU}C1C#u%+6E~QD2D0#Oo5_yC|z7ac6~Q?ZF6_^}NsRM$q8G)11O65 zvrpPDJ)nsiFct=;#mftS|A+lj_#;O3GpKh@Iawo@_w`X4kG)WMJI=jKR8ep(3t{zK z;g))k7Ahg~2VrOiz);#x#LYn3GWIX?ywQN{<+4ROUQd|8FK3^XeR)Fq@ z=MHpQm;NjuxUH>?SyGZkzs_Ok+rD$pK)+y<0XW!J2n?U?ZDLtVIiJ#T#$(ncASFEp zgMaz~LGGjoa8FKdEHa^2fF&r3-PljKWrm|cxsnoJTo}To7d;| zjF7`DoPh1yz}7GSr&*E?SBnLqlOm&$%`f9$(ODdvGX@HbP$RoA>2DHo*#Yc95j^L_Z^( zRxr%pg=J@ajA4P^Q&ZEzq9`E&x8*rtVpR(8WH0v?2ZXB%qx{|THNO%4 zf@YmjooooR-Pq|jyHU#oKXLoBV&dZBV*d^k6B9YP@L#`_@Zq4SYtm)+%X5oU@WILN z-NOZqI|C~0d@3Pnzw5p8T>ngfEq~aFn4XT!&d%=eeF*&VbnG1-=JGsEoF%ndOatfy zU7xhgXGM^2*f<185`>B60Zo4@e%H?nTm-c9(V`1s^d%Pb@<2hYzbw8Xh z|MTZhO%1y@GBUmfk9iVzq(VhMO##gg9$sRD0NMK5&`;IaTYO4xK_+RMgza53T)gds z#5aZcRQWWtG}P24d4%y$8pWk9;BLkw?t;n_gAGFmHlZ?$#pe4diqxw7>Y77(@S1AC#z>? z4(P~1Qb10NhLV%h1+b87YikcTry3o?G&D54ydE3fKXGW}y8gSlx~^_^X6EAh8g@`x z8IM5%U{4=TcVs?|kI9kPEi~Z^R-@kvev9XB; zqMe_e{ny=`W}AnjwDb@JJmTx)GoB@^DCQ5lD=aMBP35w_yu37<$c#vOsm&c|y0~){ ztfHc_y0Ws3ynBA<173L?jG+V)cpfTGUepUv#!k26)SbtTjSZbfyS_stCY_6iz>%RL z%h@6&NM}N>zq|X*Mo-WdDX`%JlC9_WzE70M+y7}Ns!fR;4@W=|i7Vh1qm@g}P8oR0 zh^mSo|3Q$I9RX~?mMH=iNPgjRe@0$TF1iM|8eq4wx*GJFxS;8S^Nt)65>gvuz_aXq zb}p_AKGzcHX;)X*$!hCXI}o!4Xe&WWqI)(Nb(=6*UIHR;f5!tqnX1XJo3B27N5-&Q zs36tH6hWMvbv;>WwqNf6g7Y{U`EFt$j|Qqo|IVBK92E~UG-PskZ@(&1jz(5S#_Cm+ zAN2M-Gcz+KB}IOF&c)TWuUHCLz*HqhHjBw@x8o((gIT(F?+^e7h~1)#!pp;hU$`- zNjp@Hll+&AO2mRPV^?elr%?c~f&SG3&WJZ#20!WKTH$lTS#D~S{HKz9@8uubY^>eIXYS=wx%sfs&h4IHAz$6qkI@pCap3@ zynP0lOaZ#H!k7KCxSx_b#}`_3Q?T@cU=A6Dozxfuog4uEu`Q$~1zL`khH=_PdQtMJ z-X;lnpF327Xeh260g4mUf{e{4j^%1!VjG}zAtllX5_Mhx$@3ssq$_EZ($oviF9ejC zrBf0DO)cu)=jf+e5r3H#z%Ht^q7=cA2YrSCb_+mBj}hr;oQn|QPpB3zP(-%E`2alu za^<2#0T5vk4k@7Bg53R+-l|uNQPLv4ha=5S@PL35;0r`0v96}YDeS2c0eGp>+H%fTneynAfor7B6sB3#Z><@?CKUwLh41)hj=)a_LGQ2 zcC%RjhA3xz{nQZ1jl73j`vHmxL8lCqgbw_a@{akD zVYn^RRj=}YORd#wApWkVoKTUm+{Vhd-@7&K?q27oqi1Z6(_+;3^0x9jNFRLYO?iu| zi?9k5A{Fo%7NnjAnzecAo8Zcwf)d`d>hde9uZ?4Ws1MtySWd2mqJOs^KR8l|4oi9wUxiy%@A%TY z^<}VyGVNBI@?uPrG6lBuzGLRCV}-{TosT-GWI^vz!h=ou79sad`)Me==Zmqq)wnsk zC`eeYH4BF`iS}k)ArtXr?Hcc5KPzJB^SBElk#Xb(`8kn!pL`_%`&*l9 zVg^wwF8Hf1OVY;@xvr<32PQt>u_C4)(FVJ!ZU$SA@-se_QCw>^#j< zCX^N9EQtGEaGv-}ACXq&1Q|(=TakTX*J&HWGQ9H3jB4%V_`QNzIz}LYU8Y^h1ejOH zLGC#TlF-)SsB86hHWWi%UC-~CT!b@(+?Z^)92Ay(386Nx$ya{m#(C=*e3hrruXFM; z;x4`M+nA9uLn1VTHbJQ>?(Z&Zm~=;=j$J%GM!Dbd@}_|wOked>264W+_zhu90sqZ z3r4h*MmS?B^JXRcwMbBa#55~DV(9`7)LZi`+#`K&mc2MUc+bmAJmtq#?OdhJCdbqe zt}-E(?1BozaBo?bpn*^_Zd&uL>YI7#Gu!0iMm?kicnlg~Y-Uy7D7mV*sH0@{Jkury zKG;){1J74&(L(YO6#A!9@=dW*f(X}x2SOBB;%XSHN3X&m>?nukl%JynXS+c;a=%>s zZHC4;rve(j0V^!FZJ2M~Yo3E6u1tqDqN|9V5TYe4Nmd;m#I+DSAtsC?n=dA|fYWPg zG(Vw*K@0z?4j>wfJA)9qC|R)-YW6!aYGv+DIjfwBcN8i@l0p{XLm88J0~(Gg^I5-V z&^wfk{?h!f%xz;TV>Az_R?*+KK79dZ5rKKXz)a|&`Z-hQ-M*pq?84ffjJl;*%cM-# zRm0?|t-S|x{twOm_JTpR(RoVRV z6gj5V*J((zn`pv}N-pY_EIPRz*Zug@KW{F?K2;ydlUtJog~&0y%X#xF zIoPNC3T&-K>_8fEr5xI)&0U(}S0%>iEKm_1+-B=ZC$3BZsozzz8;(A-=$EI9#!`~` z8XxRKdbJJQ`!L)=9y*4{(BjK5&Q%AUIv1%{3)Ezkz zP@kB+5%+a(G?o=#g-}+BFkd+_Vu<Wsdwvy7%qp)BoKViZm6vFY`0FBk)Jk-;4W-*7~5*z%nkCn5k z9X4tgND@e9BGn7V>&%^6eDlkw96YsX z@NBtk&s0=QoMI7ws@c7UewKTrhRQghL;dZ+m%e4zrIMmgcySc>S5`yc8QHLEp&lFf z-^bL^IH+@SoUX%gy7`L}*!m$o-7FFWl?0Zmi!OOb3N`V;k!x5tSKGE}_8#}-LqXod zbCiC-L$v&&*)T7e4%D`3>*zd?hRv8LJgIZmhL%R)@1i8TT63{9sqN-5w+` z7BcSLGQqXBjE&+%5{yKM8p5ebm0!yG#FR#5+U!2hzrDS!sipNx0{}O(?-P076q_4W z2$B`ibQOI(GAtWlo8B@@kot3g6iy^}lfWhs6F&EnHT*6dD=SW|XwAgDe;ZXRM$`EF zTt8VXWQD&IK$iWVE1#^DGrW`s3q_pW@aMHG{1t z?_T45A_~KwBxC}v1Ks7mkbS{9a3|}pDcS-9 zkq%hf(bF`Q?tIOX7w~cD+{Oz0ELh(>Yf3o0ZkxQ zDw2Ks71G&HhLGxmI2k<@u*3_;S8P*-y4#Dv!~2McpB^bQ_qzYqyTLjK=d5jRqBPd{y$h7^(?ys*be{1cis6X zKDS`T!W&cbVHuf_U=v8kNYSO4=Ry*jHeZWOT)+14maeVGpoSsWys~A36v~`8?{-NnP3Y;uqGI zCs+Wku6*|Q_ol`<1W+syQlpS+4=fN7c~4Ef(54(s91u!#{glkg$~rwg^}pDWM@0=Q zIP=ie)B6CFL;*E~|HVZ)*Z~EUJUk0aOML%@OsCYqC4e*Vpvy}J{sR4ft2q8Y!~K7K zfSw{e;CKs23LGN9Qa&9_iDXa3&qY9~V`6-Ko=;S5ZEa>I8CEsp2x~Yrs<&6H4-p9I z)z#G@oj_d|7!2f6=n@@WUBM2#!>CgE(?At-A7Vj40T4(IqhcxnT#2J&wNVN;P|KNS z!k*$|jU7ToO)Xv>0I(&%u_WQ-kpr)p#kj`e^imY)bUBqQjFVm_8_=oQ9PeNIhZKDeY{!Hsl zX=!QDcT74Ba-Imz&dzuJNh}7ihttkl#Yk&p-k6w7A=pklz!1q1}PZzg~WhRI)|j3YuG zXClWxSL?0uM`}<|P*z)9<5&dTjx@o~eSCbjZxqwGj{$mtab&H@QPh?)2!q^A3n)fz zFzEDy`~>LCk~R^5;?dDLeJaj<)l*}0F{b~25n69dsX(CHMxhA%)fQ@&{4HXT57JWs zJDup0Lfvw{xYWPYw-7(0`~oNy12B7FDsD%wIaQR4T~?YLm5bl7Jb$8%HjWl+tE;Qk z>dj0|kJs9X?dd@xlpJhq(h3UPY;3<}rw|>QSMp_(N*-V3P!A0c-yc?XmRKbWndmgw zV1bGm3 z)z;S6>(yJ$RT=gG+M$-7*?Itn> z?N^$rJ1?lcWqZNh&xHE=s~;#hXPdwiDzI3pvxLazw{bE6Mpxo9P-kUp zTi >Acta6p8ISKzjr{=xYfB7!~%uzYU-a#Fh$25tG^w$%aZ^`>*9eGjvdLbP@K$jN$Ojq%*_5KD5F-k}MZI{l zzA~06NGy5uu4Jq;f{3UxzS-rUI9usKHaqahjlyA(C3Gl1@Moe#iKajn=253w9 z?JIypvwhv!6=;?P>Kz#H_VlceRsP5YY)_!SFlIF!z|5heqmP|`b8+1`Ie~2t;i#s8 zSWoxLfb0jbIjuXQ4E&neT+HN|VAwX3Z}#SVaZ1(8A*c8bJvTI1Y`vIly{uw#;B_&` zdr3c)JA--}aQ(I|%X~?TbVts0y_9SD@S~-UY;j0&ONc62xJ=x(hm&Mdgvn#S5*6uziG$ylhs#T8EFtbDBCOF6DZja3ttwsdk4Mn@?@Z= ze}O^f15k&)zK?-^3=_IdMpkALVi5XhXx8wz$%__Dojc*a*UDO%zJ9WnDfqPIJc{hC*%@#KXNx6(hA^R$|GXVRLgoaUQveKMaIrsZuB?17lk9~&8sAfS;gVyDZjusgI0>tx;X% zO%hkJuMBIwt#a6{!F;d914zjhTY~!LV6{|So=*nsHMeI_4yuF{4pYpD!Rbq_ms_Uw z${GgpVYZ0HzR%A#KUS#;loPEEVTVm%S>T}rh*2yyIp*rSxARFrSwc3X3JDa-i+9*a z=H3T-pZ_t5C%w`fJj4oH|tz1#P2+k@t?p3T0yWIz4bvofN7|T`^wB6mu;^9GSmyC@Y zQdhEDFb#B?CTfV-1h7h}Yczzmi-ys@mQ#=_F~dtwlT5iXW#yG$1kfacNB~`@zHZoG z1!NrqQkNlP9Dh*&D>a$o;;=tWjY^CfQ{41Xd4NU>wo4m=h=}O$>aB$#07uq5cQb&L zZs6L~@9)SXRHosyg>#8;*%LDY$ZPG{yzdaxOdW1i9mMRY)N%O+%fXjqNP42IN zGftSKg7Amu-pg%kV|yrW zD$WH57gud#Hgp)?%U*nwxd9N-d=vZ&xfL8j<&97l2Hag}+{$EJWJd?=?qZjC1HVgK zbXQ#uCiw~!A%271{S^sWcd12aM$u*t3=irf<#)ThzLrp&{^;p>2dF5o-j$BxT_;6* zRa;&i;QBmn5M0i7JA?_X1M3a7ZSM>`_}hJhu9n_sMskhj93J(WtC z;2gcIj(s=-iZ*-hD)b5H#N1^{m9>Eo;covY1(c%V^N0Ba`bwyMB@zt}E3R$s(!C)# z@ti67odtLAl7!G$i(1Qm^;|`5Q4hr1d12-33)Cu zqW}WHMiI-TBCAXZ{pZ77GKh!tQOwZ8p+4-?V)ge(0mLgPP2#c1Z~I1d_uEO!B#S>8 zOehVUJ{C zKFz;Ynp_v$K97Bnq&clLzB90$USx#?wIsn4Xh>H_;Q3J(;b>^Y=F{g_SGH{xfGz{; z>cR(m@prQ?>_l(Hp5vzE)$EQ-2JrT#!q*I&wo`NAB3CpG@sBq_42N7Z7pZStI!VqN zazj>EddLTL{F)np+G0LD7kTzUTB;vC8`;GBBJ>Fo29bD*hHC5QiMp^#sli?L^z7`n zZ4Z->D#oX}grBJR>}WG0t-KBpmWJ1*ZNBwLerBD??rlc3p8PF~?2A8no3KY@&hb3wgV|zWck(FT8mN5|c?vSCpZy(9U?9P7FG1w8 z7A;lE{1rq_O1%HU$SmpXMuA0xGvbP3F2YxbX1ivdaxW#PHw8Xyqy|}TEoBnsf`wyvqOkdVU3Gk67-Hi6lvJDo?YCMcn z=fwJAL<)~6(&WHS1ie~c5JJ%KUdYQ1Dhi(Sc8jeCEBQS9X4N}tf6;VPg_$fg=;!r! z!?IfSlUUF4jVZy7*V5x8`Xl4B0%Wpu`S)@vu)oZ*gQ$ME1Mt1Q@eF>7>frj_1@29M z>4QJ2BXBgf`=OlZXv}mrJZ&q)=(sol+xJj|VwAALof9PCI>g#h5Hvx^o~9~v{wR~LcbIx^E9b;LtAlgJ}xjTuO$8Im1bbV$lx^Lave+pMS_alY! z(*D_dzC33DdH_|iV&|Sng;4`K7);nfJ$IKgio~41gFEpRad=C5?!?(KPxycKI3%Wf zw2obPN>btCu*t+Jn0c4FCB!LA<5HOUb)nJTwW-9;jn^E1>q^OW4>Is#J00vC%T#_> zp6=4^J5Vae_d!vWHUp;OBY|9ok)S5HPZGgzFqfyojD@A7b`c3h01GJ`uC=q#kfz8L z+%}#&O9O;Wss&Y>kw&=@Tn@kR6pkn#q{ zAMO`wfTKQXe}2bEe|>QwNPI)(~Jj(w-M&iS^f0X6?uG(puaL!Ro6@u#-m;Y7#)&P|P zJC{`csB&CTaN0Rp8S$-fNuiTe+5xc!*Zy__E^DtqmWxk3ZANpDuqq5{7_216SBvgT z6*r_xODbWy=6Joxy;CYw0W;6kr#7N`4mRz}XR-%eB|yU6x|eYCm0g@|h!XP?6K2g3 z6`zMzN!EiOF~!AC91&HJ^z`Mewnbx#i&^#&F3i|rTob%qAHKTOi9wkX zlk;-ykGSNo^te=sJfAhLPeZ}ZzTASF03XTPNl8h)yS@Ee9)t(HiQL=Wos*ZB_q753cLS;ahv@wc>25OLAhZ@I?(8x~w@aUF{RwQ-HwwR|~*~Z9HwKFV$9*>=NjKrpl?O*ohnC z5fVHd#=)?gF>IVX8`RbqQD`)+)FE71*0o&l-DsK(Ke@#Js0g;E!L~nW!hR(k6f?nJ zon}W$WfY6b5j?25{&|gRn6bgR%6>l%9mshjX2C(><6^)%|NsB$u zoWu{Ch*3?>obJvKjJ3&s`WS1*ss362i}7})Pi0$icG+bARA?Q!>@aI@@^dMEU>2otg<2wj=dKk%1RhXC{ zkf4X6e`yAXREh_v6&s$I*yTgD!u=l|#mddno$~Dp2;>(a`n#?MFkV2llD@v3iODSH za;MiWk(Qe0iT_<&t$TNvCv9(eR9dsO(Z`x4sZkb|y#j&#_i8xwyQFh}^r~4@I;w$% z`f-&Sr!Z%ZST3iv9jDOJARSbk?oPig57AUQU!)kJyExdJ<2@~aKFf}9Ci?v8F00NK zJ^SlEus||0uhu^qSZAA=e)&+aHMm{@7xEcMuF^A>JRxe_`=f)#HuG3u5wU?t(jqXX z=&^Q5A$Q|`>6@BW^!`HC&x;{s!dpV@yTJYXAa|q{@5l=>*wEdaGkSaD@ri*IhECRq zip&do4QYBg%A5-HHHf074kd%R%0phai!B=oU6A{VQE^xpDqpl6fRZd`#sfE;w<`^sDK$$#jXR1QtDFO|3}0EAtO ztVkA3n9G@NK!ubD@!ak2-{9{LU*S@)TuUjrqr)m<-!F=u;8*KCk@NBU<^yP#f$Q{# z?1tFsybIv#xj);!rj;Du(gbRr!Eh7ze4eqL9T_R9t({%P@&WLkD1%0YoUClAEyd?j zp!(NM-A{?wl%&PM(K_j_``u1&yhi#) zJx4NDA!9E-xV^r~_+oE!V>iL0E0@u*x|+)rSc+DS{VG;bFl28Z`w|>*!?)~4GUfNh z-TZ$M^%hW7ebM&t6_8L$5K&rE8tE=c>26TELpm>lAR*G7(v5U;6{NdGy1VmI_uKs5 z|GkglxI-CoIA@=|*IsL`Ip;>;IlX$;m@IJVhUEpDMxy*w?Gi82{&%lCV~R|;F2jdK ziBH)(Es-SF%R%C+-^YwQp|UxV?dGVEi(BWIq7}}uup>oPb$g&C!OnF?qHy+n_8GzD z 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

@T*iXc+QM z^mRISUw(j!n?L0*_Jn*~|J^%tFzCpw7Kx;;Z@P<4{mI@Nhe6-*7Un^D@yGmDQyu}| zcD0}E$Z&TsR+f{WuCTuT^X-j%NGN1^a^lo^{__IdRfu2c{nQ32_(y!~BPJ%ll}Br2 z$M|%Mt%~wj>>{~lbauGuB_%D^V~lrmz&{U(xOCr}9{Kfa1#oKD*ViF!>yCIr#3`+f z9jKZi9M%y{i0n!(Z0xRRYm_DV?6-QVuB}vk6b&T3JT{?Ej%;svZ?$>#qofQQQmeqoYnV^uK-7#rU$9DDDzLM zFS>CPj^(~VBd8Z!5W_+l`SzT7%Wb7f%B3+nrAunYY6rt}^nA?khF>JmdDmWSJHY?8 z=>@(;9o-Ft9h)$rW&e9iqIs&avw;WuTVvx^>xfyPbOEI^8!PJ-LvV1g3}|J0oml}M zZLV?(YgZ0>dVapk#cH?^FC)jv55M)%hG*UpTRS^wki7KevXb2{rQ}A(xzvyo9=8Mm z$$-{~_7v27wY-7MT9moz~QW_?$%dcoiQKG!OYvU&s0C3zxfQ> zR4hC^>gB^j|9&EQM#166U)leWEv(a~ZFCcZ)RtA`8zDNf%E~&e$QBt zN^IeL^9Gnu)klKcV*=JXcT@}{Bt%|w z=%2ekkNiUhHN5BM2Pn5L#szk7Et+rk_s)OSG$pzewQn%#Wh8vaXV%uhRu}&2huU2v7TLkp2YK^Jb+s9yptC@nR ziXWKΜ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 Yb(3NG!mqqn#|XS+r4;YxN$9`$UztbYQUCw| literal 0 HcmV?d00001 From d90da64097a80c5eaa22b60bae1b2056b85b8b55 Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Thu, 14 Jan 2021 01:38:38 +0200 Subject: [PATCH 02/11] Add wolfBoot config for measured boot and makefile for the test-app Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/Makefile | 109 ++++++++++++++++++ .../measured.wolfboot.config | 26 +++++ 2 files changed, 135 insertions(+) create mode 100644 test-app-STM32F4-measured-boot/Makefile create mode 100644 test-app-STM32F4-measured-boot/measured.wolfboot.config diff --git a/test-app-STM32F4-measured-boot/Makefile b/test-app-STM32F4-measured-boot/Makefile new file mode 100644 index 0000000..c18382b --- /dev/null +++ b/test-app-STM32F4-measured-boot/Makefile @@ -0,0 +1,109 @@ +# Inherit our settings for wolfBoot, TARGET, ARCH, etc. +-include measured.wolfboot.config + +# Make sure environment variables do not corrupt the binary output for MacOS users +LANG= +LC_COLLATE="C" +LC_CTYPE="C" +LC_MESSAGES="C" +LC_MONETARY="C" +LC_NUMERIC="C" +LC_TIME="C" +LC_ALL= + +APPSRC:=./src +WOLFBOOT:=../wolfBoot +WOLFSSL_ROOT:=../wolfBoot/lib/wolfssl +WOLFSSL_BUILD:=./build/lib +ECCKEY:=$(WOLFBOOT)/ecc256.der +DEBUG?=1 + +include $(WOLFBOOT)/tools/config.mk + +CFLAGS:=-g -ggdb -Wall -Wstack-usage=1024 -ffreestanding -Wno-unused -DPLATFORM_$(TARGET) -I$(WOLFBOOT)/include -I$(WOLFSSL_ROOT)/wolfssl -nostartfiles +CFLAGS+=-DWOLFBOOT_HASH_SHA256 + +APP_OBJS:= \ + $(APPSRC)/app_$(TARGET).o \ + $(APPSRC)/led.o \ + $(APPSRC)/system.o \ + $(APPSRC)/timer.o \ + $(WOLFBOOT)/hal/$(TARGET).o \ + $(WOLFBOOT)/src/libwolfboot.o \ + $(APPSRC)/startup_arm.o + +# Inherit cross-compiler and similar settings from wolfBoot +include ../wolfBoot/arch.mk + +ifneq ($(DEBUG),0) + CFLAGS+=-O0 -ggdb3 +else + CFLAGS+=-Os +endif + +vpath %.c $(dir $(WOLFSSL_ROOT)/src) +vpath %.c $(dir $(WOLFSSL_ROOT)/wolfcrypt/src) + +ENTRY_POINT=`cat .entry-point-address` +LSCRIPT:=$(APPSRC)/target-app.ld +LSCRIPT_TEMPLATE:=$(APPSRC)/$(ARCH).ld +LDFLAGS:=$(CFLAGS) -T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map + +wolfboot-example: image.bin wolfboot_align.bin + python3 $(WOLFBOOT)/tools/keytools/sign.py --ecc256 image.bin $(ECCKEY) 1 + cat wolfboot-align.bin image_v1_signed.bin >factory.bin + +wolfboot-align.bin:LSCRIPT:=$(WOLFBOOT)/target.ld +wolfboot_align.bin:CFLAGS+=-DWOLFBOOT_HASH_SHA256 +wolfboot_align.bin: wolfboot_target + make -C $(WOLFBOOT) align + cp $(WOLFBOOT)/wolfboot-align.bin . + cp $(WOLFBOOT)/wolfboot.elf . + +image.bin: image.elf wolfboot_target + $(OBJCOPY) -O binary image.elf $@ + $(SIZE) image.elf + +image.elf: wolfboot_target $(APP_OBJS) $(LSCRIPT) + @echo "\t[LD] $@" + $(Q)$(LD) $(LDFLAGS) $(APP_OBJS) -o $@ + +wolfboot_target: FORCE + cp -f measured.wolfboot.config $(WOLFBOOT)/.config + make -C $(WOLFBOOT) include/target.h + +%.o:%.c + @echo "\t[CC-$(ARCH)] $@" + $(Q)$(CC) $(CFLAGS) -c -o $@ $^ + +%.o:%.S + @echo "\t[AS-$(ARCH)] $@" + $(Q)$(CC) $(CFLAGS) -c -o $@ $^ + +clean: + make -C $(WOLFBOOT) clean + @rm -f *.bin *.elf $(OBJS) wolfboot.map *.bin *.hex src/*.o tags *.map + +$(LSCRIPT): $(LSCRIPT_TEMPLATE) FORCE + @printf "%d" $(WOLFBOOT_PARTITION_BOOT_ADDRESS) > .wolfboot-offset + @printf "%d" $(WOLFBOOT_PARTITION_SIZE) > .partition-size + @printf "%d" $(IMAGE_HEADER_SIZE) > .header-size + @expr `cat .wolfboot-offset` + `cat .header-size` > .entry-point + @printf "0x%X" `cat .entry-point` > .entry-point + @expr `cat .partition-size` - `cat .header-size` > .app-size + @printf "0x%X" `cat .app-size` > .app-size + @ cat $(LSCRIPT_TEMPLATE) | \ + sed -e "s/##WOLFBOOT_TEST_APP_SIZE##/`cat .app-size`/g" | \ + sed -e "s/##WOLFBOOT_TEST_APP_ADDRESS##/`cat .entry-point`/g" \ + > $(@) + @rm -f .app-size .entry-point .wolfboot-offset .partition-size .header-size + +flash: FORCE + st-flash write factory.bin 0x08000000 + +erase: FORCE + st-flash erase + +FORCE: + +.PHONY: FORCE clean diff --git a/test-app-STM32F4-measured-boot/measured.wolfboot.config b/test-app-STM32F4-measured-boot/measured.wolfboot.config new file mode 100644 index 0000000..005cc73 --- /dev/null +++ b/test-app-STM32F4-measured-boot/measured.wolfboot.config @@ -0,0 +1,26 @@ +ARCH?=ARM +TARGET?=stm32f4 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=1 +VTOR?=1 +CORTEX_M0?=0 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=0 +DUALBANK_SWAP?=0 +IMAGE_HEADER_SIZE?=256 +WOLFBOOT_PARTITION_SIZE?=0x20000 +WOLFBOOT_SECTOR_SIZE?=0x20000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x20000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x40000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x60000 +WOLFTPM?=1 +MEASURED_BOOT?=1 +MEASURED_PCR_A?=16 From 39fdbeb9fbd5eadd705e60518b5f15c310a5f8cc Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Mon, 18 Jan 2021 20:08:56 +0200 Subject: [PATCH 03/11] Add .gdbinit to the example for easy of use Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/.gdbinit | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test-app-STM32F4-measured-boot/.gdbinit diff --git a/test-app-STM32F4-measured-boot/.gdbinit b/test-app-STM32F4-measured-boot/.gdbinit new file mode 100644 index 0000000..082ef20 --- /dev/null +++ b/test-app-STM32F4-measured-boot/.gdbinit @@ -0,0 +1,4 @@ +tar rem:3333 +file wolfboot.elf +foc c + From d8d200db6977485c3e6d9ab8ff31401ab730eb3f Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Thu, 14 Jan 2021 01:42:14 +0200 Subject: [PATCH 04/11] Add source code of the test-app Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/src/ARM.ld | 45 +++ .../src/app_stm32f4.c | 298 ++++++++++++++++ test-app-STM32F4-measured-boot/src/led.c | 180 ++++++++++ test-app-STM32F4-measured-boot/src/led.h | 35 ++ .../src/startup_arm.c | 337 ++++++++++++++++++ test-app-STM32F4-measured-boot/src/system.c | 147 ++++++++ test-app-STM32F4-measured-boot/src/system.h | 67 ++++ .../src/target-app.ld | 45 +++ test-app-STM32F4-measured-boot/src/timer.c | 171 +++++++++ test-app-STM32F4-measured-boot/src/timer.h | 30 ++ 10 files changed, 1355 insertions(+) create mode 100644 test-app-STM32F4-measured-boot/src/ARM.ld create mode 100644 test-app-STM32F4-measured-boot/src/app_stm32f4.c create mode 100644 test-app-STM32F4-measured-boot/src/led.c create mode 100644 test-app-STM32F4-measured-boot/src/led.h create mode 100644 test-app-STM32F4-measured-boot/src/startup_arm.c create mode 100644 test-app-STM32F4-measured-boot/src/system.c create mode 100644 test-app-STM32F4-measured-boot/src/system.h create mode 100644 test-app-STM32F4-measured-boot/src/target-app.ld create mode 100644 test-app-STM32F4-measured-boot/src/timer.c create mode 100644 test-app-STM32F4-measured-boot/src/timer.h diff --git a/test-app-STM32F4-measured-boot/src/ARM.ld b/test-app-STM32F4-measured-boot/src/ARM.ld new file mode 100644 index 0000000..24318ae --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/ARM.ld @@ -0,0 +1,45 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = ##WOLFBOOT_TEST_APP_ADDRESS##, LENGTH = ##WOLFBOOT_TEST_APP_SIZE## + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K /* Run in lowmem */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.init) + *(.fini) + *(.text*) + *(.rodata*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss : + { + _start_bss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + _end = .; + } > RAM +} + +PROVIDE(_start_heap = _end); +PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); diff --git a/test-app-STM32F4-measured-boot/src/app_stm32f4.c b/test-app-STM32F4-measured-boot/src/app_stm32f4.c new file mode 100644 index 0000000..6b4fea2 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/app_stm32f4.c @@ -0,0 +1,298 @@ +/* stm32f4.c + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include "system.h" +#include "timer.h" +#include "led.h" +#include "hal.h" +#include "wolfboot/wolfboot.h" +#include "spi_flash.h" + +#ifdef PLATFORM_stm32f4 + +#define UART1 (0x40011000) + +#define UART1_SR (*(volatile uint32_t *)(UART1)) +#define UART1_DR (*(volatile uint32_t *)(UART1 + 0x04)) +#define UART1_BRR (*(volatile uint32_t *)(UART1 + 0x08)) +#define UART1_CR1 (*(volatile uint32_t *)(UART1 + 0x0c)) +#define UART1_CR2 (*(volatile uint32_t *)(UART1 + 0x10)) + +#define UART_CR1_UART_ENABLE (1 << 13) +#define UART_CR1_SYMBOL_LEN (1 << 12) +#define UART_CR1_PARITY_ENABLED (1 << 10) +#define UART_CR1_PARITY_ODD (1 << 9) +#define UART_CR1_TX_ENABLE (1 << 3) +#define UART_CR1_RX_ENABLE (1 << 2) +#define UART_CR2_STOPBITS (3 << 12) +#define UART_SR_TX_EMPTY (1 << 7) +#define UART_SR_RX_NOTEMPTY (1 << 5) + + +#define CLOCK_SPEED (168000000) + +#define APB2_CLOCK_ER (*(volatile uint32_t *)(0x40023844)) +#define UART1_APB2_CLOCK_ER (1 << 4) + +#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830)) +#define GPIOB_AHB1_CLOCK_ER (1 << 1) +#define GPIOB_BASE 0x40020400 + +#define GPIOB_MODE (*(volatile uint32_t *)(GPIOB_BASE + 0x00)) +#define GPIOB_AFL (*(volatile uint32_t *)(GPIOB_BASE + 0x20)) +#define GPIOB_AFH (*(volatile uint32_t *)(GPIOB_BASE + 0x24)) +#define UART1_PIN_AF 7 +#define UART1_RX_PIN 7 +#define UART1_TX_PIN 6 + +#define MSGSIZE 16 +#define PAGESIZE (256) +static uint8_t page[PAGESIZE]; +static const char ERR='!'; +static const char START='*'; +static const char UPDATE='U'; +static const char ACK='#'; +static uint8_t msg[MSGSIZE]; + + + +void uart_write(const char c) +{ + uint32_t reg; + do { + reg = UART1_SR; + } while ((reg & UART_SR_TX_EMPTY) == 0); + UART1_DR = c; +} + +static void uart_pins_setup(void) +{ + uint32_t reg; + AHB1_CLOCK_ER |= GPIOB_AHB1_CLOCK_ER; + /* Set mode = AF */ + reg = GPIOB_MODE & ~ (0x03 << (UART1_RX_PIN * 2)); + GPIOB_MODE = reg | (2 << (UART1_RX_PIN * 2)); + reg = GPIOB_MODE & ~ (0x03 << (UART1_TX_PIN * 2)); + GPIOB_MODE = reg | (2 << (UART1_TX_PIN * 2)); + + /* Alternate function: use low pins (6 and 7) */ + reg = GPIOB_AFL & ~(0xf << ((UART1_TX_PIN) * 4)); + GPIOB_AFL = reg | (UART1_PIN_AF << ((UART1_TX_PIN) * 4)); + reg = GPIOB_AFL & ~(0xf << ((UART1_RX_PIN) * 4)); + GPIOB_AFL = reg | (UART1_PIN_AF << ((UART1_RX_PIN) * 4)); +} + +int uart_setup(uint32_t bitrate, uint8_t data, char parity, uint8_t stop) +{ + uint32_t reg; + /* Enable pins and configure for AF7 */ + uart_pins_setup(); + /* Turn on the device */ + APB2_CLOCK_ER |= UART1_APB2_CLOCK_ER; + + /* Configure for TX + RX */ + UART1_CR1 |= (UART_CR1_TX_ENABLE | UART_CR1_RX_ENABLE); + + /* Configure clock */ + UART1_BRR = CLOCK_SPEED / bitrate; + + /* Configure data bits */ + if (data == 8) + UART1_CR1 &= ~UART_CR1_SYMBOL_LEN; + else + UART1_CR1 |= UART_CR1_SYMBOL_LEN; + + /* Configure parity */ + switch (parity) { + case 'O': + UART1_CR1 |= UART_CR1_PARITY_ODD; + /* fall through to enable parity */ + case 'E': + UART1_CR1 |= UART_CR1_PARITY_ENABLED; + break; + default: + UART1_CR1 &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD); + } + /* Set stop bits */ + reg = UART1_CR2 & ~UART_CR2_STOPBITS; + if (stop > 1) + UART1_CR2 = reg & (2 << 12); + else + UART1_CR2 = reg; + + /* Turn on uart */ + UART1_CR1 |= UART_CR1_UART_ENABLE; + + return 0; +} + +char uart_read(void) +{ + char c; + volatile uint32_t reg; + do { + reg = UART1_SR; + } while ((reg & UART_SR_RX_NOTEMPTY) == 0); + c = (char)(UART1_DR & 0xff); + return c; +} + +static void ack(uint32_t _off) +{ + uint8_t *off = (uint8_t *)(&_off); + int i; + uart_write(ACK); + for (i = 0; i < 4; i++) { + uart_write(off[i]); + } +} + +static int check(uint8_t *pkt, int size) +{ + int i; + uint16_t c = 0; + uint16_t c_rx = *((uint16_t *)(pkt + 2)); + uint16_t *p = (uint16_t *)(pkt + 4); + for (i = 0; i < ((size - 4) >> 1); i++) + c += p[i]; + if (c == c_rx) + return 0; + return -1; +} + +volatile uint32_t time_elapsed = 0; +void main(void) { + uint32_t tlen = 0; + volatile uint32_t recv_seq; + uint32_t r_total = 0; + uint32_t tot_len = 0; + uint32_t next_seq = 0; + uint32_t version = 0; + uint8_t *v_array = (uint8_t *)&version; + int i; + memset(page, 0xFF, PAGESIZE); + boot_led_on(); + flash_set_waitstates(); + clock_config(); + led_pwm_setup(); + pwm_init(CPU_FREQ, 0); + + /* Dim the led by altering the PWM duty-cicle + * in isr_tim2 (timer.c) + * + * Every 50ms, the duty cycle of the PWM connected + * to the blue led increases/decreases making a pulse + * effect. + */ + timer_init(CPU_FREQ, 1, 50); + uart_setup(115200, 8, 'N', 1); + memset(page, 0xFF, PAGESIZE); + asm volatile ("cpsie i"); + + while(time_elapsed == 0) + WFI(); + + + hal_flash_unlock(); + version = wolfBoot_current_firmware_version(); + if ((version & 0x01) == 0) + wolfBoot_success(); +#ifdef EXT_ENCRYPTED + wolfBoot_set_encrypt_key("0123456789abcdef0123456789abcdef", 32); +#endif + uart_write(START); + for (i = 3; i >= 0; i--) { + uart_write(v_array[i]); + } + while (1) { + r_total = 0; + do { + while(r_total < 2) { + msg[r_total++] = uart_read(); + if ((r_total == 2) && ((msg[0] != 0xA5) || msg[1] != 0x5A)) { + r_total = 0; + continue; + } + } + msg[r_total++] = uart_read(); + if ((tot_len == 0) && r_total == 2 + sizeof(uint32_t)) + break; + if ((r_total > 8) && (tot_len <= ((r_total - 8) + next_seq))) + break; + } while (r_total < MSGSIZE); + if (tot_len == 0) { + tlen = msg[2] + (msg[3] << 8) + (msg[4] << 16) + (msg[5] << 24); + if (tlen > WOLFBOOT_PARTITION_SIZE - 8) { + uart_write(ERR); + uart_write(ERR); + uart_write(ERR); + uart_write(ERR); + uart_write(START); + recv_seq = 0; + tot_len = 0; + continue; + } + tot_len = tlen; + ack(0); + continue; + } + if (check(msg, r_total) < 0) { + ack(next_seq); + continue; + } + recv_seq = msg[4] + (msg[5] << 8) + (msg[6] << 16) + (msg[7] << 24); + if (recv_seq == next_seq) + { + int psize = r_total - 8; + int page_idx = recv_seq % PAGESIZE; + memcpy(&page[recv_seq % PAGESIZE], msg + 8, psize); + page_idx += psize; + if ((page_idx == PAGESIZE) || (next_seq + psize >= tot_len)) { + uint32_t dst = (WOLFBOOT_PARTITION_UPDATE_ADDRESS + recv_seq + psize) - page_idx; + if ((dst % WOLFBOOT_SECTOR_SIZE) == 0) { + hal_flash_erase(dst, WOLFBOOT_SECTOR_SIZE); + } + hal_flash_write(dst, page, PAGESIZE); + memset(page, 0xFF, PAGESIZE); + } + next_seq += psize; + } + ack(next_seq); + if (next_seq >= tot_len) { + /* Update complete */ + spi_flash_probe(); + wolfBoot_update_trigger(); + spi_release(); + hal_flash_lock(); + break; + } + } + /* Wait for reboot */ + while(1) + ; +} +#endif /** PLATFORM_stm32f4 **/ + diff --git a/test-app-STM32F4-measured-boot/src/led.c b/test-app-STM32F4-measured-boot/src/led.c new file mode 100644 index 0000000..e183f6c --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/led.c @@ -0,0 +1,180 @@ +/* led.c + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include "wolfboot/wolfboot.h" + +#ifdef PLATFORM_stm32f4 + +#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830)) +#define GPIOD_AHB1_CLOCK_ER (1 << 3) + +#define GPIOD_BASE 0x40020c00 +#define GPIOD_MODE (*(volatile uint32_t *)(GPIOD_BASE + 0x00)) +#define GPIOD_OTYPE (*(volatile uint32_t *)(GPIOD_BASE + 0x04)) +#define GPIOD_OSPD (*(volatile uint32_t *)(GPIOD_BASE + 0x08)) +#define GPIOD_PUPD (*(volatile uint32_t *)(GPIOD_BASE + 0x0c)) +#define GPIOD_ODR (*(volatile uint32_t *)(GPIOD_BASE + 0x14)) +#define GPIOD_BSRR (*(volatile uint32_t *)(GPIOD_BASE + 0x18)) +#define GPIOD_AFL (*(volatile uint32_t *)(GPIOD_BASE + 0x20)) +#define GPIOD_AFH (*(volatile uint32_t *)(GPIOD_BASE + 0x24)) +#define LED_PIN (15) +#define LED_BOOT_PIN (14) +#define GPIO_OSPEED_100MHZ (0x03) +void led_pwm_setup(void) +{ + uint32_t reg; + AHB1_CLOCK_ER |= GPIOD_AHB1_CLOCK_ER; + reg = GPIOD_MODE & ~ (0x03 << (LED_PIN * 2)); + GPIOD_MODE = reg | (2 << (LED_PIN * 2)); + + reg = GPIOD_OSPD & ~(0x03 << (LED_PIN * 2)); + GPIOD_OSPD = reg | (0x03 << (LED_PIN * 2)); + + reg = GPIOD_PUPD & ~(0x03 << (LED_PIN * 2)); + GPIOD_PUPD = reg | (0x02 << (LED_PIN * 2)); + + /* Alternate function: use high pin */ + reg = GPIOD_AFH & ~(0xf << ((LED_PIN - 8) * 4)); + GPIOD_AFH = reg | (0x2 << ((LED_PIN - 8) * 4)); +} + +void boot_led_on(void) +{ + uint32_t reg; + uint32_t pin = LED_BOOT_PIN;// - 2 * (wolfBoot_current_firmware_version() & 0x01); + AHB1_CLOCK_ER |= GPIOD_AHB1_CLOCK_ER; + reg = GPIOD_MODE & ~(0x03 << (pin * 2)); + GPIOD_MODE = reg | (1 << (pin * 2)); + reg = GPIOD_PUPD & ~(0x03 << (pin * 2)); + GPIOD_PUPD = reg | (1 << (pin * 2)); + GPIOD_BSRR |= (1 << pin); +} + +#endif /* PLATFORM_stm32f4 */ + +#ifdef PLATFORM_stm32l0 +#define LED_BOOT_PIN (5) + +#define RCC_IOPENR (*(volatile uint32_t *)(0x4002102C)) +#define IOPAEN (1 << 0) + + +#define GPIOA_BASE 0x50000000 +#define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) +#define GPIOA_OTYPE (*(volatile uint32_t *)(GPIOA_BASE + 0x04)) +#define GPIOA_OSPD (*(volatile uint32_t *)(GPIOA_BASE + 0x08)) +#define GPIOA_PUPD (*(volatile uint32_t *)(GPIOA_BASE + 0x0c)) +#define GPIOA_ODR (*(volatile uint32_t *)(GPIOA_BASE + 0x14)) +#define GPIOA_BSRR (*(volatile uint32_t *)(GPIOA_BASE + 0x18)) +#define GPIOA_AFL (*(volatile uint32_t *)(GPIOA_BASE + 0x20)) +#define GPIOA_AFH (*(volatile uint32_t *)(GPIOA_BASE + 0x24)) + + +void boot_led_on(void) +{ + uint32_t reg; + uint32_t pin = LED_BOOT_PIN; + RCC_IOPENR |= IOPAEN; + reg = GPIOA_MODE & ~(0x03 << (pin * 2)); + GPIOA_MODE = reg | (1 << (pin * 2)); + reg = GPIOA_PUPD & ~(0x03 << (pin * 2)); + GPIOA_PUPD = reg | (1 << (pin * 2)); + GPIOA_BSRR |= (1 << pin); +} + + +#endif /* PLATFORM_stm32l0 */ + +#ifdef PLATFORM_stm32g0 +#include +#include "wolfboot/wolfboot.h" + + +/*GPIOA5*/ +#define RCC_IOPENR (*(volatile uint32_t *)(0x40021034)) // 40021034 +#define RCC_IOPENR_GPIOAEN (1 << 0) + +#define GPIOA_BASE 0x50000000 +#define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) +#define GPIOA_OTYPE (*(volatile uint32_t *)(GPIOA_BASE + 0x04)) +#define GPIOA_OSPD (*(volatile uint32_t *)(GPIOA_BASE + 0x08)) +#define GPIOA_PUPD (*(volatile uint32_t *)(GPIOA_BASE + 0x0c)) +#define GPIOA_ODR (*(volatile uint32_t *)(GPIOA_BASE + 0x14)) +#define GPIOA_BSRR (*(volatile uint32_t *)(GPIOA_BASE + 0x18)) +#define GPIOA_AFL (*(volatile uint32_t *)(GPIOA_BASE + 0x20)) +#define GPIOA_AFH (*(volatile uint32_t *)(GPIOA_BASE + 0x24)) +#define LED_PIN (5) +#define LED_BOOT_PIN (5) +#define GPIO_OSPEED_100MHZ (0x03) + +void boot_led_on(void) +{ + uint32_t reg; + uint32_t pin = LED_BOOT_PIN; + RCC_IOPENR |= RCC_IOPENR_GPIOAEN; + reg = GPIOA_MODE & ~(0x03 << (pin * 2)); + GPIOA_MODE = reg | (1 << (pin * 2)); // general purpose output mode + reg = GPIOA_PUPD & ~(0x03 << (pin * 2)); + GPIOA_PUPD = reg | (1 << (pin * 2)); // pull-up + GPIOA_BSRR |= (1 << pin); // set pin +} + +#endif /** PLATFORM_stm32g0 **/ + +#ifdef PLATFORM_stm32wb +#define LED_BOOT_PIN (0) +#define RCC_AHB2_CLOCK_ER (*(volatile uint32_t *)(0x5800004C)) +#define GPIOB_AHB2_CLOCK_ER (1 << 1) + +#define GPIOB_BASE 0x48000400 +#define GPIOB_MODE (*(volatile uint32_t *)(GPIOB_BASE + 0x00)) +#define GPIOB_OTYPE (*(volatile uint32_t *)(GPIOB_BASE + 0x04)) +#define GPIOB_OSPD (*(volatile uint32_t *)(GPIOB_BASE + 0x08)) +#define GPIOB_PUPD (*(volatile uint32_t *)(GPIOB_BASE + 0x0c)) +#define GPIOB_ODR (*(volatile uint32_t *)(GPIOB_BASE + 0x14)) +#define GPIOB_BSRR (*(volatile uint32_t *)(GPIOB_BASE + 0x18)) +#define GPIOB_AFL (*(volatile uint32_t *)(GPIOB_BASE + 0x20)) +#define GPIOB_AFH (*(volatile uint32_t *)(GPIOB_BASE + 0x24)) + + +void boot_led_on(void) +{ + uint32_t reg; + uint32_t pin = LED_BOOT_PIN; + RCC_AHB2_CLOCK_ER |= GPIOB_AHB2_CLOCK_ER; + reg = GPIOB_MODE & ~(0x03 << (pin * 2)); + GPIOB_MODE = reg | (1 << (pin * 2)); + reg = GPIOB_PUPD & ~(0x03 << (pin * 2)); + GPIOB_PUPD = reg | (1 << (pin * 2)); + GPIOB_BSRR |= (1 << pin); +} + +void boot_led_off(void) +{ + uint32_t reg; + uint32_t pin = LED_BOOT_PIN; + GPIOB_BSRR |= (1 << (pin + 16)); +} + + +#endif /* PLATFORM_stm32wb */ diff --git a/test-app-STM32F4-measured-boot/src/led.h b/test-app-STM32F4-measured-boot/src/led.h new file mode 100644 index 0000000..3c40e4d --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/led.h @@ -0,0 +1,35 @@ +/* led.h + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef GPIO_H_INCLUDED +#define GPIO_H_INCLUDED + +int led_setup(void); +void led_on(void); +void led_off(void); +void led_toggle(void); +void led_pwm_setup(void); +void boot_led_on(void); +void boot_led_off(void); + +#endif /* !GPIO_H_INCLUDED */ diff --git a/test-app-STM32F4-measured-boot/src/startup_arm.c b/test-app-STM32F4-measured-boot/src/startup_arm.c new file mode 100644 index 0000000..bb8bcd7 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/startup_arm.c @@ -0,0 +1,337 @@ +/* startup_arm.c + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +extern unsigned int _stored_data; +extern unsigned int _start_data; +extern unsigned int _end_data; +extern unsigned int _start_bss; +extern unsigned int _end_bss; +extern unsigned int _end_stack; +extern unsigned int _start_heap; + +static int zeroed_variable_in_bss; +static int initialized_variable_in_data = 42; + +extern void isr_tim2(void); + +#define STACK_PAINTING + +static volatile unsigned int avail_mem = 0; +static unsigned int sp; + +extern void main(void); + +void isr_reset(void) { + register unsigned int *src, *dst; + src = (unsigned int *) &_stored_data; + dst = (unsigned int *) &_start_data; + while (dst < (unsigned int *)&_end_data) { + *dst = *src; + dst++; + src++; + } + + dst = &_start_bss; + while (dst < (unsigned int *)&_end_bss) { + *dst = 0U; + dst++; + } + + avail_mem = &_end_stack - &_start_heap; +#ifdef STACK_PAINTING + { + asm volatile("mrs %0, msp" : "=r"(sp)); + dst = ((unsigned int *)(&_end_stack)) - (8192 / sizeof(unsigned int)); ; + while ((unsigned int)dst < sp) { + *dst = 0xDEADC0DE; + dst++; + } + } +#endif + + + main(); +} + +void isr_fault(void) +{ + /* Panic. */ + while(1) ;; + +} + +void isr_memfault(void) +{ + /* Panic. */ + while(1) ;; +} + +void isr_busfault(void) +{ + /* Panic. */ + while(1) ;; +} + +void isr_usagefault(void) +{ + /* Panic. */ + while(1) ;; +} + + +void isr_empty(void) +{ + +} + + + +__attribute__ ((section(".isr_vector"))) +void (* const IV[])(void) = +{ + (void (*)(void))(&_end_stack), + isr_reset, // Reset + isr_fault, // NMI + isr_fault, // HardFault + isr_memfault, // MemFault + isr_busfault, // BusFault + isr_usagefault, // UsageFault + 0, // SecureFault + 0, // reserved + 0, // reserved + 0, // reserved + isr_empty, // SVC + isr_empty, // DebugMonitor + 0, // reserved + isr_empty, // PendSV + isr_empty, // SysTick + +/* Device specific IRQs for LM3S */ + +#ifdef LM3S + isr_empty, // GPIO Port A + isr_empty, // GPIO Port B + isr_empty, // GPIO Port C + isr_empty, // GPIO Port D + isr_empty, // GPIO Port E + isr_empty, // UART0 Rx and Tx + isr_empty, // UART1 Rx and Tx + isr_empty, // SSI0 Rx and Tx + isr_empty, // I2C0 Master and Slave + isr_empty, // PWM Fault + isr_empty, // PWM Generator 0 + isr_empty, // PWM Generator 1 + isr_empty, // PWM Generator 2 + isr_empty, // Quadrature Encoder 0 + isr_empty, // ADC Sequence 0 + isr_empty, // ADC Sequence 1 + isr_empty, // ADC Sequence 2 + isr_empty, // ADC Sequence 3 + isr_empty, // Watchdog timer + isr_empty, // Timer 0 subtimer A + isr_empty, // Timer 0 subtimer B + isr_empty, // Timer 1 subtimer A + isr_empty, // Timer 1 subtimer B + isr_empty, // Timer 2 subtimer A + isr_empty, // Timer 3 subtimer B + isr_empty, // Analog Comparator 0 + isr_empty, // Analog Comparator 1 + isr_empty, // Analog Comparator 2 + isr_empty, // System Control (PLL, OSC, BO) + isr_empty, // FLASH Control + isr_empty, // GPIO Port F + isr_empty, // GPIO Port G + isr_empty, // GPIO Port H + isr_empty, // UART2 Rx and Tx + isr_empty, // SSI1 Rx and Tx + isr_empty, // Timer 3 subtimer A + isr_empty, // Timer 3 subtimer B + isr_empty, // I2C1 Master and Slave + isr_empty, // Quadrature Encoder 1 + isr_empty, // CAN0 + isr_empty, // CAN1 + isr_empty, // CAN2 + isr_empty, // Ethernet + isr_empty, // Hibernate +#elif PLATFORM_stm32l5 + isr_empty, // WWDG_IRQHandler + isr_empty, // PVD_PVM_IRQHandler + isr_empty, // RTC_IRQHandler + isr_empty, // RTC_S_IRQHandler + isr_empty, // TAMP_IRQHandler + isr_empty, // TAMP_S_IRQHandler + isr_empty, // FLASH_IRQHandler + isr_empty, // FLASH_S_IRQHandler + isr_empty, // GTZC_IRQHandler + isr_empty, // RCC_IRQHandler + isr_empty, // RCC_S_IRQHandler + isr_empty, // EXTI0_IRQHandler + isr_empty, // EXTI1_IRQHandler + isr_empty, // EXTI2_IRQHandler + isr_empty, // EXTI3_IRQHandler + isr_empty, // EXTI4_IRQHandler + isr_empty, // EXTI5_IRQHandler + isr_empty, // EXTI6_IRQHandler + isr_empty, // EXTI7_IRQHandler + isr_empty, // EXTI8_IRQHandler + isr_empty, // EXTI9_IRQHandler + isr_empty, // EXTI10_IRQHandler + isr_empty, // EXTI11_IRQHandler + isr_empty, // EXTI12_IRQHandler + isr_empty, // EXTI13_IRQHandler + isr_empty, // EXTI14_IRQHandler + isr_empty, // EXTI15_IRQHandler + isr_empty, // DMAMUX1_IRQHandler + isr_empty, // DMAMUX1_S_IRQHandler + isr_empty, // DMA1_Channel1_IRQHandler + isr_empty, // DMA1_Channel2_IRQHandler + isr_empty, // DMA1_Channel3_IRQHandler + isr_empty, // DMA1_Channel4_IRQHandler + isr_empty, // DMA1_Channel5_IRQHandler + isr_empty, // DMA1_Channel6_IRQHandler + isr_empty, // DMA1_Channel7_IRQHandler + isr_empty, // DMA1_Channel8_IRQHandler + isr_empty, // ADC1_2_IRQHandler + isr_empty, // DAC_IRQHandler + isr_empty, // FDCAN1_IT0_IRQHandler + isr_empty, // FDCAN1_IT1_IRQHandler + isr_empty, // TIM1_BRK_IRQHandler + isr_empty, // TIM1_UP_IRQHandler + isr_empty, // TIM1_TRG_COM_IRQHandler + isr_empty, // TIM1_CC_IRQHandler + isr_empty, // TIM2_IRQHandler + isr_empty, // TIM3_IRQHandler + isr_empty, // TIM4_IRQHandler + isr_empty, // TIM5_IRQHandler + isr_empty, // TIM6_IRQHandler + isr_empty, // TIM7_IRQHandler + isr_empty, // TIM8_BRK_IRQHandler + isr_empty, // TIM8_UP_IRQHandler + isr_empty, // TIM8_TRG_COM_IRQHandler + isr_empty, // TIM8_CC_IRQHandler + isr_empty, // I2C1_EV_IRQHandler + isr_empty, // I2C1_ER_IRQHandler + isr_empty, // I2C2_EV_IRQHandler + isr_empty, // I2C2_ER_IRQHandler + isr_empty, // SPI1_IRQHandler + isr_empty, // SPI2_IRQHandler + isr_empty, // USART1_IRQHandler + isr_empty, // USART2_IRQHandler + isr_empty, // USART3_IRQHandler + isr_empty, // UART4_IRQHandler + isr_empty, // UART5_IRQHandler + isr_empty, // LPUART1_IRQHandler + isr_empty, // LPTIM1_IRQHandler + isr_empty, // LPTIM2_IRQHandler + isr_empty, // TIM15_IRQHandler + isr_empty, // TIM16_IRQHandler + isr_empty, // TIM17_IRQHandler + isr_empty, // COMP_IRQHandler + isr_empty, // USB_FS_IRQHandler + isr_empty, // CRS_IRQHandler + isr_empty, // FMC_IRQHandler + isr_empty, // OCTOSPI1_IRQHandler + isr_empty, // 0 + isr_empty, // SDMMC1_IRQHandler + isr_empty, // 0 + isr_empty, // DMA2_Channel1_IRQHandler + isr_empty, // DMA2_Channel2_IRQHandler + isr_empty, // DMA2_Channel3_IRQHandler + isr_empty, // DMA2_Channel4_IRQHandler + isr_empty, // DMA2_Channel5_IRQHandler + isr_empty, // DMA2_Channel6_IRQHandler + isr_empty, // DMA2_Channel7_IRQHandler + isr_empty, // DMA2_Channel8_IRQHandler + isr_empty, // I2C3_EV_IRQHandler + isr_empty, // I2C3_ER_IRQHandler + isr_empty, // SAI1_IRQHandler + isr_empty, // SAI2_IRQHandler + isr_empty, // TSC_IRQHandler + isr_empty, // AES_IRQHandler + isr_empty, // RNG_IRQHandler + isr_empty, // FPU_IRQHandler + isr_empty, // HASH_IRQHandler + isr_empty, // PKA_IRQHandler + isr_empty, // LPTIM3_IRQHandler + isr_empty, // SPI3_IRQHandler + isr_empty, // I2C4_ER_IRQHandler + isr_empty, // I2C4_EV_IRQHandler + isr_empty, // DFSDM1_FLT0_IRQHandler + isr_empty, // DFSDM1_FLT1_IRQHandler + isr_empty, // DFSDM1_FLT2_IRQHandler + isr_empty, // DFSDM1_FLT3_IRQHandler + isr_empty, // UCPD1_IRQHandler + isr_empty, // ICACHE_IRQHandler + isr_empty, // OTFDEC1_IRQHandler +#else /* For STM32F4 */ + isr_empty, // NVIC_WWDG_IRQ 0 + isr_empty, // PVD_IRQ 1 + isr_empty, // TAMP_STAMP_IRQ 2 + isr_empty, // RTC_WKUP_IRQ 3 + isr_empty, // FLASH_IRQ 4 + isr_empty, // RCC_IRQ 5 + isr_empty, // EXTI0_IRQ 6 + isr_empty, // EXTI1_IRQ 7 + isr_empty, // EXTI2_IRQ 8 + isr_empty, // EXTI3_IRQ 9 + isr_empty, // EXTI4_IRQ 10 + isr_empty, // DMA1_STREAM0_IRQ 11 + isr_empty, // DMA1_STREAM1_IRQ 12 + isr_empty, // DMA1_STREAM2_IRQ 13 + isr_empty, // DMA1_STREAM3_IRQ 14 + isr_empty, // DMA1_STREAM4_IRQ 15 + isr_empty, // DMA1_STREAM5_IRQ 16 + isr_empty, // DMA1_STREAM6_IRQ 17 + isr_empty, // ADC_IRQ 18 + isr_empty, // CAN1_TX_IRQ 19 + isr_empty, // CAN1_RX0_IRQ 20 + isr_empty, // CAN1_RX1_IRQ 21 + isr_empty, // CAN1_SCE_IRQ 22 + isr_empty, // EXTI9_5_IRQ 23 + isr_empty, // TIM1_BRK_TIM9_IRQ 24 + isr_empty, // TIM1_UP_TIM10_IRQ 25 + isr_empty, // TIM1_TRG_COM_TIM11_IRQ 26 + isr_empty, // TIM1_CC_IRQ 27 + isr_tim2, // TIM2_IRQ 28 + isr_empty, // TIM3_IRQ 29 + isr_empty, // TIM4_IRQ 30 + isr_empty, // I2C1_EV_IRQ 31 + isr_empty, // I2C1_ER_IRQ 32 + isr_empty, // I2C2_EV_IRQ 33 + isr_empty, // I2C2_ER_IRQ 34 + isr_empty, // SPI1_IRQ 35 + isr_empty, // SPI2_IRQ 36 + isr_empty, // USART1_IRQ 37 + isr_empty, // USART2_IRQ 38 + isr_empty, // USART3_IRQ 39 + isr_empty, // EXTI15_10_IRQ 40 + isr_empty, // RTC_ALARM_IRQ 41 + isr_empty, // USB_FS_WKUP_IRQ 42 + isr_empty, // TIM8_BRK_TIM12_IRQ 43 + isr_empty, // TIM8_UP_TIM13_IRQ 44 + isr_empty, // TIM8_TRG_COM_TIM14_IRQ 45 + isr_empty, // TIM8_CC_IRQ 46 + isr_empty, // DMA1_STREAM7_IRQ 47 + +#endif +}; diff --git a/test-app-STM32F4-measured-boot/src/system.c b/test-app-STM32F4-measured-boot/src/system.c new file mode 100644 index 0000000..01d6c57 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/system.c @@ -0,0 +1,147 @@ +/* system.c + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#if defined(PLATFORM_stm32f4) || defined(PLATFORM_stm32f7) +#include +#include "system.h" + +/*** FLASH ***/ +#define FLASH_BASE (0x40023C00) +#define FLASH_ACR (*(volatile uint32_t *)(FLASH_BASE + 0x00)) +#define FLASH_ACR_ENABLE_DATA_CACHE (1 << 10) +#define FLASH_ACR_ENABLE_INST_CACHE (1 << 9) + +/*** RCC ***/ + +#define RCC_BASE (0x40023800) +#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00)) +#define RCC_PLLCFGR (*(volatile uint32_t *)(RCC_BASE + 0x04)) +#define RCC_CFGR (*(volatile uint32_t *)(RCC_BASE + 0x08)) +#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00)) + +#define RCC_CR_PLLRDY (1 << 25) +#define RCC_CR_PLLON (1 << 24) +#define RCC_CR_HSERDY (1 << 17) +#define RCC_CR_HSEON (1 << 16) +#define RCC_CR_HSIRDY (1 << 1) +#define RCC_CR_HSION (1 << 0) + +#define RCC_CFGR_SW_HSI 0x0 +#define RCC_CFGR_SW_HSE 0x1 +#define RCC_CFGR_SW_PLL 0x2 + + +#define RCC_PLLCFGR_PLLSRC (1 << 22) + +#define RCC_PRESCALER_DIV_NONE 0 +#define RCC_PRESCALER_DIV_2 8 +#define RCC_PRESCALER_DIV_4 9 + + +/* STM32F4-Discovery, 168 MHz */ +#ifdef PLATFORM_stm32f4 +# define PLLM 8 +# define PLLN 336 +# define PLLP 2 +# define PLLQ 7 +# define PLLR 0 +# define TARGET_FLASH_WAITSTATES 5 +#endif + +/* STM32F7-Discovery, 216 MHz */ +#ifdef PLATFORM_stm32f7 +# define PLLM 25 +# define PLLN 432 +# define PLLP 2 +# define PLLQ 9 +# define PLLR 0 +# define TARGET_FLASH_WAITSTATES 7 +#endif + +void flash_set_waitstates(void) +{ + FLASH_ACR |= TARGET_FLASH_WAITSTATES | FLASH_ACR_ENABLE_DATA_CACHE | FLASH_ACR_ENABLE_INST_CACHE; +} + + +void clock_config(void) +{ + uint32_t reg32; + /* Enable internal high-speed oscillator. */ + RCC_CR |= RCC_CR_HSION; + DMB(); + while ((RCC_CR & RCC_CR_HSIRDY) == 0) {}; + + /* Select HSI as SYSCLK source. */ + + reg32 = RCC_CFGR; + reg32 &= ~((1 << 1) | (1 << 0)); + RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI); + DMB(); + + /* Enable external high-speed oscillator 8MHz. */ + RCC_CR |= RCC_CR_HSEON; + DMB(); + while ((RCC_CR & RCC_CR_HSERDY) == 0) {}; + + /* + * Set prescalers for AHB, ADC, ABP1, ABP2. + */ + reg32 = RCC_CFGR; + reg32 &= ~(0xF0); + RCC_CFGR = (reg32 | (RCC_PRESCALER_DIV_NONE << 4)); + DMB(); + reg32 = RCC_CFGR; + reg32 &= ~(0x1C00); + RCC_CFGR = (reg32 | (RCC_PRESCALER_DIV_2 << 10)); + DMB(); + reg32 = RCC_CFGR; + reg32 &= ~(0x07 << 13); + RCC_CFGR = (reg32 | (RCC_PRESCALER_DIV_4 << 13)); + DMB(); + + /* Set PLL config */ + reg32 = RCC_PLLCFGR; + reg32 &= ~(PLL_FULL_MASK); + RCC_PLLCFGR = reg32 | RCC_PLLCFGR_PLLSRC | PLLM | + (PLLN << 6) | (((PLLP >> 1) - 1) << 16) | + (PLLQ << 24); + DMB(); + /* Enable PLL oscillator and wait for it to stabilize. */ + RCC_CR |= RCC_CR_PLLON; + DMB(); + while ((RCC_CR & RCC_CR_PLLRDY) == 0) {}; + + /* Select PLL as SYSCLK source. */ + reg32 = RCC_CFGR; + reg32 &= ~((1 << 1) | (1 << 0)); + RCC_CFGR = (reg32 | RCC_CFGR_SW_PLL); + DMB(); + + /* Wait for PLL clock to be selected. */ + while ((RCC_CFGR & ((1 << 1) | (1 << 0))) != RCC_CFGR_SW_PLL) {}; + + /* Disable internal high-speed oscillator. */ + RCC_CR &= ~RCC_CR_HSION; +} + +#endif /* PLATFORM_stm32f4 */ diff --git a/test-app-STM32F4-measured-boot/src/system.h b/test-app-STM32F4-measured-boot/src/system.h new file mode 100644 index 0000000..e8089c0 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/system.h @@ -0,0 +1,67 @@ +/* system.h + * + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef SYSTEM_H_INCLUDED +#define SYSTEM_H_INCLUDED + +/* System specific: PLL with 8 MHz external oscillator, CPU at 168MHz */ +#define CPU_FREQ (168000000) +#define PLL_FULL_MASK (0x7F037FFF) + +/* Assembly helpers */ +#define DMB() asm volatile ("dmb"); +#define WFI() asm volatile ("wfi"); + +/* Master clock setting */ +void clock_config(void); +void flash_set_waitstates(void); + + +/* NVIC */ +/* NVIC ISER Base register (Cortex-M) */ + +#define NVIC_TIM2_IRQN (28) +#define NVIC_ISER_BASE (0xE000E100) +#define NVIC_ICER_BASE (0xE000E180) +#define NVIC_IPRI_BASE (0xE000E400) + +static inline void nvic_irq_enable(uint8_t n) +{ + int i = n / 32; + volatile uint32_t *nvic_iser = ((volatile uint32_t *)(NVIC_ISER_BASE + 4 * i)); + *nvic_iser |= (1 << (n % 32)); +} + +static inline void nvic_irq_disable(uint8_t n) +{ + int i = n / 32; + volatile uint32_t *nvic_icer = ((volatile uint32_t *)(NVIC_ICER_BASE + 4 * i)); + *nvic_icer |= (1 << (n % 32)); +} + +static inline void nvic_irq_setprio(uint8_t n, uint8_t prio) +{ + volatile uint8_t *nvic_ipri = ((volatile uint8_t *)(NVIC_IPRI_BASE + n)); + *nvic_ipri = prio; +} + +#endif /* !SYSTEM_H_INCLUDED */ diff --git a/test-app-STM32F4-measured-boot/src/target-app.ld b/test-app-STM32F4-measured-boot/src/target-app.ld new file mode 100644 index 0000000..1047b51 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/target-app.ld @@ -0,0 +1,45 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x20100, LENGTH = 0x1FF00 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K /* Run in lowmem */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.init) + *(.fini) + *(.text*) + *(.rodata*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss : + { + _start_bss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + _end = .; + } > RAM +} + +PROVIDE(_start_heap = _end); +PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); diff --git a/test-app-STM32F4-measured-boot/src/timer.c b/test-app-STM32F4-measured-boot/src/timer.c new file mode 100644 index 0000000..0b73408 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/timer.c @@ -0,0 +1,171 @@ +/* timer.c + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifdef PLATFORM_stm32f4 +#include + +#include "system.h" +#include "led.h" + + +/* STM32 specific defines */ +#define APB1_CLOCK_ER (*(volatile uint32_t *)(0x40023840)) +#define APB1_CLOCK_RST (*(volatile uint32_t *)(0x40023820)) +#define TIM4_APB1_CLOCK_ER_VAL (1 << 2) +#define TIM2_APB1_CLOCK_ER_VAL (1 << 0) + +#define TIM2_BASE (0x40000000) +#define TIM2_CR1 (*(volatile uint32_t *)(TIM2_BASE + 0x00)) +#define TIM2_DIER (*(volatile uint32_t *)(TIM2_BASE + 0x0c)) +#define TIM2_SR (*(volatile uint32_t *)(TIM2_BASE + 0x10)) +#define TIM2_PSC (*(volatile uint32_t *)(TIM2_BASE + 0x28)) +#define TIM2_ARR (*(volatile uint32_t *)(TIM2_BASE + 0x2c)) + +#define TIM4_BASE (0x40000800) +#define TIM4_CR1 (*(volatile uint32_t *)(TIM4_BASE + 0x00)) +#define TIM4_DIER (*(volatile uint32_t *)(TIM4_BASE + 0x0c)) +#define TIM4_SR (*(volatile uint32_t *)(TIM4_BASE + 0x10)) +#define TIM4_CCMR1 (*(volatile uint32_t *)(TIM4_BASE + 0x18)) +#define TIM4_CCMR2 (*(volatile uint32_t *)(TIM4_BASE + 0x1c)) +#define TIM4_CCER (*(volatile uint32_t *)(TIM4_BASE + 0x20)) +#define TIM4_PSC (*(volatile uint32_t *)(TIM4_BASE + 0x28)) +#define TIM4_ARR (*(volatile uint32_t *)(TIM4_BASE + 0x2c)) +#define TIM4_CCR4 (*(volatile uint32_t *)(TIM4_BASE + 0x40)) + +#define TIM_DIER_UIE (1 << 0) +#define TIM_SR_UIF (1 << 0) +#define TIM_CR1_CLOCK_ENABLE (1 << 0) +#define TIM_CR1_UPD_RS (1 << 2) +#define TIM_CR1_ARPE (1 << 7) + +#define TIM_CCER_CC4_ENABLE (1 << 12) +#define TIM_CCMR1_OC1M_PWM1 (0x06 << 4) +#define TIM_CCMR2_OC4M_PWM1 (0x06 << 12) + +#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830)) +#define GPIOD_AHB1_CLOCK_ER (1 << 3) + +#define GPIOD_BASE 0x40020c00 +#define GPIOD_MODE (*(volatile uint32_t *)(GPIOD_BASE + 0x00)) +#define GPIOD_OTYPE (*(volatile uint32_t *)(GPIOD_BASE + 0x04)) +#define GPIOD_PUPD (*(volatile uint32_t *)(GPIOD_BASE + 0x0c)) +#define GPIOD_ODR (*(volatile uint32_t *)(GPIOD_BASE + 0x14)) + +static uint32_t master_clock = 0; + +/** Use TIM4_CH4, which is linked to PD15 AF1 **/ +int pwm_init(uint32_t clock, uint32_t threshold) +{ + uint32_t val = (clock / 100000); /* Frequency is 100 KHz */ + uint32_t lvl; + master_clock = clock; + + if (threshold > 100) + return -1; + + lvl = (val * threshold) / 100; + if (lvl != 0) + lvl--; + + APB1_CLOCK_RST |= TIM4_APB1_CLOCK_ER_VAL; + asm volatile ("dmb"); + APB1_CLOCK_RST &= ~TIM4_APB1_CLOCK_ER_VAL; + APB1_CLOCK_ER |= TIM4_APB1_CLOCK_ER_VAL; + + /* disable CC */ + TIM4_CCER &= ~TIM_CCER_CC4_ENABLE; + TIM4_CR1 = 0; + TIM4_PSC = 0; + TIM4_ARR = val - 1; + TIM4_CCR4 = lvl; + TIM4_CCMR1 &= ~(0x03 << 0); + TIM4_CCMR1 &= ~(0x07 << 4); + TIM4_CCMR1 |= TIM_CCMR1_OC1M_PWM1; + TIM4_CCMR2 &= ~(0x03 << 8); + TIM4_CCMR2 &= ~(0x07 << 12); + TIM4_CCMR2 |= TIM_CCMR2_OC4M_PWM1; + TIM4_CCER |= TIM_CCER_CC4_ENABLE; + TIM4_CR1 |= TIM_CR1_CLOCK_ENABLE | TIM_CR1_ARPE; + asm volatile ("dmb"); + return 0; +} + +int timer_init(uint32_t clock, uint32_t prescaler, uint32_t interval_ms) +{ + uint32_t val = 0; + uint32_t psc = 1; + uint32_t err = 0; + clock = ((clock * prescaler) / 1000) * interval_ms; + + while (psc < 65535) { + val = clock / psc; + err = clock % psc; + if ((val < 65535) && (err == 0)) { + val--; + break; + } + val = 0; + psc++; + } + if (val == 0) + return -1; + + nvic_irq_enable(NVIC_TIM2_IRQN); + nvic_irq_setprio(NVIC_TIM2_IRQN, 0); + APB1_CLOCK_RST |= TIM2_APB1_CLOCK_ER_VAL; + asm volatile ("dmb"); + APB1_CLOCK_RST &= ~TIM2_APB1_CLOCK_ER_VAL; + APB1_CLOCK_ER |= TIM2_APB1_CLOCK_ER_VAL; + + TIM2_CR1 = 0; + asm volatile ("dmb"); + TIM2_PSC = psc; + TIM2_ARR = val; + TIM2_CR1 |= TIM_CR1_CLOCK_ENABLE; + TIM2_DIER |= TIM_DIER_UIE; + asm volatile ("dmb"); + return 0; +} + +extern volatile uint32_t time_elapsed; +void isr_tim2(void) +{ + static volatile uint32_t tim2_ticks = 0; + TIM2_SR &= ~TIM_SR_UIF; + + /* Dim the led by altering the PWM duty-cicle */ + if (++tim2_ticks > 15) + tim2_ticks = 0; + if (tim2_ticks > 8) + pwm_init(master_clock, 10 * (16 - tim2_ticks)); + else + pwm_init(master_clock, 10 * tim2_ticks); + + time_elapsed++; +} +#else +void isr_tim2(void) +{ +} + +#endif /* PLATFORM_stm32f4 */ diff --git a/test-app-STM32F4-measured-boot/src/timer.h b/test-app-STM32F4-measured-boot/src/timer.h new file mode 100644 index 0000000..bd66dae --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/timer.h @@ -0,0 +1,30 @@ +/* timer.h + * + * Test bare-metal blinking led application + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef TIMER_H_INCLUDED +#define TIMER_H_INCLUDED + +int pwm_init(uint32_t clock, uint32_t threshold); +int timer_init(uint32_t clock, uint32_t scaler, uint32_t interval); + +#endif /* !TIMER_H_INCLUDED */ From 05754344259f9b365962a742e3c5b6d84eca917f Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Thu, 14 Jan 2021 17:02:27 +0200 Subject: [PATCH 05/11] Updated wolfBoot submodule to latest commit in master This also updates the wolfTPM version --- wolfBoot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfBoot b/wolfBoot index 19f328c..3516620 160000 --- a/wolfBoot +++ b/wolfBoot @@ -1 +1 @@ -Subproject commit 19f328cffdd1fe65b9ed764d4812adada3c819cf +Subproject commit 3516620f1a4df029e18734ef5cd9ee22813174e7 From 3187576b181e3b8851fa009679d89bd82f296c8b Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Fri, 22 Jan 2021 15:25:52 +0200 Subject: [PATCH 06/11] TPM used from test app and made the example comptabile with latest wolfTPM * Add the correct TPM pinout and re-use the STM32F4 SPI driver from wolfboot * Add read out of TPM PCR and print in hex Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/Makefile | 29 ++++- .../src/app_stm32f4.c | 116 +++++++++++++++++- test-app-STM32F4-measured-boot/src/spi_tpm.h | 36 ++++++ 3 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 test-app-STM32F4-measured-boot/src/spi_tpm.h diff --git a/test-app-STM32F4-measured-boot/Makefile b/test-app-STM32F4-measured-boot/Makefile index c18382b..ce9a7b4 100644 --- a/test-app-STM32F4-measured-boot/Makefile +++ b/test-app-STM32F4-measured-boot/Makefile @@ -14,14 +14,18 @@ LC_ALL= APPSRC:=./src WOLFBOOT:=../wolfBoot WOLFSSL_ROOT:=../wolfBoot/lib/wolfssl -WOLFSSL_BUILD:=./build/lib +WOLFTPM_ROOT:=../wolfBoot/lib/wolfTPM ECCKEY:=$(WOLFBOOT)/ecc256.der DEBUG?=1 include $(WOLFBOOT)/tools/config.mk -CFLAGS:=-g -ggdb -Wall -Wstack-usage=1024 -ffreestanding -Wno-unused -DPLATFORM_$(TARGET) -I$(WOLFBOOT)/include -I$(WOLFSSL_ROOT)/wolfssl -nostartfiles +CFLAGS:=-g -ggdb -Wall -Wstack-usage=1024 -ffreestanding -Wno-unused -DPLATFORM_$(TARGET) \ + -I$(WOLFBOOT)/include -I$(WOLFBOOT) -I$(WOLFSSL_ROOT) -I$(WOLFTPM_ROOT) \ + -DWOLFBOOT_MEASURED_PCR_A -nostartfiles CFLAGS+=-DWOLFBOOT_HASH_SHA256 +CFLAGS+=-DWOLFSSL_USER_SETTINGS +CFLAGS+=-DWOLFTPM_USER_SETTINGS APP_OBJS:= \ $(APPSRC)/app_$(TARGET).o \ @@ -30,8 +34,23 @@ APP_OBJS:= \ $(APPSRC)/timer.o \ $(WOLFBOOT)/hal/$(TARGET).o \ $(WOLFBOOT)/src/libwolfboot.o \ + $(WOLFBOOT)/hal/spi/spi_drv_stm32.o \ $(APPSRC)/startup_arm.o +# Add objects for wolfCrypt support required by wolfTPM +APP_OBJS+= \ + $(WOLFSSL_ROOT)/wolfcrypt/src/hmac.o \ + $(WOLFSSL_ROOT)/wolfcrypt/src/aes.o \ + $(WOLFSSL_ROOT)/wolfcrypt/src/wc_port.o + +# Add objects for wolfTPM support +APP_OBJS+= \ + $(WOLFTPM_ROOT)/src/tpm2.o \ + $(WOLFTPM_ROOT)/src/tpm2_packet.o \ + $(WOLFTPM_ROOT)/src/tpm2_tis.o \ + $(WOLFTPM_ROOT)/src/tpm2_wrap.o \ + $(WOLFTPM_ROOT)/src/tpm2_param_enc.o + # Inherit cross-compiler and similar settings from wolfBoot include ../wolfBoot/arch.mk @@ -43,13 +62,15 @@ endif vpath %.c $(dir $(WOLFSSL_ROOT)/src) vpath %.c $(dir $(WOLFSSL_ROOT)/wolfcrypt/src) +vpath %.c $(dir $(WOLFBOOT))/lib/wolfTPM/wolftpm) ENTRY_POINT=`cat .entry-point-address` LSCRIPT:=$(APPSRC)/target-app.ld LSCRIPT_TEMPLATE:=$(APPSRC)/$(ARCH).ld LDFLAGS:=$(CFLAGS) -T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map -wolfboot-example: image.bin wolfboot_align.bin + +wolfboot-example: wolfboot_align.bin image.bin python3 $(WOLFBOOT)/tools/keytools/sign.py --ecc256 image.bin $(ECCKEY) 1 cat wolfboot-align.bin image_v1_signed.bin >factory.bin @@ -60,7 +81,7 @@ wolfboot_align.bin: wolfboot_target cp $(WOLFBOOT)/wolfboot-align.bin . cp $(WOLFBOOT)/wolfboot.elf . -image.bin: image.elf wolfboot_target +image.bin: wolfboot_target image.elf $(OBJCOPY) -O binary image.elf $@ $(SIZE) image.elf diff --git a/test-app-STM32F4-measured-boot/src/app_stm32f4.c b/test-app-STM32F4-measured-boot/src/app_stm32f4.c index 6b4fea2..71fbd18 100644 --- a/test-app-STM32F4-measured-boot/src/app_stm32f4.c +++ b/test-app-STM32F4-measured-boot/src/app_stm32f4.c @@ -30,8 +30,12 @@ #include "hal.h" #include "wolfboot/wolfboot.h" #include "spi_flash.h" +#include "spi_drv.h" +#include "spi_tpm.h" -#ifdef PLATFORM_stm32f4 +#include "wolftpm/tpm2.h" +#include "wolftpm/tpm2_wrap.h" +static WOLFTPM2_DEV wolftpm_dev; #define UART1 (0x40011000) @@ -59,7 +63,6 @@ #define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830)) #define GPIOB_AHB1_CLOCK_ER (1 << 1) -#define GPIOB_BASE 0x40020400 #define GPIOB_MODE (*(volatile uint32_t *)(GPIOB_BASE + 0x00)) #define GPIOB_AFL (*(volatile uint32_t *)(GPIOB_BASE + 0x20)) @@ -76,8 +79,10 @@ static const char START='*'; static const char UPDATE='U'; static const char ACK='#'; static uint8_t msg[MSGSIZE]; - - +static const char startString[]="App started"; +static const char TPMfailString[]="tpm_init failed"; +static const char TPMpcrString[]="Measured Boot PCR is = "; +static const char HEX [16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; void uart_write(const char c) { @@ -88,6 +93,12 @@ void uart_write(const char c) UART1_DR = c; } +void uart_write_hex(const char c) +{ + uart_write(HEX[(c >> 4) & 0x0F]); + uart_write(HEX[c & 0x0F]); +} + static void uart_pins_setup(void) { uint32_t reg; @@ -183,7 +194,77 @@ static int check(uint8_t *pkt, int size) return -1; } +static int app_tpm2_IoCb(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, + word16 xferSz, void* userCtx) +{ + (void)userCtx; + (void)ctx; + word16 i; + + spi_cs_on(SPI_CS_TPM); + + memset(rxBuf, 0, xferSz); + for (i = 0; i < xferSz; i++) + { + spi_write(txBuf[i]); + rxBuf[i] = spi_read(); + } + spi_cs_off(SPI_CS_TPM); + + /* + printf("\r\nSPI TX: "); + printbin(txBuf, xferSz); + printf("SPI RX: "); + printbin(rxBuf, xferSz); + printf("\r\n"); + */ + + return 0; +} + +static int app_tpm2_init(void) +{ + int rc; + WOLFTPM2_CAPS caps; + + spi_init(0,0); + + /* Init the TPM2 device */ + rc = wolfTPM2_Init(&wolftpm_dev, app_tpm2_IoCb, NULL); + if (rc != 0) { + return rc; + } + + /* Get device capabilities + options */ + rc = wolfTPM2_GetCapabilities(&wolftpm_dev, &caps); + if (rc != 0) { + return rc; + } + + return 0; +} + +/* Reads out the TPM measurement created by wolfBoot */ +static int read_measured_boot(uint8_t* digest) +{ + int rc; + PCR_Read_In pcrReadCmd; + PCR_Read_Out pcrReadResp; + + XMEMSET(&pcrReadCmd, 0, sizeof(pcrReadCmd)); + TPM2_SetupPCRSel(&pcrReadCmd.pcrSelectionIn, TPM_ALG_SHA256, WOLFBOOT_MEASURED_PCR_A); + rc = TPM2_PCR_Read(&pcrReadCmd, &pcrReadResp); + if (rc == TPM_RC_SUCCESS) { + XMEMCPY(digest, pcrReadResp.pcrValues.digests[0].buffer, + pcrReadResp.pcrValues.digests[0].size); + rc = 0; + } + + return rc; +} + volatile uint32_t time_elapsed = 0; +volatile uint32_t testme = 1; void main(void) { uint32_t tlen = 0; volatile uint32_t recv_seq; @@ -192,6 +273,7 @@ void main(void) { uint32_t next_seq = 0; uint32_t version = 0; uint8_t *v_array = (uint8_t *)&version; + uint8_t boot_measurement[WOLFBOOT_SHA_DIGEST_SIZE]; int i; memset(page, 0xFF, PAGESIZE); boot_led_on(); @@ -223,10 +305,35 @@ void main(void) { #ifdef EXT_ENCRYPTED wolfBoot_set_encrypt_key("0123456789abcdef0123456789abcdef", 32); #endif + + for(i=0; i < sizeof(startString); i++) { + uart_write(startString[i++]); + } + uart_write(START); for (i = 3; i >= 0; i--) { uart_write(v_array[i]); } + + if(app_tpm2_init() != 0) { + for(i=0; i < sizeof(TPMfailString); i++) { + uart_write(TPMfailString[i]); + } + } + + if(read_measured_boot(boot_measurement) == 0) { + for(i = 0; i < sizeof(TPMpcrString); i++) { + uart_write(TPMpcrString[i]); + } + /* Print the digest of the measurement */ + for(i=0; i < sizeof(boot_measurement); i++) { + uart_write_hex(boot_measurement[i]); + } + /* For better view on the UART terminal */ + uart_write('\n'); + uart_write('\r'); + } + while (1) { r_total = 0; do { @@ -294,5 +401,4 @@ void main(void) { while(1) ; } -#endif /** PLATFORM_stm32f4 **/ diff --git a/test-app-STM32F4-measured-boot/src/spi_tpm.h b/test-app-STM32F4-measured-boot/src/spi_tpm.h new file mode 100644 index 0000000..1c01ca7 --- /dev/null +++ b/test-app-STM32F4-measured-boot/src/spi_tpm.h @@ -0,0 +1,36 @@ +/* tpm_spi.h + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/** SPI settings for TPM2.0 module **/ + +#undef SPI_GPIO +#define SPI_GPIO GPIOB_BASE + +#undef SPI_CS_GPIO +#define SPI_CS_GPIO GPIOE_BASE + +#undef SPI_CS_TPM +#define SPI_CS_TPM 0 /* TPM CS connected to GPIOE0 */ + +#undef SPI1_CLOCK_PIN +#define SPI1_CLOCK_PIN 3 /* SPI_SCK: PB3 */ + +#undef SPI1_MISO_PIN +#define SPI1_MISO_PIN 4 /* SPI_MISO PB4 */ + +#undef SPI1_MOSI_PIN +#define SPI1_MOSI_PIN 5 /* SPI_MOSI PB5 */ From c9b4906f0cc26b14b03bf6a6b018dc34f1d4a419 Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Wed, 27 Jan 2021 19:34:21 +0200 Subject: [PATCH 07/11] Added openocd config for STM32F407-Discovery revision D, uses ST-LinkV2.1 Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/openocd.cfg | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test-app-STM32F4-measured-boot/openocd.cfg diff --git a/test-app-STM32F4-measured-boot/openocd.cfg b/test-app-STM32F4-measured-boot/openocd.cfg new file mode 100644 index 0000000..65b5d8a --- /dev/null +++ b/test-app-STM32F4-measured-boot/openocd.cfg @@ -0,0 +1,8 @@ +source [find interface/stlink-v2-1.cfg] +source [find target/stm32f4x.cfg] +$_TARGETNAME configure -event reset-init { + mmw 0xe0042004 0x7 0x0 +} +init +reset +halt From ab088d61c43ac4f082c344858307e2474d373a2c Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Wed, 27 Jan 2021 19:26:48 +0200 Subject: [PATCH 08/11] Added usage and debug information in the README file Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/README.md | 100 +++++++++++++++++++++-- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/test-app-STM32F4-measured-boot/README.md b/test-app-STM32F4-measured-boot/README.md index 11b12ff..06ff47e 100644 --- a/test-app-STM32F4-measured-boot/README.md +++ b/test-app-STM32F4-measured-boot/README.md @@ -1,4 +1,4 @@ -# STM32F4-Discovery-Measured-Boot +# STM32F407-Discovery-Measured-Boot Measured Boot example running on STM32F407-Discovery board. @@ -78,25 +78,109 @@ Enter the project folder for this example: `wolfboot-examples/wolfBoot$ cd ../test-app-STM32F4-measured-boot` -Makefile in the project folder takes care of compiling wolfBoot, compiling the test application and signing the firmware: +Makefile in the project folder takes care of compiling wolfTPM, wolfBoot, compiling the test application and signing the firmware: `wolfboot-examples/wolfBoot$ make` -After a successful operation the following files will be available in the project folder: +The following files will be available in the project folder after successful build: - factory.bin - Test-app and wolfboot combined, ready for flashing +- wolfboot.elf - WolfBoot with Debug information, useful for gdb - image.bin - Test-app without signature +- image.elf - Test-app with Debug information, useful for gdb - image_v1_signed.bin - Test-app with signature - image.map - Listing of the Test-app -## Copyright notice -This example is Copyright (c) 2020 wolfSSL Inc., and distributed under the term of GNU GPL2. +## Usage -Some STM/STMicroelectronics specific drivers used in this example are Copyright of ST Microelectronics. Distributed freely as specified by BSD-3-Clause License. +After successful compilation, the `flash` target can be used to flash the `factory.bin` image to the STM32F4-Discovery board using ST-Link2 + +`make flash` + +Typical output: + +``` +test-app-STM32F4-measured-boot % make flash +st-flash write factory.bin 0x08000000 +st-flash 1.6.1 +2021-01-27T19:24:01 INFO common.c: F4xx: 192 KiB SRAM, 1024 KiB flash in at least 16 KiB pages. +file factory.bin md5 checksum: 7cc2a98a3e5366ecc54bbb79a646286, stlink checksum: 0x01c745fb +2021-01-27T19:24:01 INFO common.c: Attempting to write 143576 (0x230d8) bytes to stm32 address: 134217728 (0x8000000) +EraseFlash - Sector:0x0 Size:0x4000 2021-01-27T19:24:01 INFO common.c: Flash page at addr: 0x08000000 erased +EraseFlash - Sector:0x1 Size:0x4000 2021-01-27T19:24:02 INFO common.c: Flash page at addr: 0x08004000 erased +EraseFlash - Sector:0x2 Size:0x4000 2021-01-27T19:24:02 INFO common.c: Flash page at addr: 0x08008000 erased +EraseFlash - Sector:0x3 Size:0x4000 2021-01-27T19:24:02 INFO common.c: Flash page at addr: 0x0800c000 erased +EraseFlash - Sector:0x4 Size:0x10000 2021-01-27T19:24:03 INFO common.c: Flash page at addr: 0x08010000 erased +EraseFlash - Sector:0x5 Size:0x20000 2021-01-27T19:24:05 INFO common.c: Flash page at addr: 0x08020000 erased +2021-01-27T19:24:05 INFO common.c: Finished erasing 6 pages of 131072 (0x20000) bytes +2021-01-27T19:24:05 INFO common.c: Starting Flash write for F2/F4/L4 +2021-01-27T19:24:05 INFO flash_loader.c: Successfully loaded flash loader in sram +enabling 32-bit flash writes +size: 32768 +size: 32768 +size: 32768 +size: 32768 +size: 12504 +2021-01-27T19:24:07 INFO common.c: Starting verification of write complete +2021-01-27T19:24:08 INFO common.c: Flash written and verified! jolly good! +``` + +Launch arm-none-eabi-gdb and perform the following steps for debugging + +1. Load symbol file for test application. Wolfboot.elf is already loaded thanks to .gdbinit supplied with the project. + +`add-symbol-file image.elf` + +2. Set breakpoints at "main", two breakpoints should be created, one for main() in wolfBoot and one for main() in the test app. + +`b main` + +3. Set breakpoints at "measure_boot", "read_measured_boot" + +`b measure_boot` +`b read_measured_boot` + +4. Start execution by invoking "continue" to gdb + +`c` + +5. Expected output on the connected UART terminal (see UART pinout in `Prerequisites`) + +``` +App started +Measured Boot PCR is = 0x01020304050607080910 +``` + +This value is the one created by wolfBoot during start of the device. This value is the result of PCR Extend operation and depends on the firmware image loaded. Using the same firmware image should produce the same PCR measurement. By using different firmware images a change in the PCR value can be obsereved, simulating tampering. + +For more information about measured boot contact us at facts@wolfssl.com + +## Notes + +The supplied openocd.config is prepared for STM32F407-Discovery revision D that uses newer ST-LINK2 variant. + +If you are using STM32F407-Discovery revision B, then modify the very first line in the config file: + +`source [find interface/stlink-v2-1.cfg]` + +with + +`source [find interface/stlink-v2.cfg]` + +Start new openocd session using + +`openocd -f openocd.config` + +Start new gdb session as described in `Usage`. + +Please contact us at support@wolfssl.com for any questions about using this example. + +## Copyright notice + +This example is Copyright (c) 2020 wolfSSL Inc., and distributed under the term of GNU GPL2. wolfBoot, wolfSSL (formerly known as CyaSSL) and wolfCrypt are Copyright (c) 2006-2018 wolfSSL Inc., and licensed for use under GPLv2. -wolfTPM, is Copyright (c) 2018-2020 wolfSSL Inc., and licensed for use under GPLv2. +wolfTPM is Copyright (c) 2018-2020 wolfSSL Inc., and licensed for use under GPLv2. See the documentation within each component subdirectory for more information about using and distributing this software. - From 012a05864f8b0bda373e2bdd71f887f6321864db Mon Sep 17 00:00:00 2001 From: Dimitar Tomov Date: Wed, 27 Jan 2021 19:35:02 +0200 Subject: [PATCH 09/11] Minor changes from peer review Signed-off-by: Dimitar Tomov --- test-app-STM32F4-measured-boot/src/app_stm32f4.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test-app-STM32F4-measured-boot/src/app_stm32f4.c b/test-app-STM32F4-measured-boot/src/app_stm32f4.c index 71fbd18..f75bf24 100644 --- a/test-app-STM32F4-measured-boot/src/app_stm32f4.c +++ b/test-app-STM32F4-measured-boot/src/app_stm32f4.c @@ -1,8 +1,8 @@ -/* stm32f4.c +/* app_stm32f4.c * - * Test bare-metal blinking led application + * Test bare-metal application for reporting measured boot value over UART * - * Copyright (C) 2020 wolfSSL Inc. + * Copyright (C) 2021 wolfSSL Inc. * * This file is part of wolfBoot. * @@ -84,6 +84,8 @@ static const char TPMfailString[]="tpm_init failed"; static const char TPMpcrString[]="Measured Boot PCR is = "; static const char HEX [16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; +volatile uint32_t time_elapsed = 0; /* Used for PWM on LED */ + void uart_write(const char c) { uint32_t reg; @@ -203,7 +205,7 @@ static int app_tpm2_IoCb(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, spi_cs_on(SPI_CS_TPM); - memset(rxBuf, 0, xferSz); + XMEMSET(rxBuf, 0, xferSz); for (i = 0; i < xferSz; i++) { spi_write(txBuf[i]); @@ -263,9 +265,8 @@ static int read_measured_boot(uint8_t* digest) return rc; } -volatile uint32_t time_elapsed = 0; -volatile uint32_t testme = 1; -void main(void) { +void main(void) +{ uint32_t tlen = 0; volatile uint32_t recv_seq; uint32_t r_total = 0; From 0b533d218f5ad5bf4e64432c1b3fea27267a51a1 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 27 Jan 2021 14:59:02 -0800 Subject: [PATCH 10/11] Add support for UART2 (STLink UART). Add `WOLFBOOT_ROOT` for proper detection of signing/keytools (requires PR https://github.com/wolfSSL/wolfBoot/pull/103) --- test-app-STM32F4-measured-boot/Makefile | 55 +++++++---- test-app-STM32F4-measured-boot/README.md | 9 +- .../src/app_stm32f4.c | 93 +++++++++++++------ 3 files changed, 112 insertions(+), 45 deletions(-) diff --git a/test-app-STM32F4-measured-boot/Makefile b/test-app-STM32F4-measured-boot/Makefile index ce9a7b4..7a1a628 100644 --- a/test-app-STM32F4-measured-boot/Makefile +++ b/test-app-STM32F4-measured-boot/Makefile @@ -12,16 +12,39 @@ LC_TIME="C" LC_ALL= APPSRC:=./src -WOLFBOOT:=../wolfBoot +WOLFBOOT_ROOT:=../wolfBoot WOLFSSL_ROOT:=../wolfBoot/lib/wolfssl WOLFTPM_ROOT:=../wolfBoot/lib/wolfTPM -ECCKEY:=$(WOLFBOOT)/ecc256.der +ECCKEY:=$(WOLFBOOT_ROOT)/ecc256.der DEBUG?=1 -include $(WOLFBOOT)/tools/config.mk +include $(WOLFBOOT_ROOT)/tools/config.mk +export WOLFBOOT_ROOT + + +ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen)","") + KEYGEN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/keygen +else + ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen.exe)","") + KEYGEN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/keygen.exe + else + KEYGEN_TOOL:=python3 $(WOLFBOOT_ROOT)/tools/keytools/keygen.py + endif +endif + +ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign)","") + SIGN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/sign +else + ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign.exe)","") + SIGN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/sign.exe + else + SIGN_TOOL:=python3 $(WOLFBOOT_ROOT)/tools/keytools/sign.py + endif +endif + CFLAGS:=-g -ggdb -Wall -Wstack-usage=1024 -ffreestanding -Wno-unused -DPLATFORM_$(TARGET) \ - -I$(WOLFBOOT)/include -I$(WOLFBOOT) -I$(WOLFSSL_ROOT) -I$(WOLFTPM_ROOT) \ + -I$(WOLFBOOT_ROOT)/include -I$(WOLFBOOT_ROOT) -I$(WOLFSSL_ROOT) -I$(WOLFTPM_ROOT) \ -DWOLFBOOT_MEASURED_PCR_A -nostartfiles CFLAGS+=-DWOLFBOOT_HASH_SHA256 CFLAGS+=-DWOLFSSL_USER_SETTINGS @@ -32,9 +55,9 @@ APP_OBJS:= \ $(APPSRC)/led.o \ $(APPSRC)/system.o \ $(APPSRC)/timer.o \ - $(WOLFBOOT)/hal/$(TARGET).o \ - $(WOLFBOOT)/src/libwolfboot.o \ - $(WOLFBOOT)/hal/spi/spi_drv_stm32.o \ + $(WOLFBOOT_ROOT)/hal/$(TARGET).o \ + $(WOLFBOOT_ROOT)/src/libwolfboot.o \ + $(WOLFBOOT_ROOT)/hal/spi/spi_drv_stm32.o \ $(APPSRC)/startup_arm.o # Add objects for wolfCrypt support required by wolfTPM @@ -62,7 +85,7 @@ endif vpath %.c $(dir $(WOLFSSL_ROOT)/src) vpath %.c $(dir $(WOLFSSL_ROOT)/wolfcrypt/src) -vpath %.c $(dir $(WOLFBOOT))/lib/wolfTPM/wolftpm) +vpath %.c $(dir $(WOLFBOOT_ROOT))/lib/wolfTPM/wolftpm) ENTRY_POINT=`cat .entry-point-address` LSCRIPT:=$(APPSRC)/target-app.ld @@ -71,15 +94,15 @@ LDFLAGS:=$(CFLAGS) -T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map wolfboot-example: wolfboot_align.bin image.bin - python3 $(WOLFBOOT)/tools/keytools/sign.py --ecc256 image.bin $(ECCKEY) 1 + $(SIGN_TOOL) --ecc256 image.bin $(ECCKEY) 1 cat wolfboot-align.bin image_v1_signed.bin >factory.bin -wolfboot-align.bin:LSCRIPT:=$(WOLFBOOT)/target.ld +wolfboot-align.bin:LSCRIPT:=$(WOLFBOOT_ROOT)/target.ld wolfboot_align.bin:CFLAGS+=-DWOLFBOOT_HASH_SHA256 wolfboot_align.bin: wolfboot_target - make -C $(WOLFBOOT) align - cp $(WOLFBOOT)/wolfboot-align.bin . - cp $(WOLFBOOT)/wolfboot.elf . + make -C $(WOLFBOOT_ROOT) align + cp $(WOLFBOOT_ROOT)/wolfboot-align.bin . + cp $(WOLFBOOT_ROOT)/wolfboot.elf . image.bin: wolfboot_target image.elf $(OBJCOPY) -O binary image.elf $@ @@ -90,8 +113,8 @@ image.elf: wolfboot_target $(APP_OBJS) $(LSCRIPT) $(Q)$(LD) $(LDFLAGS) $(APP_OBJS) -o $@ wolfboot_target: FORCE - cp -f measured.wolfboot.config $(WOLFBOOT)/.config - make -C $(WOLFBOOT) include/target.h + cp -f measured.wolfboot.config $(WOLFBOOT_ROOT)/.config + make -C $(WOLFBOOT_ROOT) include/target.h %.o:%.c @echo "\t[CC-$(ARCH)] $@" @@ -102,7 +125,7 @@ wolfboot_target: FORCE $(Q)$(CC) $(CFLAGS) -c -o $@ $^ clean: - make -C $(WOLFBOOT) clean + make -C $(WOLFBOOT_ROOT) clean @rm -f *.bin *.elf $(OBJS) wolfboot.map *.bin *.hex src/*.o tags *.map $(LSCRIPT): $(LSCRIPT_TEMPLATE) FORCE diff --git a/test-app-STM32F4-measured-boot/README.md b/test-app-STM32F4-measured-boot/README.md index 06ff47e..bc9b921 100644 --- a/test-app-STM32F4-measured-boot/README.md +++ b/test-app-STM32F4-measured-boot/README.md @@ -54,7 +54,9 @@ Hardware connections must be made between the TPM2.0 module and the STM32F4 boar | 3V | +3V | Pin 1 | | GND | Ground | Pin 6 | -UART1 on the STM32F4 is used. The UART Pinout can be found below: +UART1 on the STM32F4 is used by default. The default baud rate is 115200. + +The UART Pinout can be found below: | STM32F4 | Pin function | |----------|:------------:| @@ -62,7 +64,10 @@ UART1 on the STM32F4 is used. The UART Pinout can be found below: | PB7 | UART RX | | GND | Ground | -Make sure the Ground connection between your USB-UART converter is connected to the STM32F4 board, otherwise UART levels will float and communication will be corrupted. +Note: The ST-Link USB UDC is UART 2 on PA3 (TX) / PA2 (RX). This can be changed in app_stm32f4.c using APP_UART. + +Note: Make sure the Ground connection between your USB-UART converter is connected to the STM32F4 board, otherwise UART levels will float and communication will be corrupted. + ## Compiling diff --git a/test-app-STM32F4-measured-boot/src/app_stm32f4.c b/test-app-STM32F4-measured-boot/src/app_stm32f4.c index f75bf24..3e3707d 100644 --- a/test-app-STM32F4-measured-boot/src/app_stm32f4.c +++ b/test-app-STM32F4-measured-boot/src/app_stm32f4.c @@ -38,12 +38,16 @@ static WOLFTPM2_DEV wolftpm_dev; #define UART1 (0x40011000) +#define UART2 (0x40014400) -#define UART1_SR (*(volatile uint32_t *)(UART1)) -#define UART1_DR (*(volatile uint32_t *)(UART1 + 0x04)) -#define UART1_BRR (*(volatile uint32_t *)(UART1 + 0x08)) -#define UART1_CR1 (*(volatile uint32_t *)(UART1 + 0x0c)) -#define UART1_CR2 (*(volatile uint32_t *)(UART1 + 0x10)) +#ifndef APP_UART +#define APP_UART UART1 +#endif +#define UART_SR (*(volatile uint32_t *)(APP_UART)) +#define UART_DR (*(volatile uint32_t *)(APP_UART + 0x04)) +#define UART_BRR (*(volatile uint32_t *)(APP_UART + 0x08)) +#define UART_CR1 (*(volatile uint32_t *)(APP_UART + 0x0c)) +#define UART_CR2 (*(volatile uint32_t *)(APP_UART + 0x10)) #define UART_CR1_UART_ENABLE (1 << 13) #define UART_CR1_SYMBOL_LEN (1 << 12) @@ -58,18 +62,32 @@ static WOLFTPM2_DEV wolftpm_dev; #define CLOCK_SPEED (168000000) -#define APB2_CLOCK_ER (*(volatile uint32_t *)(0x40023844)) +#define APB2_CLOCK_ER (*(volatile uint32_t *)(0x40023844)) #define UART1_APB2_CLOCK_ER (1 << 4) +#define UART2_APB2_CLOCK_ER (1 << 17) -#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830)) +#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830)) +#define GPIOA_AHB1_CLOCK_ER (1 << 0) #define GPIOB_AHB1_CLOCK_ER (1 << 1) +#ifndef GPIOA_BASE +#define GPIOA_BASE 0x48000000 +#endif +#define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) +#define GPIOA_AFL (*(volatile uint32_t *)(GPIOA_BASE + 0x20)) +#define GPIOA_AFH (*(volatile uint32_t *)(GPIOA_BASE + 0x24)) +#ifndef GPIOB_BASE +#define GPIOB_BASE 0x48000400 +#endif #define GPIOB_MODE (*(volatile uint32_t *)(GPIOB_BASE + 0x00)) #define GPIOB_AFL (*(volatile uint32_t *)(GPIOB_BASE + 0x20)) #define GPIOB_AFH (*(volatile uint32_t *)(GPIOB_BASE + 0x24)) -#define UART1_PIN_AF 7 -#define UART1_RX_PIN 7 -#define UART1_TX_PIN 6 + +#define UART_PIN_AF 7 +#define UART1_RX_PIN 7 /* PB7 */ +#define UART1_TX_PIN 6 /* PB6 */ +#define UART2_RX_PIN 3 /* PA3 */ +#define UART2_TX_PIN 2 /* PA2 */ #define MSGSIZE 16 #define PAGESIZE (256) @@ -90,9 +108,9 @@ void uart_write(const char c) { uint32_t reg; do { - reg = UART1_SR; + reg = UART_SR; } while ((reg & UART_SR_TX_EMPTY) == 0); - UART1_DR = c; + UART_DR = c; } void uart_write_hex(const char c) @@ -104,6 +122,7 @@ void uart_write_hex(const char c) static void uart_pins_setup(void) { uint32_t reg; +#if APP_UART == UART1 AHB1_CLOCK_ER |= GPIOB_AHB1_CLOCK_ER; /* Set mode = AF */ reg = GPIOB_MODE & ~ (0x03 << (UART1_RX_PIN * 2)); @@ -113,9 +132,25 @@ static void uart_pins_setup(void) /* Alternate function: use low pins (6 and 7) */ reg = GPIOB_AFL & ~(0xf << ((UART1_TX_PIN) * 4)); - GPIOB_AFL = reg | (UART1_PIN_AF << ((UART1_TX_PIN) * 4)); + GPIOB_AFL = reg | (UART_PIN_AF << ((UART1_TX_PIN) * 4)); reg = GPIOB_AFL & ~(0xf << ((UART1_RX_PIN) * 4)); - GPIOB_AFL = reg | (UART1_PIN_AF << ((UART1_RX_PIN) * 4)); + GPIOB_AFL = reg | (UART_PIN_AF << ((UART1_RX_PIN) * 4)); +#elif APP_UART == UART2 + AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER; + /* Set mode = AF */ + reg = GPIOA_MODE & ~ (0x03 << (UART2_RX_PIN * 2)); + GPIOA_MODE = reg | (2 << (UART2_RX_PIN * 2)); + reg = GPIOA_MODE & ~ (0x03 << (UART2_TX_PIN * 2)); + GPIOA_MODE = reg | (2 << (UART2_TX_PIN * 2)); + + /* Alternate function: use low pins (6 and 7) */ + reg = GPIOA_AFL & ~(0xf << ((UART2_TX_PIN) * 4)); + GPIOA_AFL = reg | (UART_PIN_AF << ((UART2_TX_PIN) * 4)); + reg = GPIOA_AFL & ~(0xf << ((UART2_RX_PIN) * 4)); + GPIOA_AFL = reg | (UART_PIN_AF << ((UART2_RX_PIN) * 4)); +#else + #error Please configure the UART pins +#endif } int uart_setup(uint32_t bitrate, uint8_t data, char parity, uint8_t stop) @@ -124,40 +159,44 @@ int uart_setup(uint32_t bitrate, uint8_t data, char parity, uint8_t stop) /* Enable pins and configure for AF7 */ uart_pins_setup(); /* Turn on the device */ +#if APP_UART == UART1 APB2_CLOCK_ER |= UART1_APB2_CLOCK_ER; +#elif APP_UART == UART2 + APB2_CLOCK_ER |= UART2_APB2_CLOCK_ER; +#endif /* Configure for TX + RX */ - UART1_CR1 |= (UART_CR1_TX_ENABLE | UART_CR1_RX_ENABLE); + UART_CR1 |= (UART_CR1_TX_ENABLE | UART_CR1_RX_ENABLE); /* Configure clock */ - UART1_BRR = CLOCK_SPEED / bitrate; + UART_BRR = CLOCK_SPEED / bitrate; /* Configure data bits */ if (data == 8) - UART1_CR1 &= ~UART_CR1_SYMBOL_LEN; + UART_CR1 &= ~UART_CR1_SYMBOL_LEN; else - UART1_CR1 |= UART_CR1_SYMBOL_LEN; + UART_CR1 |= UART_CR1_SYMBOL_LEN; /* Configure parity */ switch (parity) { case 'O': - UART1_CR1 |= UART_CR1_PARITY_ODD; + UART_CR1 |= UART_CR1_PARITY_ODD; /* fall through to enable parity */ case 'E': - UART1_CR1 |= UART_CR1_PARITY_ENABLED; + UART_CR1 |= UART_CR1_PARITY_ENABLED; break; default: - UART1_CR1 &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD); + UART_CR1 &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD); } /* Set stop bits */ - reg = UART1_CR2 & ~UART_CR2_STOPBITS; + reg = UART_CR2 & ~UART_CR2_STOPBITS; if (stop > 1) - UART1_CR2 = reg & (2 << 12); + UART_CR2 = reg & (2 << 12); else - UART1_CR2 = reg; + UART_CR2 = reg; /* Turn on uart */ - UART1_CR1 |= UART_CR1_UART_ENABLE; + UART_CR1 |= UART_CR1_UART_ENABLE; return 0; } @@ -167,9 +206,9 @@ char uart_read(void) char c; volatile uint32_t reg; do { - reg = UART1_SR; + reg = UART_SR; } while ((reg & UART_SR_RX_NOTEMPTY) == 0); - c = (char)(UART1_DR & 0xff); + c = (char)(UART_DR & 0xff); return c; } From ab886c973ce68d906526db926c344d0d79eaefcc Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 27 Jan 2021 15:28:12 -0800 Subject: [PATCH 11/11] Added note about starting GDB server. --- test-app-STM32F4-measured-boot/README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test-app-STM32F4-measured-boot/README.md b/test-app-STM32F4-measured-boot/README.md index bc9b921..93c8077 100644 --- a/test-app-STM32F4-measured-boot/README.md +++ b/test-app-STM32F4-measured-boot/README.md @@ -54,6 +54,7 @@ Hardware connections must be made between the TPM2.0 module and the STM32F4 boar | 3V | +3V | Pin 1 | | GND | Ground | Pin 6 | + UART1 on the STM32F4 is used by default. The default baud rate is 115200. The UART Pinout can be found below: @@ -130,26 +131,28 @@ size: 12504 2021-01-27T19:24:08 INFO common.c: Flash written and verified! jolly good! ``` -Launch arm-none-eabi-gdb and perform the following steps for debugging +To debug start first start GDB server (see GDB Server section below). -1. Load symbol file for test application. Wolfboot.elf is already loaded thanks to .gdbinit supplied with the project. +1. Launch arm-none-eabi-gdb and perform the following steps for debugging + +2. Load symbol file for test application. Wolfboot.elf is already loaded thanks to .gdbinit supplied with the project. `add-symbol-file image.elf` -2. Set breakpoints at "main", two breakpoints should be created, one for main() in wolfBoot and one for main() in the test app. +3. Set breakpoints at "main", two breakpoints should be created, one for main() in wolfBoot and one for main() in the test app. `b main` -3. Set breakpoints at "measure_boot", "read_measured_boot" +4. Set breakpoints at "measure_boot", "read_measured_boot" `b measure_boot` `b read_measured_boot` -4. Start execution by invoking "continue" to gdb +5. Start execution by invoking "continue" to gdb `c` -5. Expected output on the connected UART terminal (see UART pinout in `Prerequisites`) +6. Expected output on the connected UART terminal (see UART pinout in `Prerequisites`) ``` App started @@ -160,7 +163,7 @@ This value is the one created by wolfBoot during start of the device. This value For more information about measured boot contact us at facts@wolfssl.com -## Notes +## GDB Server The supplied openocd.config is prepared for STM32F407-Discovery revision D that uses newer ST-LINK2 variant. @@ -178,6 +181,8 @@ Start new openocd session using Start new gdb session as described in `Usage`. +Note: You can also use the ST-Util for GDB server using: `st-util -p 3333`. + Please contact us at support@wolfssl.com for any questions about using this example. ## Copyright notice