- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello
Thanks to contact Altera. My name is Rolando and will assist you with this issue. The build instructions provided were tested for 25.1 release and they were working properly by the time they were created using the 25.1 release repositories. I will rebuild the binaries again adding some log messages to the functions that you describe above to get the values that are being assigned in the data structure and also see what flow is being follow once the data in the structure is retrieved (see how it handles the dtb marked as Non-Executable). I will share the results once I got them.
Thanks
Rolando
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello,
I think you found a bug in the code, but at the same time the results from the build instructions provided in the page seems to work.
When you build with DEBUG=0, for some reason the asserts are disabled. So even if the attribute for the DTB is not set executable the assertion is skiped and it can proceed with the boot flow.
For debug purposes you can use NOTICE instead of INFO and this will allow you to print messages having DEBUG=0.
In my log I see that the value that evaluates the assert for the first 2 items is 1, while the one for the last item is 0. So when DEBUG=1 I think this last condition make it to fail. I will check with the engineering team about this which looks like a bug in the source code. Thanks for finding this issue.
NOTICE: QSPI boot
NOTICE: BL2: v2.12.0(release):QPDS25.1_REL_GSRD_PR-dirty
NOTICE: BL2: Built : 15:02:54, May 7 2025
NOTICE: loop 1 count: 0, IMAGE ID: 3, Attr:0x18
NOTICE: Assert 0 skiped(next_bl_params.head)
NOTICE: loop 2 count: 0, IMAGE ID: 3, Attr:0x18
NOTICE: Image 0 Assert 1 skiped(executable) assert condition: 1
NOTICE: Image 0 Assert 3 skiped(link_index)
NOTICE: loop 2 count: 1, IMAGE ID: 5, Attr:0x9
NOTICE: Image 1 Assert 1 skiped(executable) assert condition: 1
NOTICE: Image 1 Assert 2 skiped(nextparaminfo)
NOTICE: Image 1 Assert 3 skiped(link_index)
NOTICE: loop 2 count: 2, IMAGE ID: 27, Attr:0x1
NOTICE: Image 2 Assert 1 skiped(executable) assert condition: 0
NOTICE: Image 2 Assert 2 skiped(nextparaminfo)
NOTICE: Image 2 Assert 4 skiped(img_id)
NOTICE: BL2: Booting BL31
NOTICE: BL31: v2.12.0(release):QPDS25.1_REL_GSRD_PR-dirty
NOTICE: BL31: Built : 15:02:57, May 7 2025
:
Thanks
Rolando
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi
Do you have a follow up question on this issue?
Regards
Jingyang, Teh
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The issue was introduced during the upstreaming process of the Altera Arm Trusted Firmware code (pushing changes from the Altera repositories to the official ATF repos).
The problem resides on the NON_EXECUTABLE attribute assigned to the Linux device tree (with ID NT_FW_CONFIG_ID) in plat/intel/soc/common/bl2_plat_mem_params_desc.c file. The BL31, the Linux kernel and the Linux device tree are part of the FIP file created and the attributes of each one of the components of this file are evaluated as part of BL2 through an assertion and it is expected to be configured as EXECUTABLE. Initially in the Altera source code the device tree was configured as EXECUTABLE to prevent seeing the assertion, but during the upstream process, the community reviewers rejected this assignment requesting to change the configuration as NON_EXECUTABLE.
Note: The assertions in the ATF only works when this is built in debug mode.
The workaround for this problem is to manually update the attributes of the device tree to classify this as EXECUTABLE. This is done by updating the plat/intel/soc/common/bl2_plat_mem_params_desc.c file as follows:
.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), <-- Replace this line
NON_SECURE | EXECUTABLE), <-- with this line

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page