ContributionsMost RecentMost LikesSolutionsDirect ATF to Linux from QSPI boot flow not working I have a board with an Agilex SoC device. Trying to debug the Altera TSE IP operation with the appropriate Linux drivers. However, the SD card reader is having hardware problems, so I would like to implement a full QSPI boot flow, following this tutorial. I've followed all the steps with a couple of small differences (additional packages in rootfs, different hardware design and kernel configs for TSE and other IP). I also enabled debug build and added some additional logging statements to ATF, resulting in the following: INFO: DDR: DRAM calibration success. INFO: Scrubbing ECC INFO: mailbox_poll_response_v3: SDM err code: 0x2ff INFO: QSPI ref clock: 400000000 NOTICE: QSPI boot INFO: Initializing Qspi INFO: QSPI Capacity: 10000000 INFO: Flash size: 268435456 Bytes INFO: mailbox_poll_response_v3: SDM err code: 0xc WARNING: ROS: Not booted in RSU mode NOTICE: BL2: v2.12.0(debug):QPDS25.1_REL_GSRD_PR-dirty NOTICE: BL2: Built : 10:43:27, May 6 2025 INFO: BL2: Doing platform setup INFO: BL2: Loading image id 3 INFO: Loading image id=3 at address 0x1000 INFO: Image id=3 loaded: 0x1000 - 0x1201d INFO: BL2: Loading image id 5 INFO: Loading image id=5 at address 0x2000000 INFO: Image id=5 loaded: 0x2000000 - 0x4c4b200 INFO: BL2: Loading image id 27 INFO: Loading image id=27 at address 0x10000000 INFO: Image id=27 loaded: 0x10000000 - 0x1000438a INFO: loop 1 count: 0, IMAGE ID: 3, Attr:0x18 INFO: loop 2 count: 0, IMAGE ID: 3, Attr:0x18 INFO: loop 2 count: 1, IMAGE ID: 5, Attr:0x9 INFO: loop 2 count: 2, IMAGE ID: 27, Attr:0x1 ASSERT: common/desc_image_load.c:175 BACKTRACE: START: assert 0: EL3: 0xffe00b2c 1: EL3: 0xffe07a50 2: EL3: 0xffe010b4 3: EL3: 0xffe00990 4: EL3: 0xffe00a68 5: EL3: 0xffe000fc BACKTRACE: END: assert ... restarts and repeats the same ... the two relevant pieces of ATF source code are in arm-trusted-firmware/plat/intel/soc/common/bl2_plat_mem_params_desc.c: #include <common/bl_common.h> #include <common/desc_image_load.h> #include <platform_def.h> #include <plat/common/platform.h> /******************************************************************************* * Following descriptor provides BL image/ep information that gets used * by BL2 to load the images and also subset of this information is * passed to next BL image. The image loading sequence is managed by * populating the images in required loading order. The image execution * sequence is managed by populating the `next_handoff_image_id` with * the next executable image id. ******************************************************************************/ static bl_mem_params_node_t bl2_mem_params_descs[] = { #ifdef SCP_BL2_BASE /* Fill SCP_BL2 related information if it exists */ { .image_id = SCP_BL2_IMAGE_ID, SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, VERSION_2, entry_point_info_t, SECURE | NON_EXECUTABLE), SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, VERSION_2, image_info_t, 0), .image_info.image_base = SCP_BL2_BASE, .image_info.image_max_size = SCP_BL2_SIZE, .next_handoff_image_id = INVALID_IMAGE_ID, }, #endif /* SCP_BL2_BASE */ #ifdef EL3_PAYLOAD_BASE /* Fill EL3 payload related information (BL31 is EL3 payload)*/ { .image_id = BL31_IMAGE_ID, SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, entry_point_info_t, SECURE | EXECUTABLE | EP_FIRST_EXE), .ep_info.pc = EL3_PAYLOAD_BASE, .ep_info.spsr = SPSR_64(MODE_EL3, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS), SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t, IMAGE_ATTRIB_PLAT_SETUP | IMAGE_ATTRIB_SKIP_LOADING), .next_handoff_image_id = INVALID_IMAGE_ID, }, #else /* EL3_PAYLOAD_BASE */ /* Fill BL31 related information */ { .image_id = BL31_IMAGE_ID, SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, entry_point_info_t, SECURE | EXECUTABLE | EP_FIRST_EXE), .ep_info.pc = BL31_BASE, .ep_info.spsr = SPSR_64(MODE_EL3, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS), SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t, IMAGE_ATTRIB_PLAT_SETUP), .image_info.image_base = BL31_BASE, .image_info.image_max_size = BL31_LIMIT - BL31_BASE, .next_handoff_image_id = BL33_IMAGE_ID, }, #endif /* EL3_PAYLOAD_BASE */ { .image_id = BL33_IMAGE_ID, SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, VERSION_2, entry_point_info_t, NON_SECURE | EXECUTABLE), .ep_info.pc = PLAT_NS_IMAGE_OFFSET, SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t, 0), .image_info.image_base = PLAT_NS_IMAGE_OFFSET, .image_info.image_max_size = 0x0 + 0x40000000 - PLAT_NS_IMAGE_OFFSET, # if ARM_LINUX_KERNEL_AS_BL33 != 0 .next_handoff_image_id = NT_FW_CONFIG_ID, }, { .image_id = NT_FW_CONFIG_ID, SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY, VERSION_2, entry_point_info_t, NON_SECURE | NON_EXECUTABLE), SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY, VERSION_2, image_info_t, 0), .image_info.image_base = ARM_PRELOADED_DTB_BASE, .image_info.image_max_size = 0x0 + 0x40000000 - ARM_PRELOADED_DTB_BASE, .next_handoff_image_id = INVALID_IMAGE_ID, }, #else .next_handoff_image_id = INVALID_IMAGE_ID, }, # endif }; REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs) where based on build options specified in the tutorial (ARM_LINUX_KERNEL_AS_BL33=1), the firmware image/entrypoint descriptor list has three items corresponding to bl31, the kernel image, and the kernel .dtb file. The `EXECUTABLE` attributes and `next_handoff_image_id` are as expected, corresponding to the FIP image file created from bl31, image, and .dtb files in the referenced tutorial. The second relevant piece of ATF source code is in arm-trusted-firmware/common/desc_image_load.c: /******************************************************************************* * This function creates the list of executable images, by populating and * linking each `bl_params_node_t` type node, using the internal array of * image descriptor provided by bl_mem_params_desc_ptr. It also populates * and returns `bl_params_t` type structure that contains head of the list * of executable images. ******************************************************************************/ bl_params_t *get_next_bl_params_from_mem_params_desc(void) { unsigned int count; unsigned int img_id = 0U; unsigned int link_index = 0U; bl_params_node_t *bl_current_exec_node = NULL; bl_params_node_t *bl_last_exec_node = NULL; bl_mem_params_node_t *desc_ptr; /* If there is no image to start with, return NULL */ if (bl_mem_params_desc_num == 0U) return NULL; /* Get the list HEAD */ for (count = 0U; count < bl_mem_params_desc_num; count++) { desc_ptr = &bl_mem_params_desc_ptr[count]; INFO("loop 1 count: %u, IMAGE ID: %u, Attr:0x%x", count, desc_ptr->image_id, desc_ptr->ep_info.h.attr); if ((EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE) && (EP_GET_FIRST_EXE(desc_ptr->ep_info.h.attr) == EP_FIRST_EXE)) { next_bl_params.head = &desc_ptr->params_node_mem; link_index = count; break; } } /* Make sure we have a HEAD node */ assert(next_bl_params.head != NULL); /* Populate the HEAD information */ SET_PARAM_HEAD(&next_bl_params, PARAM_BL_PARAMS, VERSION_2, 0U); /* * Go through the image descriptor array and create the list. * This bounded loop is to make sure that we are not looping forever. */ for (count = 0U; count < bl_mem_params_desc_num; count++) { desc_ptr = &bl_mem_params_desc_ptr[link_index]; INFO("loop 2 count: %u, IMAGE ID: %u, Attr:0x%x\n", count, desc_ptr->image_id, desc_ptr->ep_info.h.attr); /* Make sure the image is executable */ assert(EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE); /* Get the memory for current node */ bl_current_exec_node = &desc_ptr->params_node_mem; /* Populate the image information */ bl_current_exec_node->image_id = desc_ptr->image_id; bl_current_exec_node->image_info = &desc_ptr->image_info; bl_current_exec_node->ep_info = &desc_ptr->ep_info; if (bl_last_exec_node != NULL) { /* Assert if loop detected */ assert(bl_last_exec_node->next_params_info == NULL); /* Link the previous node to the current one */ bl_last_exec_node->next_params_info = bl_current_exec_node; } /* Update the last node */ bl_last_exec_node = bl_current_exec_node; /* If no next hand-off image then break out */ img_id = desc_ptr->next_handoff_image_id; if (img_id == INVALID_IMAGE_ID) break; /* Get the index for the next hand-off image */ link_index = get_bl_params_node_index(img_id); assert((link_index > 0U) && (link_index < bl_mem_params_desc_num)); } /* Invalid image is expected to terminate the loop */ assert(img_id == INVALID_IMAGE_ID); return &next_bl_params; } The logs show bl2 failing at assert(EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE); on the third trip through the loop, where `desc_ptr` points to the nt-fw-config image corresponding to the dtb partition in the FIP file. The second for-loop seems like it should break on the last loop trip where the next image in the list has next_handoff_image_id == INVALID_IMAGE_ID but the assertion fails before this loop can break because the nt-fw-config file is not executable. Not really sure where to go from this point. I'm using the same tag for the arm-trusted-firmware repo as the tutorial suggests. There are changes between my configuration and the tutorial as I mentioned, mostly involving the hardware design, but the ATF build was exactly the same. It seems like ATF firmware isn't really capable of booting Linux given the configuration provided in the tutorial, unless there's some other build option I'm missing. Re: Bloated Linux configuration for 64 bit HPS architectures Unless I'm misunderstanding something about kernel configuration, that doesn't really help. I am working on deploying a Linux kernel on an Agilex device. Here are the steps as I see them: 1. Clone linux-socfpga and, with `ARCH=arm64`, run make defconfig 2. Add custom configurations based on the particular needs for my kernel, such as including drivers for FPGA IP or adding debug features for use during development. This is done with a custom config fragment file and ./scripts/kconfig/merge_config.sh -O ./ ./.config ./config-fragment-linux 3. Build the kernel: make Image dtbs modules 4. Deploy the modules in the root filesystem (using a full SD card boot). 5. Deploy kernel image, dtb, other bootloader binaries, and FPGA bitstream onto SD card. 6. Boot the Agilex board. At this point, I could use your script to extract the running config from the target board, bring it to the Linux source path, and use make savedefconfig to extract the non-default configurations (i.e. those configs that differ from arch/arm64/config/defconfig). But I already know what the non-default configurations were, I set them in the config fragment. This doesn't solve my fundamental problem, which is that there are hundreds of configs set in arch/arm64/config/defconfig that don't apply to the Altera SoCs with Arm64 processors. For example, configs enabling different vendor platforms and peripherals What I want is for there to be a defconfig in the Linux source tree which is specific for Altera boards with Arm64 HPS. There is such a config for Cyclone/Arria devices at arch/arm/config/socfpga_defconfig. Bloated Linux configuration for 64 bit HPS architectures Is there any way to get an arm64 defconfig for SoC devices with an aarch64 processor in linux-socfpga? There is an socfpga_defconfig for 32 bit architectures (Arria/Cyclone) in the linux source tree, but the sole defconfig available in arch/arm64/config is bloated with configs for other boards and it's a huge pain to pare that down as a non-expert in the HPS configuration in Agilex/Stratix devices.