You are currently reviewing an older revision of this page.
The System Control Processor (SCP) is a dedicated processor that is used to abstract power and system management tasks away from Application Processors (APs). The SCP Firmware provides a software reference implementation for the SCP on the Arm Total Compute Platform.
The primary services of the SCP firmware include:
On the Total Compute platform, the SCP firmware consists of ROM firmware (SCP BL1) and RAM firmware (SCP BL2). The SCP BL1 firmware is responsible for configuring the entire system and subsequently powering up the main CPU core of the AP. The SCP BL2 firmware serves as the runtime firmware for the SCP, implementing the main features and functionalities of the SCP.
This guide describes:
This guide assumes that you already set up the debugging environment as described in the Guide to Set Up Debugging Environment for Total Compute Software Stack.
The guidance given in this guide is based on the Total Compute TC2-2023.10.04 code.Note: Future code updates might introduce changes, so the guidance might not apply to all cases.
This guide includes the following sections:
The Guide to Set Up Debugging Environment for Total Compute Software Stack describes how to set up the Total Compute debugging environment in Arm DS.
There are some considerations to be aware of when setting up the Total Compute debugging environment.
Step 1: Select Cortex-M3 as your target as follows, when debugging the SCP.
Step 2: Enable debug symbol table file, based on the firmware image you want to debug
By default, however, the SCP firmware is compiled without debug symbol tables. Therefore, the following modifications are necessary to enable the debug symbol tables:
build-scripts:diff --git a/config/common.config b/config/common.config index a99008c..032e3ea 100644 --- a/config/common.config +++ b/config/common.config @@ -23,9 +23,9 @@ CMAKE=${TOOLS_DIR}/cmake-3.22.4-linux-x86_64/bin/cmake SCP_OUTDIR=$OUTPUT_DIR/tmp_build/scp/ SCP_SRC=$SRC_DIR/SCP-firmware SCP_LOG_LEVEL="INFO" -SCP_BUILD_RELEASE=1 +SCP_BUILD_RELEASE=0 SCP_COMPILER=$ARM_BARE_METAL/arm-none-eabi -SCP_BUILD_MODE="release" +SCP_BUILD_MODE="debug" SCP_PLATFORM_VARIANT_STD=0 SCP_PLATFORM_VARIANT_EXPERIMENT=1 SCP_PLATFORM_VARIANT_MPMM=2 src/SCP-firmware:
diff --git a/config/common.config b/config/common.config index a99008c..032e3ea 100644 --- a/config/common.config +++ b/config/common.config @@ -23,9 +23,9 @@ CMAKE=${TOOLS_DIR}/cmake-3.22.4-linux-x86_64/bin/cmake SCP_OUTDIR=$OUTPUT_DIR/tmp_build/scp/ SCP_SRC=$SRC_DIR/SCP-firmware SCP_LOG_LEVEL="INFO" -SCP_BUILD_RELEASE=1 +SCP_BUILD_RELEASE=0 SCP_COMPILER=$ARM_BARE_METAL/arm-none-eabi -SCP_BUILD_MODE="release" +SCP_BUILD_MODE="debug" SCP_PLATFORM_VARIANT_STD=0 SCP_PLATFORM_VARIANT_EXPERIMENT=1 SCP_PLATFORM_VARIANT_MPMM=2
diff --git a/framework/src/fwk_dlist.c b/framework/src/fwk_dlist.c index 6fcfd08b..fa3b3ac7 100644 --- a/framework/src/fwk_dlist.c +++ b/framework/src/fwk_dlist.c @@ -73,10 +73,6 @@ void __fwk_dlist_remove( fwk_assert(node->prev != NULL); fwk_assert(node->next != NULL); - assert(__fwk_slist_contains( - (struct fwk_slist *)list, - (struct fwk_slist_node *)node)); - node->prev->next = node->next; node->next->prev = node->prev; diff --git a/framework/src/fwk_slist.c b/framework/src/fwk_slist.c index b4f41006..a9d09d57 100644 --- a/framework/src/fwk_slist.c +++ b/framework/src/fwk_slist.c @@ -108,8 +108,6 @@ struct fwk_slist_node *__fwk_slist_next( fwk_assert(list != NULL); fwk_assert(node != NULL); - fwk_assert(__fwk_slist_contains(list, node)); - return (node->next == (struct fwk_slist_node *)list) ? NULL : node->next; }
You must recompile the SCP firmware image. The commands to recompile the image are different for Android and the Build Root file system:
export PLATFORM=tc2 export TC_GPU=hwr-prebuilt export TC_TARGET_FLAVOR=fvp export FILESYSTEM=android-fvp ./build-scripts/build-scp.sh clean ./build-scripts/build-scp.sh build ./build-scripts/build-scp.sh deploy build-scripts/build-tfa-trusty.sh build build-scripts/build-tfa-trusty.sh deploy #signed BL1 and SCP_BL1 build-scripts/build-rss.sh deploy #update the fip image build-scripts/build-flash-image.sh deploy
export PLATFORM=tc2 export TC_TARGET_FLAVOR=fvp export FILESYSTEM=buildroot ./build-scripts/build-scp.sh clean ./build-scripts/build-scp.sh build ./build-scripts/build-scp.sh deploy build-scripts/build-tfa.sh build build-scripts/build-tfa.sh deploy #signed BL1 and SCP_BL1 build-scripts/build-rss.sh deploy #update the fip image build-scripts/build-flash-image.sh deploy
The symbol table files corresponding to different firmware are as follows:SCP BL1 Firmware:
output/buildroot/tmp_build/scp/scp/bin/tc2-bl1.elf
output/buildroot/tmp_build/scp/scp/bin/tc2-bl2.elf
Step 3: In the Debugger tab of the SCP debug configuration, include corresponding commands in Execute Debugger Commands to add symbol tables, as shown in the following figure. Each time the debugging connection starts, these commands are automatically executed to load the symbol table files.
Step 4: Click the Debug button to enter the following debugging interface, and you can start debugging.
SCP firmware is divided into three layers, the following figure shows an overview of the components of the SCP firmware:
The Cortex Microcontroller Software Interface Standard (CMSIS) provides a standardized interface for system initialization and low-level hardware access. It enables consistent and efficient software development across different Cortex-M processors. CMSIS RTX2 plays a real-time operating system (RTOS) for SCP firmware.
SCP Firmware includes the following firmwares:
Modules are the building blocks of a product firmware. SCP firmware is composed of several modules, which can be either common modules or modules related to a specific platform.
To find out the specific modules that form the SCP firmware on Total Compute platform, see the following Makefile code:
SCP BL1 Firmware (product/tc2/scp_romfw/Firmware.cmake):if(SCP_ENABLE_PLAT_FVP) list(APPEND SCP_MODULES "pl011") list(APPEND SCP_MODULES "ppu-v1") list(APPEND SCP_MODULES "tc2-bl1") list(APPEND SCP_MODULES "bootloader") list(APPEND SCP_MODULES "system-pll") list(APPEND SCP_MODULES "pik-clock") list(APPEND SCP_MODULES "css-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "timer") list(APPEND SCP_MODULES "cmn-booker") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "mhu2") list(APPEND SCP_MODULES "transport") else() list(APPEND SCP_MODULES "ppu-v1") list(APPEND SCP_MODULES "pl011") list(APPEND SCP_MODULES "msys-rom") list(APPEND SCP_MODULES "snps-umctl") list(APPEND SCP_MODULES "cmn-booker") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "bootloader") list(APPEND SCP_MODULES "system-pll") list(APPEND SCP_MODULES "pik-clock") list(APPEND SCP_MODULES "css-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "timer") endif() SCP BL2 Firmware (product/tc2/scp_ramfw/Firmware.cmake):list(APPEND SCP_MODULES "armv7m-mpu") list(APPEND SCP_MODULES "pl011") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "timer") list(APPEND SCP_MODULES "ppu-v1") list(APPEND SCP_MODULES "system-power") list(APPEND SCP_MODULES "mhu2") list(APPEND SCP_MODULES "transport") list(APPEND SCP_MODULES "scmi") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "system-pll") list(APPEND SCP_MODULES "pik-clock") list(APPEND SCP_MODULES "css-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "power-domain") list(APPEND SCP_MODULES "scmi-power-domain") list(APPEND SCP_MODULES "scmi-system-power") list(APPEND SCP_MODULES "dvfs") list(APPEND SCP_MODULES "scmi-clock") list(APPEND SCP_MODULES "scmi-perf") list(APPEND SCP_MODULES "mock-psu") list(APPEND SCP_MODULES "psu") list(APPEND SCP_MODULES "tc2-system") if(SCP_ENABLE_RESOURCE_PERMISSIONS) list(APPEND SCP_MODULES,"resource-perms") endif()
if(SCP_ENABLE_PLAT_FVP) list(APPEND SCP_MODULES "pl011") list(APPEND SCP_MODULES "ppu-v1") list(APPEND SCP_MODULES "tc2-bl1") list(APPEND SCP_MODULES "bootloader") list(APPEND SCP_MODULES "system-pll") list(APPEND SCP_MODULES "pik-clock") list(APPEND SCP_MODULES "css-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "timer") list(APPEND SCP_MODULES "cmn-booker") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "mhu2") list(APPEND SCP_MODULES "transport") else() list(APPEND SCP_MODULES "ppu-v1") list(APPEND SCP_MODULES "pl011") list(APPEND SCP_MODULES "msys-rom") list(APPEND SCP_MODULES "snps-umctl") list(APPEND SCP_MODULES "cmn-booker") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "bootloader") list(APPEND SCP_MODULES "system-pll") list(APPEND SCP_MODULES "pik-clock") list(APPEND SCP_MODULES "css-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "timer") endif()
list(APPEND SCP_MODULES "armv7m-mpu") list(APPEND SCP_MODULES "pl011") list(APPEND SCP_MODULES "gtimer") list(APPEND SCP_MODULES "timer") list(APPEND SCP_MODULES "ppu-v1") list(APPEND SCP_MODULES "system-power") list(APPEND SCP_MODULES "mhu2") list(APPEND SCP_MODULES "transport") list(APPEND SCP_MODULES "scmi") list(APPEND SCP_MODULES "sds") list(APPEND SCP_MODULES "system-pll") list(APPEND SCP_MODULES "pik-clock") list(APPEND SCP_MODULES "css-clock") list(APPEND SCP_MODULES "clock") list(APPEND SCP_MODULES "power-domain") list(APPEND SCP_MODULES "scmi-power-domain") list(APPEND SCP_MODULES "scmi-system-power") list(APPEND SCP_MODULES "dvfs") list(APPEND SCP_MODULES "scmi-clock") list(APPEND SCP_MODULES "scmi-perf") list(APPEND SCP_MODULES "mock-psu") list(APPEND SCP_MODULES "psu") list(APPEND SCP_MODULES "tc2-system") if(SCP_ENABLE_RESOURCE_PERMISSIONS) list(APPEND SCP_MODULES,"resource-perms") endif()
In the SCP, each module does not contain platform-specific information. When compiling modules into firmware, you must provide configuration for each module based on the platform. In the SCP Firmware of the Total Compute platform, you can find the configuration files in the following directories:
SCP BL1 Firmware:product/tc2/scp_romfw/config_bootloader.c product/tc2/scp_romfw/config_clock.c product/tc2/scp_romfw/config_cmn_booker.c product/tc2/scp_romfw/config_css_clock.c product/tc2/scp_romfw/config_gtimer.c product/tc2/scp_romfw/config_mhu2.c product/tc2/scp_romfw/config_msys_rom.c product/tc2/scp_romfw/config_pik_clock.c product/tc2/scp_romfw/config_pl011.c product/tc2/scp_romfw/config_ppu_v1.c product/tc2/scp_romfw/config_sds.c product/tc2/scp_romfw/config_system_pll.c product/tc2/scp_romfw/config_tc2_bl1.c product/tc2/scp_romfw/config_timer.c product/tc2/scp_romfw/config_transport.c product/tc2/scp_romfw/fmw_memory.h SCP BL2 Firmware:product/tc2/scp_ramfw/config_armv7m_mpu.c product/tc2/scp_ramfw/config_clock.c product/tc2/scp_ramfw/config_css_clock.c product/tc2/scp_ramfw/config_dvfs.c product/tc2/scp_ramfw/config_gtimer.c product/tc2/scp_ramfw/config_mhu2.c product/tc2/scp_ramfw/config_mock_psu.c product/tc2/scp_ramfw/config_mpmm.c product/tc2/scp_ramfw/config_pik_clock.c product/tc2/scp_ramfw/config_pl011.c product/tc2/scp_ramfw/config_power_domain.c product/tc2/scp_ramfw/config_ppu_v1.c product/tc2/scp_ramfw/config_psu.c product/tc2/scp_ramfw/config_reg_sensor.c product/tc2/scp_ramfw/config_resource_perms.c product/tc2/scp_ramfw/config_scmi.c product/tc2/scp_ramfw/config_scmi_clock.c product/tc2/scp_ramfw/config_scmi_perf.c product/tc2/scp_ramfw/config_scmi_power_domain.c product/tc2/scp_ramfw/config_scmi_system_power.c product/tc2/scp_ramfw/config_sds.c product/tc2/scp_ramfw/config_sensor.c product/tc2/scp_ramfw/config_system_pll.c product/tc2/scp_ramfw/config_system_power.c product/tc2/scp_ramfw/config_tc2_power_model.c product/tc2/scp_ramfw/config_thermal_mgmt.c product/tc2/scp_ramfw/config_timer.c product/tc2/scp_ramfw/config_traffic_cop.c product/tc2/scp_ramfw/config_transport.c product/tc2/scp_ramfw/fmw_memory.h product/tc2/scp_ramfw/fmw_notification.h
product/tc2/scp_romfw/config_bootloader.c product/tc2/scp_romfw/config_clock.c product/tc2/scp_romfw/config_cmn_booker.c product/tc2/scp_romfw/config_css_clock.c product/tc2/scp_romfw/config_gtimer.c product/tc2/scp_romfw/config_mhu2.c product/tc2/scp_romfw/config_msys_rom.c product/tc2/scp_romfw/config_pik_clock.c product/tc2/scp_romfw/config_pl011.c product/tc2/scp_romfw/config_ppu_v1.c product/tc2/scp_romfw/config_sds.c product/tc2/scp_romfw/config_system_pll.c product/tc2/scp_romfw/config_tc2_bl1.c product/tc2/scp_romfw/config_timer.c product/tc2/scp_romfw/config_transport.c product/tc2/scp_romfw/fmw_memory.h
product/tc2/scp_ramfw/config_armv7m_mpu.c product/tc2/scp_ramfw/config_clock.c product/tc2/scp_ramfw/config_css_clock.c product/tc2/scp_ramfw/config_dvfs.c product/tc2/scp_ramfw/config_gtimer.c product/tc2/scp_ramfw/config_mhu2.c product/tc2/scp_ramfw/config_mock_psu.c product/tc2/scp_ramfw/config_mpmm.c product/tc2/scp_ramfw/config_pik_clock.c product/tc2/scp_ramfw/config_pl011.c product/tc2/scp_ramfw/config_power_domain.c product/tc2/scp_ramfw/config_ppu_v1.c product/tc2/scp_ramfw/config_psu.c product/tc2/scp_ramfw/config_reg_sensor.c product/tc2/scp_ramfw/config_resource_perms.c product/tc2/scp_ramfw/config_scmi.c product/tc2/scp_ramfw/config_scmi_clock.c product/tc2/scp_ramfw/config_scmi_perf.c product/tc2/scp_ramfw/config_scmi_power_domain.c product/tc2/scp_ramfw/config_scmi_system_power.c product/tc2/scp_ramfw/config_sds.c product/tc2/scp_ramfw/config_sensor.c product/tc2/scp_ramfw/config_system_pll.c product/tc2/scp_ramfw/config_system_power.c product/tc2/scp_ramfw/config_tc2_power_model.c product/tc2/scp_ramfw/config_thermal_mgmt.c product/tc2/scp_ramfw/config_timer.c product/tc2/scp_ramfw/config_traffic_cop.c product/tc2/scp_ramfw/config_transport.c product/tc2/scp_ramfw/fmw_memory.h product/tc2/scp_ramfw/fmw_notification.h
Perform the following steps to load and authenticate firmware:
SCP BL1 Firmware:product/tc2/scp_romfw/fmw_memory.h: /* * ROM memory */ #define FMW_MEM0_SIZE SCP_BOOT_ROM_SIZE #define FMW_MEM0_BASE SCP_BOOT_ROM_BASE arch/arm/arm-m/src/arch.ld.S: mem0 (rwx) : ORIGIN = FMW_MEM0_BASE, LENGTH = FMW_MEM0_SIZE
product/tc2/scp_romfw/fmw_memory.h: /* * ROM memory */ #define FMW_MEM0_SIZE SCP_BOOT_ROM_SIZE #define FMW_MEM0_BASE SCP_BOOT_ROM_BASE
arch/arm/arm-m/src/arch.ld.S: mem0 (rwx) : ORIGIN = FMW_MEM0_BASE, LENGTH = FMW_MEM0_SIZE
SCP BL2 Firmware:product/tc2/scp_ramfw/fmw_memory.h: /* RAM */ #define FMW_MEM0_BASE SCP_RAM_BASE #define FMW_MEM0_SIZE SCP_RAM_SIZE arch/arm/arm-m/src/arch.ld.S: mem0 (rwx) : ORIGIN = FMW_MEM0_BASE, LENGTH = FMW_MEM0_SIZE
product/tc2/scp_ramfw/fmw_memory.h: /* RAM */ #define FMW_MEM0_BASE SCP_RAM_BASE #define FMW_MEM0_SIZE SCP_RAM_SIZE
build-scripts/build-rss.sh: sign_image $RSS_SIGN_AP_BL1_NAME \ $RSS_SIGN_AP_BL1_LOAD_ADDRESS $RSS_SIGN_AP_BL1_BIN_SIZE sign_image $RSS_SIGN_SCP_BL1_NAME \ $RSS_SIGN_SCP_BL1_LOAD_ADDRESS $RSS_SIGN_SCP_BL1_BIN_SIZE
sign_image $RSS_SIGN_AP_BL1_NAME \ $RSS_SIGN_AP_BL1_LOAD_ADDRESS $RSS_SIGN_AP_BL1_BIN_SIZE sign_image $RSS_SIGN_SCP_BL1_NAME \ $RSS_SIGN_SCP_BL1_LOAD_ADDRESS $RSS_SIGN_SCP_BL1_BIN_SIZE
build-scripts/build-flash-image.sh: $FIPTOOL update --align 8192 --rss-bl2 $BINDIR/bl2_signed.bin $2 $FIPTOOL update --align 8192 --rss-scp-bl1 $RSS_BINDIR/signed_$RSS_SIGN_SCP_BL1_NAME $2 $FIPTOOL update --align 8192 --rss-ap-bl1 $RSS_BINDIR/signed_$RSS_SIGN_AP_BL1_NAME $2
$FIPTOOL update --align 8192 --rss-bl2 $BINDIR/bl2_signed.bin $2 $FIPTOOL update --align 8192 --rss-scp-bl1 $RSS_BINDIR/signed_$RSS_SIGN_SCP_BL1_NAME $2 $FIPTOOL update --align 8192 --rss-ap-bl1 $RSS_BINDIR/signed_$RSS_SIGN_AP_BL1_NAME $2
The SCP BL2 firmware is loaded and authenticated by the BL2 firmware of the AP. Therefore, the firmware is packaged into the FIP image during the compilation of TF-A. See the following code for reference:
build-scripts/config/tc2.config:SCP_BL2="$SCP_OUTDIR/scp/bin/tc2-bl2.bin"
SCP_BL2="$SCP_OUTDIR/scp/bin/tc2-bl2.bin"
build-scripts/build-flash-image.sh: # create the GPT layout sgdisk $gpt_image \ --set-alignment 16 \ --disk-guid $location_uuid \ \ --new 1:$start_sector_1:+$num_sectors_fip \ --change-name 1:FIP_A \ --typecode 1:$fip_type_uuid \ --partition-guid 1:$FIP_A_uuid \ \ --new 2:$start_sector_2:+$num_sectors_fip \ --change-name 2:FIP_B \ --typecode 2:$fip_type_uuid \ --partition-guid 2:$FIP_B_uuid \ \ --new 3:$start_sector_3:+$num_sectors_metadata \ --change-name 3:FWU-Metadata \ --typecode 3:$metadata_type_uuid \ \ --new 4:$start_sector_4:+$num_sectors_metadata \ --change-name 4:Bkup-FWU-Metadata \ --typecode 4:$metadata_type_uuid # populate the GPT partitions dd if=$fip_bin of=$gpt_image bs=$sector_size seek=$(gdisk -l $gpt_image | grep " FIP_A$" | awk '{print $2}') count=$num_sectors_fip conv=notrunc dd if=$fip_bin of=$gpt_image bs=$sector_size seek=$(gdisk -l $gpt_image | grep " FIP_B$" | awk '{print $2}') count=$num_sectors_fip conv=notrunc
# create the GPT layout sgdisk $gpt_image \ --set-alignment 16 \ --disk-guid $location_uuid \ \ --new 1:$start_sector_1:+$num_sectors_fip \ --change-name 1:FIP_A \ --typecode 1:$fip_type_uuid \ --partition-guid 1:$FIP_A_uuid \ \ --new 2:$start_sector_2:+$num_sectors_fip \ --change-name 2:FIP_B \ --typecode 2:$fip_type_uuid \ --partition-guid 2:$FIP_B_uuid \ \ --new 3:$start_sector_3:+$num_sectors_metadata \ --change-name 3:FWU-Metadata \ --typecode 3:$metadata_type_uuid \ \ --new 4:$start_sector_4:+$num_sectors_metadata \ --change-name 4:Bkup-FWU-Metadata \ --typecode 4:$metadata_type_uuid # populate the GPT partitions dd if=$fip_bin of=$gpt_image bs=$sector_size seek=$(gdisk -l $gpt_image | grep " FIP_A$" | awk '{print $2}') count=$num_sectors_fip conv=notrunc dd if=$fip_bin of=$gpt_image bs=$sector_size seek=$(gdisk -l $gpt_image | grep " FIP_B$" | awk '{print $2}') count=$num_sectors_fip conv=notrunc
-C board.flashloader0.fname=output/buildroot/deploy/tc2//fip_gpt-tc.bin
According to the Guide to Debug RSS Firmware Booting on Total Compute Platform, SCP BL1 Firmware is loaded and verified by the RSS. When the RSS completes the loading and authentication, it releases the SCP CPU from the wait state and waits for the SCP to send it an MHU message before proceeding with the booting process.
The following figure shows the interaction between the RSS and the SCP:
The interaction between the RSS and the SCP consists of the following steps:
Step 1: During the SCP BL1 Firmware start phase, the start API of the tc2_bl1 module is invoked. This function sends an event FWK_ID_EVENT(FWK_MODULE_IDX_TC2_BL1, MOD_BL1_EVENT_RUN).The specific code can be referenced as follows:product/tc2/module/tc2_bl1/src/mod_tc2_bl1.cstatic int tc2_bl1_start(fwk_id_t id) { struct fwk_event event = { .source_id = FWK_ID_MODULE(FWK_MODULE_IDX_TC2_BL1), .target_id = FWK_ID_MODULE(FWK_MODULE_IDX_TC2_BL1), .id = FWK_ID_EVENT(FWK_MODULE_IDX_TC2_BL1, MOD_BL1_EVENT_RUN), }; return fwk_put_event(&event); } The call stack is as follows:#0 tc2_bl1_start() at mod_tc2_bl1.c:136 #1 fwk_module_start_module() at fwk_module.c:385 #2 start_modules() at fwk_module.c:405 #3 fwk_module_start() at fwk_module.c:444 #4 fwk_arch_init() at fwk_arch.c:100 #5 main() at arch_main.c:70 #6 [_mainCRTStartup+0x4E]
static int tc2_bl1_start(fwk_id_t id) { struct fwk_event event = { .source_id = FWK_ID_MODULE(FWK_MODULE_IDX_TC2_BL1), .target_id = FWK_ID_MODULE(FWK_MODULE_IDX_TC2_BL1), .id = FWK_ID_EVENT(FWK_MODULE_IDX_TC2_BL1, MOD_BL1_EVENT_RUN), }; return fwk_put_event(&event); }
#0 tc2_bl1_start() at mod_tc2_bl1.c:136 #1 fwk_module_start_module() at fwk_module.c:385 #2 start_modules() at fwk_module.c:405 #3 fwk_module_start() at fwk_module.c:444 #4 fwk_arch_init() at fwk_arch.c:100 #5 main() at arch_main.c:70 #6 [_mainCRTStartup+0x4E]
Step 2: After start phase, the SCP BL1 Firmware framework notifies the module tc2_bl1 handle above event FWK_ID_EVENT(FWK_MODULE_IDX_TC2_BL1, MOD_BL1_EVENT_RUN). When handling the event, the tc2_bl1 module sends an MHU message to the RSS. The call stack is as follows:#0 raise_interrupt() at mod_mhu2.c:111 #1 transport_trigger_interrupt() at mod_transport.c:415 #2 bl1_deferred_setup() at mod_tc2_bl1.c:80 #3 tc2_bl1_process_notification() at mod_tc2_bl1.c:207 #4 process_next_event() at fwk_core.c:227 #5 fwk_process_event_queue() at fwk_core.c:297 #6 __fwk_run_main_loop() at fwk_core.c:311 #7 fwk_arch_init() at fwk_arch.c:117 #8 main() at arch_main.c:70 #9 [_mainCRTStartup+0x4E]
#0 raise_interrupt() at mod_mhu2.c:111 #1 transport_trigger_interrupt() at mod_transport.c:415 #2 bl1_deferred_setup() at mod_tc2_bl1.c:80 #3 tc2_bl1_process_notification() at mod_tc2_bl1.c:207 #4 process_next_event() at fwk_core.c:227 #5 fwk_process_event_queue() at fwk_core.c:297 #6 __fwk_run_main_loop() at fwk_core.c:311 #7 fwk_arch_init() at fwk_arch.c:117 #8 main() at arch_main.c:70 #9 [_mainCRTStartup+0x4E]
Step 3: After sending the above MHU message to RSS, the SCP BL1 firmware enters a waiting state. The call stack is as follows:#0 fwk_arch_suspend() at fwk_arch.c:140 #1 __fwk_run_main_loop() at fwk_core.c:312 #2 fwk_arch_init() at fwk_arch.c:117 #3 main() at arch_main.c:70 #4 [_mainCRTStartup+0x4E]
#0 fwk_arch_suspend() at fwk_arch.c:140 #1 __fwk_run_main_loop() at fwk_core.c:312 #2 fwk_arch_init() at fwk_arch.c:117 #3 main() at arch_main.c:70 #4 [_mainCRTStartup+0x4E]
Step 4: When RSS receive the MHU message sent by the SCP, the RSS proceeds to continue loading and authenticating the BL1 firmware of the AP.
Step 5: When RSS loads and authentication of the AP's BL1 firmware has completed, RSS sends another MHU message to SCP.
Step 6: When SCP receives this message, it sends out the event MOD_BL1_EVENT_RSS_HANDSHAKE.The call stack is as follows:#0 tc2_signal_message() at mod_tc2_bl1.c:52 #1 transport_signal_message() at mod_transport.c:664 #2 mhu2_isr() at mod_mhu2.c:99 #3 irq_global() at arch_nvic.c:69 #4 [0xFFFFFFF8]
#0 tc2_signal_message() at mod_tc2_bl1.c:52 #1 transport_signal_message() at mod_transport.c:664 #2 mhu2_isr() at mod_mhu2.c:99 #3 irq_global() at arch_nvic.c:69 #4 [0xFFFFFFF8]
Step 7: Then tc2_bl1 module processes the MOD_BL1_EVENT_RSS_HANDSHAKE event.The call stack is as follows:#0 tc2_bl1_process_event() at mod_tc2_bl1.c:149 #1 process_next_event() at fwk_core.c:227 #2 fwk_process_event_queue() at fwk_core.c:297 #3 __fwk_run_main_loop() at fwk_core.c:311 #4 fwk_arch_init() at fwk_arch.c:117 #5 main() at arch_main.c:70 #6 [_mainCRTStartup+0x4E]
#0 tc2_bl1_process_event() at mod_tc2_bl1.c:149 #1 process_next_event() at fwk_core.c:227 #2 fwk_process_event_queue() at fwk_core.c:297 #3 __fwk_run_main_loop() at fwk_core.c:311 #4 fwk_arch_init() at fwk_arch.c:117 #5 main() at arch_main.c:70 #6 [_mainCRTStartup+0x4E]
The AP BL1 firmware is loaded and authenticated by the RSS, as described in the previous section. After the RSS notifies the SCP BL1 firmware through an MHU message, the SCP BL1 firmware continues to power up the primary CPU of the AP. Subsequently, the main CPU of AP starts up to load the SCP BL2 firmware.The following figure shows you the interaction process between the AP and the SCP:
Step 1: SCP power up primary CPU of AP When the SCP receives the MHU message indicating the completion of AP BL1 firmware loading from the RSS, the SCP can power up the primary CPU of AP by programming the PPU registers. This enables the primary CPU of the AP to start executing the BL1 firmware of the AP. See the following code for reference(product/tc2/module/tc2_bl1/src/mod_tc2_bl1.c):
FWK_LOG_INFO("[TC2_BL1] Got ACK from RSS"); /* Power on the primary cluster and cpu */ ctx.ppu_boot_api->power_mode_on(ctx.bl1_config->id_primary_cluster); ctx.ppu_boot_api->power_mode_on(ctx.bl1_config->id_primary_core);
#0 ppu_v1_request_power_mode() at ppu_v1.c:51 #1 ppu_v1_set_power_mode() at ppu_v1.c:70 #2 ppu_power_mode_on() at mod_ppu_v1.c:645 #3 tc2_bl1_process_event() at mod_tc2_bl1.c:178 #4 process_next_event() at fwk_core.c:227 #5 fwk_process_event_queue() at fwk_core.c:297 #6 __fwk_run_main_loop() at fwk_core.c:311 #7 fwk_arch_init() at fwk_arch.c:117 #8 main() at arch_main.c:70 #9 [_mainCRTStartup+0x4E]
Step 2: AP loads and authenticates SCP BL2 firmware When the primary CPU of the AP is powered up, it starts loading and authenticating the SCP BL2 firmware from the FIP file. However, the SCP BL1 firmware must wait until the primary CPU of the AP finishes loading the SCP BL2 firmware before it can proceed to the next step. The SCP and AP communicates through the mechanism of shared memory, using a Shared Data Structure (SDS). The SCP continuously reads the structure TC2_SDS_BOOTLOADER until AP writes the TC2_SDS_BOOTLOADER structure. Then, the SCP proceeds to the next step. See the following code for reference: while (true) { status = mod_bootloader_ctx.sds_api->struct_read( mod_bootloader_ctx.module_config->sds_struct_id, BOOTLOADER_STRUCT_VALID_POS, &image_flags, sizeof(image_flags)); if (status != FWK_SUCCESS) { return status; } if ((image_flags & (uint32_t)IMAGE_FLAGS_VALID_MASK) != (uint32_t)0) { break; } } The call stack is as follows:#0 sds_struct_read() at mod_sds.c:558 #1 load_image() at mod_bootloader.c:105 #2 tc2_bl1_process_event() at mod_tc2_bl1.c:182 #3 process_next_event() at fwk_core.c:227 #4 fwk_process_event_queue() at fwk_core.c:297 #5 __fwk_run_main_loop() at fwk_core.c:311 #6 fwk_arch_init() at fwk_arch.c:117 #7 main() at arch_main.c:70 #8 [_mainCRTStartup+0x4E]
while (true) { status = mod_bootloader_ctx.sds_api->struct_read( mod_bootloader_ctx.module_config->sds_struct_id, BOOTLOADER_STRUCT_VALID_POS, &image_flags, sizeof(image_flags)); if (status != FWK_SUCCESS) { return status; } if ((image_flags & (uint32_t)IMAGE_FLAGS_VALID_MASK) != (uint32_t)0) { break; } }
#0 sds_struct_read() at mod_sds.c:558 #1 load_image() at mod_bootloader.c:105 #2 tc2_bl1_process_event() at mod_tc2_bl1.c:182 #3 process_next_event() at fwk_core.c:227 #4 fwk_process_event_queue() at fwk_core.c:297 #5 __fwk_run_main_loop() at fwk_core.c:311 #6 fwk_arch_init() at fwk_arch.c:117 #7 main() at arch_main.c:70 #8 [_mainCRTStartup+0x4E]
Step 3: Detect SCP BL2 firmware ready and jump to SCP BL2 firmwareWhen the firmware of SCP BL1 firmware detects that structure TC2_SDS_BOOTLOADER is already valid, it proceeds to parse this structure to get the image information of SCP BL2 firmware. Based on this information, the SCP BL2 firmware is copied from shared memory to the SCP SRAM, the address specified in the linker script as SCP_RAM_BASE.Then, the task of SCP BL1 is complete, and then jump to SCP BL2 firmware.See the following code for reference(product/tc2/scp_romfw/config_bootloader.c):static const struct mod_bootloader_config bootloader_module_config = { .source_base = SCP_TRUSTED_RAM_BASE, .source_size = 512 * 1024, .destination_base = SCP_RAM_BASE, .destination_size = SCP_RAM_SIZE, .sds_struct_id = TC2_SDS_BOOTLOADER, }; module/bootloader/src/mod_bootloader_boot.S:mod_bootloader_boot: movs r4, r0 /* Save the destination - it soon points to the vector table */ 1: ldrb r5, [r1], #1 /* Load next byte from source */ strb r5, [r0], #1 /* Store next byte at destination */ subs r2, #1 /* Decrement the size, which we use as the counter... */ bne 1b /* ... until it reaches zero */ str r4, [r3] /* Store vector table address in SCB->VTOR (if it exists) */ ldr r0, [r4] /* Grab new stack pointer from vector table... */ msr msp, r0 /* ... and update the main stack pointer with it */ ldr r0, [r4, #4] /* Load the reset address from the vector table... */ bx r0 /* ... and take a leap of faith */ .pool At this point, the primary CPU of the AP cannot proceed because the SCMI in the SCP firmware might not have completed initialization. Therefore, the primary CPU of the AP must wait for the SCMI initialization in the SCP BL2 firmware to finish before it can continue execution.
static const struct mod_bootloader_config bootloader_module_config = { .source_base = SCP_TRUSTED_RAM_BASE, .source_size = 512 * 1024, .destination_base = SCP_RAM_BASE, .destination_size = SCP_RAM_SIZE, .sds_struct_id = TC2_SDS_BOOTLOADER, };
mod_bootloader_boot: movs r4, r0 /* Save the destination - it soon points to the vector table */ 1: ldrb r5, [r1], #1 /* Load next byte from source */ strb r5, [r0], #1 /* Store next byte at destination */ subs r2, #1 /* Decrement the size, which we use as the counter... */ bne 1b /* ... until it reaches zero */ str r4, [r3] /* Store vector table address in SCB->VTOR (if it exists) */ ldr r0, [r4] /* Grab new stack pointer from vector table... */ msr msp, r0 /* ... and update the main stack pointer with it */ ldr r0, [r4, #4] /* Load the reset address from the vector table... */ bx r0 /* ... and take a leap of faith */ .pool
Step 4: The SCP BL2 firmware initialization The SCP BL2 firmware initializes each SCMI element based on the SCMI configuration on the Total Compute platform. When the initialization of the last element is completed, it sends the event mod_scmi_notification_id_initializedSee the following code for reference:product/tc2/scp_ramfw/config_scmi.c:static const struct fwk_element service_table[ SCP_TC2_SCMI_SERVICE_IDX_COUNT + 1] = { [SCP_TC2_SCMI_SERVICE_IDX_PSCI] = { .name = "PSCI", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_PSCI), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_PSCI, .scmi_p2a_id = FWK_ID_NONE_INIT, }), }, [SCP_TC2_SCMI_SERVICE_IDX_OSPM_0] = { .name = "OSPM0", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_OSPM_0), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_OSPM, .scmi_p2a_id = FWK_ID_NONE_INIT, }), }, [SCP_TC2_SCMI_SERVICE_IDX_OSPM_1] = { .name = "OSPM1", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_OSPM_1), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_OSPM, .scmi_p2a_id = FWK_ID_NONE_INIT, }), }, [SCP_TC2_SCMI_SERVICE_IDX_COUNT] = { 0 } }; module/scmi/src/mod_scmi.c (scmi_start): if (fwk_id_is_equal(config->transport_notification_init_id, FWK_ID_NONE)) { /* Notify that the service is immediately ready */ struct fwk_event scmi_services_initialized_notification = { .id = mod_scmi_notification_id_initialized, .source_id = id, }; return fwk_notification_notify(&scmi_services_initialized_notification, ¬ifications_sent); } The call stack is as follows:#0 scmi_start() at mod_scmi.c:1130 #1 fwk_module_start_elements() at fwk_module.c:363 #2 fwk_module_start_module() at fwk_module.c:394 #3 start_modules() at fwk_module.c:405 #4 fwk_module_start() at fwk_module.c:444 #5 fwk_arch_init() at fwk_arch.c:100 #6 main() at arch_main.c:70 #7 [_mainCRTStartup+0x4E]
static const struct fwk_element service_table[ SCP_TC2_SCMI_SERVICE_IDX_COUNT + 1] = { [SCP_TC2_SCMI_SERVICE_IDX_PSCI] = { .name = "PSCI", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_PSCI), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_PSCI, .scmi_p2a_id = FWK_ID_NONE_INIT, }), }, [SCP_TC2_SCMI_SERVICE_IDX_OSPM_0] = { .name = "OSPM0", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_OSPM_0), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_OSPM, .scmi_p2a_id = FWK_ID_NONE_INIT, }), }, [SCP_TC2_SCMI_SERVICE_IDX_OSPM_1] = { .name = "OSPM1", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_OSPM_1), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_OSPM, .scmi_p2a_id = FWK_ID_NONE_INIT, }), }, [SCP_TC2_SCMI_SERVICE_IDX_COUNT] = { 0 } };
if (fwk_id_is_equal(config->transport_notification_init_id, FWK_ID_NONE)) { /* Notify that the service is immediately ready */ struct fwk_event scmi_services_initialized_notification = { .id = mod_scmi_notification_id_initialized, .source_id = id, }; return fwk_notification_notify(&scmi_services_initialized_notification, ¬ifications_sent); }
#0 scmi_start() at mod_scmi.c:1130 #1 fwk_module_start_elements() at fwk_module.c:363 #2 fwk_module_start_module() at fwk_module.c:394 #3 start_modules() at fwk_module.c:405 #4 fwk_module_start() at fwk_module.c:444 #5 fwk_arch_init() at fwk_arch.c:100 #6 main() at arch_main.c:70 #7 [_mainCRTStartup+0x4E]
Step 5: SCP BL2 firmware notifies the AP firmware that it can proceed with the booting processWhen all SCMI modules have completed initialization, the SCP BL2 firmware is ready to handle SCMI requests sent by the AP. At this point, SCP BL2 notifies the AP firmware that it can proceed with the booting process. The SCP BL2 informs the AP that the SCMI stack is initialized by writing the structure TC2_SDS_FEATURE_AVAILABILITY. See the following code for reference:product/tc2/scp_ramfw/config_sds.c: { .name = "Feature Availability", .data = &((struct mod_sds_structure_desc){ .id = TC2_SDS_FEATURE_AVAILABILITY, .size = TC2_SDS_FEATURE_AVAILABILITY_SIZE, .payload = &feature_flags, .region_id = TC2_SDS_REGION_SECURE, .finalize = true, }), }, product/tc2/module/tc2_system/src/mod_tc2_system.c:static int messaging_stack_ready(void) { const struct mod_sds_structure_desc *sds_structure_desc = fwk_module_get_data(sds_feature_availability_id); /* * Write SDS Feature Availability to signal the completion of the messaging * stack */ return mod_ctx.sds_api->struct_write( sds_structure_desc->id, 0, (void *)(&feature_flags), sds_structure_desc->size); } The call stack is as follows:#0 messaging_stack_ready() at mod_tc2_system.c:74 #1 tc2_system_process_notification() at mod_tc2_system.c:220 #2 process_next_event() at fwk_core.c:227 #3 fwk_process_event_queue() at fwk_core.c:297 #4 __fwk_run_main_loop() at fwk_core.c:311 #5 fwk_arch_init() at fwk_arch.c:117 #6 main() at arch_main.c:70 #7 [_mainCRTStartup+0x4E]
{ .name = "Feature Availability", .data = &((struct mod_sds_structure_desc){ .id = TC2_SDS_FEATURE_AVAILABILITY, .size = TC2_SDS_FEATURE_AVAILABILITY_SIZE, .payload = &feature_flags, .region_id = TC2_SDS_REGION_SECURE, .finalize = true, }), },
static int messaging_stack_ready(void) { const struct mod_sds_structure_desc *sds_structure_desc = fwk_module_get_data(sds_feature_availability_id); /* * Write SDS Feature Availability to signal the completion of the messaging * stack */ return mod_ctx.sds_api->struct_write( sds_structure_desc->id, 0, (void *)(&feature_flags), sds_structure_desc->size); }
#0 messaging_stack_ready() at mod_tc2_system.c:74 #1 tc2_system_process_notification() at mod_tc2_system.c:220 #2 process_next_event() at fwk_core.c:227 #3 fwk_process_event_queue() at fwk_core.c:297 #4 __fwk_run_main_loop() at fwk_core.c:311 #5 fwk_arch_init() at fwk_arch.c:117 #6 main() at arch_main.c:70 #7 [_mainCRTStartup+0x4E]
Step 6: The SCP BL2 firmware completes the boot process and enters a state of waiting to receive SCMI messages sent by the AP.The call stack is as follows:#0 fwk_arch_suspend() at fwk_arch.c:140 #1 __fwk_run_main_loop() at fwk_core.c:312 #2 fwk_arch_init() at fwk_arch.c:117 #3 main() at arch_main.c:70 #4 [_mainCRTStartup+0x4E]
When the AP reads the structure TC2_SDS_FEATURE_AVAILABILITY written by the SCP BL2 Firmware, the AP proceeds to boot into Linux.In Linux, based on the CPU information in the device tree, it calls the PSCI interface provided by TF-A to power up the subsequent CPUs.In TF-A, the CPU is powered up by invoking SCP services through the SCMI interface.The following figure shows you how CPUs are powered up:
This guide focuses on the SCP process. For the AP software stack process, you can consult the following call stack for reference.Call stack in Linux: #0 __invoke_psci_fn_smc() at psci.c:134 #1 psci_to_linux_errno() at psci.c:224 #2 psci_0_2_cpu_on() at psci.c:139 #3 cpu_psci_cpu_boot(cpu = 2) at psci.c:42 #4 boot_secondary(cpu = 2) at smp.c:111 #5 __cpu_up(cpu = 2,) at smp.c:113 #6 bringup_cpu(cpu = 2) at cpu.c:607 #7 cpuhp_invoke_callback(cpu = 2, state = CPUHP_BRINGUP_CPU, bringup = true) at cpu.c:191 #8 cpuhp_invoke_callback_range(cpu = 2, target = CPUHP_BRINGUP_CPU) at cpu.c:675 #9 cpuhp_up_callbacks(cpu = 2,target = CPUHP_BRINGUP_CPU) at cpu.c:703 #10 _cpu_up(cpu = 2, tasks_frozen = 0, target = CPUHP_ONLINE) at atomic_ll_sc.h:229 #11 cpu_up(cpu = 2, target = CPUHP_ONLINE) at cpu.c:1444 #12 bringup_nonboot_cpus(setup_max_cpus = 32) at cpu.c:1514 #13 atomic_read() at smp.c:1094 #14 smp_init() at atomic-instrumented.h:28 #15 kernel_init_freeable() at main.c:1614 #16 kernel_init() at main.c:1514 Call stack in TF-A:#0 scmi_send_sync_command() at scmi_common.c:63 #1 mmio_read_32(addr = 67112412) at scmi_pwr_dmn_proto.c:47 #2 scmi_pwr_state_set() at mmio.h:46 #3 css_scp_on() at css_pm_scmi.c:233 #4 css_pwr_domain_on() at css_pm.c:73 #5 psci_cpu_on_start() at psci_on.c:149 #6 psci_cpu_on(target_cpu = 256, entrypoint = 2166538848) at psci_main.c:47 #7 psci_smc_handler() at psci_main.c:554 #8 std_svc_smc_handler() at std_svc_setup.c:133 #9 sync_exception_handler() at runtime_exceptions.S:559 #10 [EL3:0xFC482060040223A4]
#0 __invoke_psci_fn_smc() at psci.c:134 #1 psci_to_linux_errno() at psci.c:224 #2 psci_0_2_cpu_on() at psci.c:139 #3 cpu_psci_cpu_boot(cpu = 2) at psci.c:42 #4 boot_secondary(cpu = 2) at smp.c:111 #5 __cpu_up(cpu = 2,) at smp.c:113 #6 bringup_cpu(cpu = 2) at cpu.c:607 #7 cpuhp_invoke_callback(cpu = 2, state = CPUHP_BRINGUP_CPU, bringup = true) at cpu.c:191 #8 cpuhp_invoke_callback_range(cpu = 2, target = CPUHP_BRINGUP_CPU) at cpu.c:675 #9 cpuhp_up_callbacks(cpu = 2,target = CPUHP_BRINGUP_CPU) at cpu.c:703 #10 _cpu_up(cpu = 2, tasks_frozen = 0, target = CPUHP_ONLINE) at atomic_ll_sc.h:229 #11 cpu_up(cpu = 2, target = CPUHP_ONLINE) at cpu.c:1444 #12 bringup_nonboot_cpus(setup_max_cpus = 32) at cpu.c:1514 #13 atomic_read() at smp.c:1094 #14 smp_init() at atomic-instrumented.h:28 #15 kernel_init_freeable() at main.c:1614 #16 kernel_init() at main.c:1514
#0 scmi_send_sync_command() at scmi_common.c:63 #1 mmio_read_32(addr = 67112412) at scmi_pwr_dmn_proto.c:47 #2 scmi_pwr_state_set() at mmio.h:46 #3 css_scp_on() at css_pm_scmi.c:233 #4 css_pwr_domain_on() at css_pm.c:73 #5 psci_cpu_on_start() at psci_on.c:149 #6 psci_cpu_on(target_cpu = 256, entrypoint = 2166538848) at psci_main.c:47 #7 psci_smc_handler() at psci_main.c:554 #8 std_svc_smc_handler() at std_svc_setup.c:133 #9 sync_exception_handler() at runtime_exceptions.S:559 #10 [EL3:0xFC482060040223A4]
For how the SCP BL2 firmware power up secondary CPU of AP, we have following steps:
Step 1: Receive MHU message from AP and send SCMI event When the AP sends an MHU message to the SCP, the SCP receives an MHU interrupt. In the interrupt handler, the transport channel information is obtained based on the MHU information. Subsequently, the transport_signal_message API provided by the transport module is called to deliver the MHU message to the transport module. At the transport module, based on the service bound to this channel, calls the signal_message API provided by the SCMI service, sending this MHU message to the corresponding SCMI service. Then, the SCMI service sends FWK_ID_EVENT(FWK_MODULE_IDX_SCMI, 0) to handle this event in the thread context.The following figure shows you the module relationships:MHU2 module configuration(product/tc2/scp_ramfw/config_mhu2.c):static const struct fwk_element mhu_element_table[ SCP_TC2_MHU_DEVICE_IDX_COUNT + 1] = { [SCP_TC2_MHU_DEVICE_IDX_SCP_AP_S_CLUS0] = { .name = "MHU_SCP_AP_S", .sub_element_count = 1, .data = &(( struct mod_mhu2_channel_config){ .irq = MHU_AP_SEC_IRQ, .recv = SCP_MHU_SCP_AP_RCV_S_CLUS0, .send = SCP_MHU_SCP_AP_SND_S_CLUS0, .channel = 0, }), }, Transport module configuration (product/tc2/scp_ramfw/config_transport.c):static const struct fwk_element transport_element_table[ SCP_TC2_SCMI_SERVICE_IDX_COUNT + 1] = { [SCP_TC2_SCMI_SERVICE_IDX_PSCI] = { .name = "PSCI", .data = &(( struct mod_transport_channel_config){ .channel_type = MOD_TRANSPORT_CHANNEL_TYPE_COMPLETER, .policies = MOD_TRANSPORT_POLICY_INIT_MAILBOX | MOD_TRANSPORT_POLICY_SECURE, .out_band_mailbox_address = (uintptr_t) SCP_SCMI_PAYLOAD_S_A2P_BASE, .out_band_mailbox_size = SCP_SCMI_PAYLOAD_SIZE, .driver_id = FWK_ID_SUB_ELEMENT_INIT( FWK_MODULE_IDX_MHU2, SCP_TC2_MHU_DEVICE_IDX_SCP_AP_S_CLUS0, 0), .driver_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_MHU2, 0), }), }, SCMI module configuration (product/tc2/scp_ramfw/config_scmi.c):static const struct fwk_element service_table[ SCP_TC2_SCMI_SERVICE_IDX_COUNT + 1] = { [SCP_TC2_SCMI_SERVICE_IDX_PSCI] = { .name = "PSCI", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_PSCI), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_PSCI, .scmi_p2a_id = FWK_ID_NONE_INIT, }), },
static const struct fwk_element mhu_element_table[ SCP_TC2_MHU_DEVICE_IDX_COUNT + 1] = { [SCP_TC2_MHU_DEVICE_IDX_SCP_AP_S_CLUS0] = { .name = "MHU_SCP_AP_S", .sub_element_count = 1, .data = &(( struct mod_mhu2_channel_config){ .irq = MHU_AP_SEC_IRQ, .recv = SCP_MHU_SCP_AP_RCV_S_CLUS0, .send = SCP_MHU_SCP_AP_SND_S_CLUS0, .channel = 0, }), },
static const struct fwk_element transport_element_table[ SCP_TC2_SCMI_SERVICE_IDX_COUNT + 1] = { [SCP_TC2_SCMI_SERVICE_IDX_PSCI] = { .name = "PSCI", .data = &(( struct mod_transport_channel_config){ .channel_type = MOD_TRANSPORT_CHANNEL_TYPE_COMPLETER, .policies = MOD_TRANSPORT_POLICY_INIT_MAILBOX | MOD_TRANSPORT_POLICY_SECURE, .out_band_mailbox_address = (uintptr_t) SCP_SCMI_PAYLOAD_S_A2P_BASE, .out_band_mailbox_size = SCP_SCMI_PAYLOAD_SIZE, .driver_id = FWK_ID_SUB_ELEMENT_INIT( FWK_MODULE_IDX_MHU2, SCP_TC2_MHU_DEVICE_IDX_SCP_AP_S_CLUS0, 0), .driver_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_MHU2, 0), }), },
static const struct fwk_element service_table[ SCP_TC2_SCMI_SERVICE_IDX_COUNT + 1] = { [SCP_TC2_SCMI_SERVICE_IDX_PSCI] = { .name = "PSCI", .data = &((struct mod_scmi_service_config) { .transport_id = FWK_ID_ELEMENT_INIT( FWK_MODULE_IDX_TRANSPORT, SCP_TC2_SCMI_SERVICE_IDX_PSCI), .transport_api_id = FWK_ID_API_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_API_IDX_SCMI_TO_TRANSPORT), .transport_notification_init_id = FWK_ID_NOTIFICATION_INIT( FWK_MODULE_IDX_TRANSPORT, MOD_TRANSPORT_NOTIFICATION_IDX_INITIALIZED), .scmi_agent_id = SCP_SCMI_AGENT_ID_PSCI, .scmi_p2a_id = FWK_ID_NONE_INIT, }), },
Step 2: Transforming SCMI events into Power Domain eventsWhen the SCMI module receives the SCMI event, it uses the API provided by the transport module to obtain information about the SCMI protocol, including the header and payload of the SCMI. Then, based on the SCMI protocol information, it calls the corresponding SCMI protocol module for processing, in this case, the SCMI Power Domain protocol module. Subsequently, the SCMI Power Domain protocol module finds the corresponding message processing function based on the provided message ID. During the message processing, it sends an event MOD_PD_PUBLIC_EVENT_IDX_SET_STATE to the Power Domain module for handling. The module relationships are illustrated in the diagram below:
Step 3: Use PPU to power up clusterWhen the SCP receives an SCMI message from the MHU, the scmi_power_domain module parses the SCMI protocol and triggers an event to the power_domain module. Upon receiving this event, the power_domain module identifies the CPU's power domain to activate. Following the hierarchical structure of the CPU's power domain, the power_domain module first activates the power domains of all its parent nodes.In this step, the parent node represents the CPU cluster, necessitating the initiation of the corresponding power domain for the cluster first. The power_domain module initially invokes the API provided by the PPU driver to activate the power domain of the cluster. Once the PPU driver completes the configuration, it requests the power_domain module to dispatch the PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION event for further processing. This event is dispatched to ensure that all parent nodes complete activation before addressing the Core's power.Upon receiving and initiating the processing of the PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION event, the power_domain module signifies a change in the power state of the cluster's power domain. Subsequently, it dispatches a notification, mod_pd_notification_id_power_state_transition, to inform the external system that the cluster has completed activation.The following figure shows module relationships:For the hierarchical structure of the CPU core power domain, you can refer the code below:module/power_domain/src/power_domain_utils.c(create_core_cluster_pd_element_table): for (cluster_idx = 0; cluster_idx < cluster_count; cluster_idx++) { for (core_idx = 0; core_idx < cores_per_clusters; core_idx++) { element = &element_table[core_element_counter]; pd_config = &pd_config_table[core_element_counter]; element->name = fwk_mm_alloc(PD_NAME_SIZE, 1); status = snprintf( (char *)element->name, PD_NAME_SIZE, "CLUS%uCORE%u", cluster_idx, core_idx); if (status < 0) { return FWK_E_PANIC; } element->data = pd_config; pd_config->attributes.pd_type = MOD_PD_TYPE_CORE; pd_config->parent_idx = cluster_idx + core_count; pd_config->driver_id = FWK_ID_ELEMENT(driver_idx, core_element_counter); pd_config->api_id = FWK_ID_API(driver_idx, api_idx); pd_config->allowed_state_mask_table = core_state_table; pd_config->allowed_state_mask_table_size = core_state_table_size; core_element_counter++; } /* Define the cluster configuration */ element = &element_table[cluster_idx + core_count]; pd_config = &pd_config_table[cluster_idx + core_count]; element->name = fwk_mm_alloc(PD_NAME_SIZE, 1); status = snprintf( (char *)element->name, PD_NAME_SIZE, "CLUS%u", cluster_idx); if (status < 0) { return FWK_E_PANIC; } element->data = pd_config; pd_config->attributes.pd_type = MOD_PD_TYPE_CLUSTER; pd_config->driver_id = FWK_ID_ELEMENT(driver_idx, cluster_idx + core_count); pd_config->api_id = FWK_ID_API(driver_idx, api_idx); pd_config->allowed_state_mask_table = cluster_state_table; pd_config->allowed_state_mask_table_size = cluster_state_table_size; }
for (cluster_idx = 0; cluster_idx < cluster_count; cluster_idx++) { for (core_idx = 0; core_idx < cores_per_clusters; core_idx++) { element = &element_table[core_element_counter]; pd_config = &pd_config_table[core_element_counter]; element->name = fwk_mm_alloc(PD_NAME_SIZE, 1); status = snprintf( (char *)element->name, PD_NAME_SIZE, "CLUS%uCORE%u", cluster_idx, core_idx); if (status < 0) { return FWK_E_PANIC; } element->data = pd_config; pd_config->attributes.pd_type = MOD_PD_TYPE_CORE; pd_config->parent_idx = cluster_idx + core_count; pd_config->driver_id = FWK_ID_ELEMENT(driver_idx, core_element_counter); pd_config->api_id = FWK_ID_API(driver_idx, api_idx); pd_config->allowed_state_mask_table = core_state_table; pd_config->allowed_state_mask_table_size = core_state_table_size; core_element_counter++; } /* Define the cluster configuration */ element = &element_table[cluster_idx + core_count]; pd_config = &pd_config_table[cluster_idx + core_count]; element->name = fwk_mm_alloc(PD_NAME_SIZE, 1); status = snprintf( (char *)element->name, PD_NAME_SIZE, "CLUS%u", cluster_idx); if (status < 0) { return FWK_E_PANIC; } element->data = pd_config; pd_config->attributes.pd_type = MOD_PD_TYPE_CLUSTER; pd_config->driver_id = FWK_ID_ELEMENT(driver_idx, cluster_idx + core_count); pd_config->api_id = FWK_ID_API(driver_idx, api_idx); pd_config->allowed_state_mask_table = cluster_state_table; pd_config->allowed_state_mask_table_size = cluster_state_table_size; }
Step 4: Use PPU to power up secondary coreWhen the power_domain module receives the notification that the cluster is powered up, it can begin powering up the core. The entire process is similar to the cluster power-on process.The following figure shows the module relationships: