1. 程式人生 > >C02.TF-A(Arm Trusted Firmware, ATF ) BL1-ROMCode

C02.TF-A(Arm Trusted Firmware, ATF ) BL1-ROMCode

2. BL1

2.1 Build bl1

export CROSS_COMPILE=<path-to-aarch64-gcc>/bin/aarch64-linux-gnu-

Make PLAT=fvp COLD_BOOT_SINGLE_CPU=1 SPD=tspd bl1

變數

COLD_BOOT_SINGLE_CPU=1 復位時只release一個core,HW設計時確定的

復位入口地址:Cold/warm reset entry:bl1_entrypoint

檔案:bl1_entrypoint.S

Ld檔案:bl1.ld.s

在編譯bl1時,會在./build/<PLAT>/<DEBUG>bl1/目錄下下生成最終的ld檔案bl1.ld。

2.2 BL1啟動流程

BL1階段從EL3上的平臺復位向量開始執行。復位地址是平臺確定的,通常位於 trust ROM上。 BL1 的data section在執行時複製到 trust SRAM。

在Arm開發平臺上,BL1程式碼從BL1_RO_BASE位置開始執行。 BL1 data section 複製到trust SRAM的頂部,由BL1_RW_BASE定義。

BL階段實現的功能如下:

2.2.1 確定boot路徑

當CPU從復位釋放時,BL1需要區分 warm boot和cold boot。 這是使用平臺特定的機制完成的(參考Porting Guide中的plat_get_my_entrypoint()

函式)。 在warm boot的情況下,CPU應該從一個不同於cold boot的單獨的入口繼續執行。 在cold boot的情況下,從CPU處於特定於安全平臺的狀態(參考Porting Guideplat_secondary_cold_boot_setup()函式),主CPU執行剩餘的cold boot路徑。

注意,只有在PROGRAMMABLE_RESET_ADDRESS = 0時適用。 有關PROGRAMMABLE_RESET_ADDRESS平臺build選項的效果的更多資訊,參考Reset Design 

2.2.2 arch初始化

BL1執行以下的最小的架構初始化:

  1. Exception vectors

BL1為同步和非同步異常設定簡單的異常向量。 接收異常時的預設行為是填充通用暫存器X0 / R0中的狀態程式碼並呼叫plat_report_exception()函式(參考Porting Guide)。 status code :

對於AArch64:

0x0 : Synchronous exception from Current EL with SP_EL0

0x1 : IRQ exception from Current EL with SP_EL0

0x2 : FIQ exception from Current EL with SP_EL0

0x3 : System Error exception from Current EL with SP_EL0

0x4 : Synchronous exception from Current EL with SP_ELx

0x5 : IRQ exception from Current EL with SP_ELx

0x6 : FIQ exception from Current EL with SP_ELx

0x7 : System Error exception from Current EL with SP_ELx

0x8 : Synchronous exception from Lower EL using aarch64

0x9 : IRQ exception from Lower EL using aarch64

0xa : FIQ exception from Lower EL using aarch64

0xb : System Error exception from Lower EL using aarch64

0xc : Synchronous exception from Lower EL using aarch32

0xd : IRQ exception from Lower EL using aarch32

0xe : FIQ exception from Lower EL using aarch32

0xf : System Error exception from Lower EL using aarch32

Arm FVP上的plat_report_exception() 按以下格式對Versatile Express系統LED暫存器進行程式設計,表示異常:

SYS_LED[0]   - Security state (Secure=0/Non-Secure=1)

SYS_LED[2:1] - Exception Level (EL3=0x3, EL2=0x2, EL1=0x1, EL0=0x0) For AArch32 it is always 0x0

SYS_LED[7:3] - Exception Class (Sync/Async & origin). This is the value of the status code

這是fvp的除錯手段。可以引數下。

對LED暫存器的寫入反映在FVP的CLCD window的System LED(S6LED0..7)中。

除了SMC異常,BL1不希望收到任何異常。BL1初始化安裝一個stub程式碼。 stub期望收到一組有限的SMC型別(由通用暫存器X0 / R0中的功能ID確定):

  • BL1_SMC_RUN_IMAGE:BL2引發此SMC,使BL1將控制權遞給EL3 Runtime Software。
  • “Firmware Update Design Guide”的“BL1 SMC Interface”部分中列出的所有SMC,僅支援AArch64。 如果BL1 使用AArch32 build時,則不支援這些SMC。

 

除了SMC異常,BL1不希望收到任何異常。BL1初始化安裝一個stub程式碼。 stub期望收到一組有限的SMC型別(由通用暫存器X0 / R0中的功能ID確定):

    2. CPU 初始化

BL1呼叫reset_handler()函式,該函式又呼叫CPU特定的復位處理函式(SoC廠商自己實現一個plat_開頭的函式)

    3. Control register 設定(AArch64)

         文件裡說的很清楚了,直接貼原文:

    SCTLR_EL3. Instruction cache is enabled by setting the SCTLR_EL3.I bit. Alignment and stack alignment checking is enabled by setting the SCTLR_EL3.A and SCTLR_EL3.SA bits. Exception endianness is set to little-endian by clearing the SCTLR_EL3.EE bit.

    SCR_EL3. The register width of the next lower exception level is set to AArch64 by setting the SCR.RW bit. The SCR.EA bit is set to trap both External Aborts and SError Interrupts in EL3. The SCR.SIF bit is also set to disable instruction fetches from Non-secure memory when in secure state.

    CPTR_EL3. Accesses to the CPACR_EL1 register from EL1 or EL2, or the CPTR_EL2 register from EL2 are configured to not trap to EL3 by clearing the CPTR_EL3.TCPAC bit. Access to the trace functionality is configured not to trap to EL3 by clearing the CPTR_EL3.TTA bit. Instructions that access the registers associated with Floating Point and Advanced SIMD execution are configured to not trap to EL3 by clearing the CPTR_EL3.TFP bit.

    DAIF. The SError interrupt is enabled by clearing the SError interrupt mask bit.

    MDCR_EL3. The trap controls, MDCR_EL3.TDOSA, MDCR_EL3.TDA and MDCR_EL3.TPM, are set so that accesses to the registers they control do not trap to EL3. AArch64 Secure self-hosted debug is disabled by setting the MDCR_EL3.SDD bit. Also MDCR_EL3.SPD32 is set to disable AArch32 Secure self-hosted privileged debug from S-EL1.

2.3.3 platform初始化

對於ARM fvp,BL1 執行下列的初始化步驟:

  1. 使能Trusted Watchdog.
  2. 初始化console(PL101).
  3. 配置Interconnect,使能硬體coherency.
  4. 使能MMU ,map the memory,訪問許可權.
  5. 載入下一階段的image(BL2).
  6. 如果使用動態配置檔案TB_FW_CONFIG, 通過arg0將檔案地址傳給BL2
  7. 配置system timer ,程式設計CNTFRQ_EL0 ,給NS-BL1U and NS-BL2U firmware update images使用.

 

2.2.4韌體更新檢測和執行

執行平臺設定後,BL1公共程式碼呼叫bl1_plat_get_next_image_id()以確定是否需要韌體更新或繼續正常啟動過程。 如果平臺程式碼返回BL2_IMAGE_ID,則正常引導BL2,否則BL1假定需要進行韌體更新,並且執行將傳遞到韌體更新過程中的第一個映像(韌體更新映象可能是多個)。 在任何一種情況下,BL1都是通過呼叫bl1_plat_get_image_desc()來獲取下一個image的描述符。 image描述符包含entry_point_info_t結構體,BL1用它來初始化下一image的執行狀態

 

2.2.5 BL2載入和執行

在正常啟動流程中,BL1繼續執行:

  1.  BL1從主CPU列印以下字串,表示BL1階段的成功執行:
    Booting Trusted Firmware
  2. BL1在platforms 指定的一個基地址處(SoC廠商自行實現的從外部flash載入BL2的方法)載入BL2原始二進位制映象。在載入之前,BL1呼叫bl1_plat_handle_pre_image_load(),主要是platforms 更新或使用當前的映象資訊。 如果BL2檔案不存在或者沒有足夠的可用的 trust SRAM,則會列印以下錯誤訊息:
    Failed to load BL2 firmware.

     

  3. BL1呼叫bl1_plat_handle_post_image_load(),platforms 在映象載入後首先填充BL2的必要引數,其中還包括SRAM layout。
  4. BL1將控制權交給安全EL1(對於AArch64)或安全SVC模式(對於AArch32)的BL2,從BL2的載入地址開始執

 

2.3 bl1.ld.s

先看bl1.ld.s

 

15行,BL_RO_BASE在arm-def.h中定義

361行,在platform.h中定義

Fvp平臺上是reset entry在00000000地址

大小:64MB

注意這個USE_ROM_LIB:

在make_helpers/defaults.mk中:

具體作用,官方並沒有給出詳細介紹,後面通過程式碼分析。

同理,RAM地址(這裡指的是SRAM):0403 5000~0404 0000 大小:B000,44K。

後面分析bl2時會發現,實際SRAM大小為:

(platform_def.h),但bl2會佔用一部分SRAM空間。

關注bl1.ld.s的25行,預設的編譯選項是SEPARATE_CODE_AND_RODATA =1的。

 

2.4 復位行為

bl1_entrypoint.S

程式碼不多,直接貼出來

27行,在.include\common\aarch64\el3_common_macros.S  line 174 中定義的巨集,

199行,設定sctlr_el3:清sctlr_el3.EE(設定小端)sctlr_el3.WXN memory access permissions disable,SCTLR_EL3.SA (disable Stack Alignment check) SCTLR_EL3.A(disable Alignment fault checking)

預設 PROGRAMMABLE_RESET_ADDRESS = 0  ,則執行213行

plat_get_my_entrypoint在plat\arm\board\fvp\aarch64\fvp_helpers.S,124行

主要工作是區分cold reset 和warm reset。 FVP上是可以從The Power Control SYS Status Register (PSYSR) 獲取CPU wake-up原因。

plat_get_my_entrypoint()

在禁用MMU和Cache的情況下呼叫此函式(SCTLR_EL3.M = 0且SCTLR_EL3.C = 0)。 該函式負責使用各個平臺自己的方法區分當前CPU的warm boot和cold boot。 如果是熱復位,則它返回在BL31初始化期間提供給plat_setup_psci_ops()的warm boot入口點。 如果是冷復位,必須返回零。

簡單的說,這個函式在cold boot時返回0,warm boot, 返回malibox中包含的地址相關資訊

,這個地址很有可能是電源狀態轉換之前設定的入口地址。各個平臺需要實現:

回到el3_entrypoint_common,如果是cold boot,則執行do_cold_boot,則否branch到x0地址執行(mailbox返回的一個地址)

 

224行,載入exception_vector->x0->vbar_el3。exception_vector巨集引數,值為bl1_exceptions

如果是cold boot,在234行跳轉到reset_handler(lib/cpus/aarch64/cpu_helpers.S, 行24)

Arm提供的reset_handler是一個通用架構,各個平臺需要自己實現plat_reset_handler

我們看下關於reset_handler 和plat_reset_handler(porting-guide.html#handling-reset)

復位要做的工作:

For each CPU, the reset vector code is responsible for the following tasks:

  1. Distinguishing between a cold boot and a warm boot.
  2. In the case of a cold boot and the CPU being a secondary CPU, ensuring that the CPU is placed in a platform-specific state until the primary CPU performs the necessary steps to remove it from this state.

In the case of a warm boot, ensuring that the CPU jumps to a platform- specific address in the BL31 image in the same processor mode as it was when released from reset.

reset_handler

Plat_reset_handler

看arm是如何要求的:

A platform may need to do additional initialization after reset. This function allows the platform to do the platform specific intializations. Platform specific errata workarounds could also be implemented here. The api should preserve the values of callee saved registers x19 to x29.

The default implementation doesn’t do anything. If a platform needs to override the default implementation, refer to the Firmware Design for general guidelines.

After a reset, the state of the CPU when it calls generic reset handler is: MMU turned off, both instruction and data caches turned off and not part of any coherency domain.

The BL entrypoint code first invokes the plat_reset_handler() to allow the platform to perform any system initialization required and any system errata workarounds that needs to be applied. The get_cpu_ops_ptr() reads the current CPU midr, finds the matching cpu_ops entry in the cpu_ops array and returns it. Note that only the part number and implementer fields in midr are used to find the matching cpu_ops entry. The reset_func() in the returned cpu_ops is then invoked which executes the required reset handling for that CPU and also any errata workarounds enabled by the platform. This function must preserve the values of general purpose registers x20 to x29.

Refer to Section “Guidelines for Reset Handlers” for general guidelines regarding placement of code in a reset handler.

復位後,CPU呼叫通用復位處理程式時的狀態為:MMU關閉,指令和資料快取都關閉,不屬於任何一致性域。

BL入口程式碼首先呼叫plat_reset_handler()以允許平臺執行所需的系統初始化以及需要應用的系統勘誤表解決方法。 get_cpu_ops_ptr()讀取當前的CPU midr,在cpu_ops陣列中找到匹配的cpu_ops入口並將它返回。 請注意,只有midr中的part number and implementer 欄位用於查詢匹配的cpu_ops入口。 然後呼叫返回的cpu_ops中的reset_func(),它執行該CPU所需的復位處理以及平臺啟用的勘誤解決方法。 此函式必須保留通用暫存器x20到x29的值。

Arm fvp平臺上plat_reset_handler(plat/common/aarch64/platform_helpers.S,122行)什麼也沒做,其他廠商,marvel在這裡使能L1/L2 ECC(MMU操作之前使能),rockchip針對A72和其他Soc修正的一些暫存器值

回到reset_handler,在看get_cpu_ops_ptr(lib/cpus/aarch64/cpu_helpers.S, 144行),前面已經說了,他的作用是獲取獲取一個cpu_ops, cpu_ops是一個會變定義的結構體內容是:

 

實現方式是根據MIDR匹配這個結構體陣列,返回在cpu_ops從 __CPU_OPS_START__處的偏移量。

事實上,在編譯fvp版本的bl1時,會把lib/aarch64/目錄下的檔案進行編譯連線(可參考下platform.mk),所有的cpu_ops都放在cpu_opssection通過midr查詢匹配。

reset_handler第37~43行,

獲取cpu_reset_func的地址,跳轉執行,使用br的意義應該是非子例程返回,即不更新x30暫存器,應該是在cpu_reset_func中自己更新或者不需要更新?

cpu_reset_func,我們使用cortex_a35進行說明,程式碼在lib/aarch64/cortex_a35.S中,44行

至此reset_handler 執行完成,返回呼叫者bl1_entrypoint->.macro.el3_entrypoint_common,執行236行:

在el3_common_macro.S,16行,主要設定sctlr_el3,

初始化SCR_EL3

初始化MDCR_EL3

使能Abort和Serror中斷

初始化CPTR_EL3

bl1_entrypoint->.macro.el3_entrypoint_common,238行

由於設定了COLD_BOOT_SINGLE_CPU=1,在引數傳遞時

不執行這部分程式碼

繼續,初始化memory

\plat\arm\common\aarch64\arm_helpers.S,81行

在porting guide中:

目前git上還沒有廠商實現

繼續bl1_entrypoint->.macro.el3_entrypoint_common,276行. 293行~299行,初始化C環境。清bss,由於預設USE_COHERENT_MEM =1,清coherent_ram段。Copy .data* 段資料到__DATA_RAM_START__ (0x04035000)

使用SP_EL0作為C runtime的SP

設定stack

plat\common\aarch64\platform_up_stack.S

40行,get_up_stack是一個巨集,在include\common\aarch64\asm_macros.S中定義的

第二個platform_normal_stacks:

Declare_stack 在(include/commonaarch64/asm_macro_common.S,92行):

將棧放到.section tzfw_normal_stacks段(對應到bl1.ld.s ,122行)

棧大小0x450,位置__STACKS_END__=0x43035540,__STACKS_START__=0x40350f1,SP:SP_EL0

回到bl1_entrypoint->.macro.el3_entrypoint_common,337行

由於預設STACK_PROTECTOR_ENABLED=0,暫時不做分析。

至此,el3_entrypoint_common巨集執行完畢。

 

繼續執行bl1_entrypoint,

(To be continued )