ast27x0: Add Caliptra Manifest boot support and MCU runtime loader framework

Add initial support for Caliptra Manifest based boot flow on AST2700
and introduce a generic MCU runtime image loader framework.

This change adds full parsing, verification, and loading support for
Caliptra flash images, including manifest header handling, checksum
verification, image table parsing, and SoC manifest processing. A new
boot policy is implemented to prefer Caliptra Manifest boot and fall
back to legacy FIT boot on failure.

Key components introduced in this change:

- Add Caliptra Manifest data structures, image metadata, and helper APIs
(manifest.c, manifest_image.c, manifest.h).
- Introduce ast_loader and stor modules to abstract image lookup and
loading from manifest storage into destination memory.
- Add board-specific manifest boot handling for AST2700, including
BL31, U-Boot, SSP, and TSP image post-processing and release flow.
- Support MCU runtime firmware loading for AST2700 A1/A2 platforms.
- Support SPL and appended DTB discovery for both Caliptra and FIT paths.
- Enable SSP boot flow and chained TSP boot when corresponding firmware
images are present.
- Refactor platform constants into platform_ast2700.h and clean up
address handling and reserved memory parsing.

The implementation is aligned with the MCU runtime design used in the
Zephyr-based reference implementation:
https://github.com/AspeedTech-BMC/aspeed-zephyr-project/tree/aspeed-master/apps/mcu-runtime

This commit establishes the foundation for Caliptra-based boot while
maintaining backward compatibility with existing FIT-based boot
flows.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
diff --git a/ast27x0/Makefile b/ast27x0/Makefile
index 6330cea..cf201f0 100644
--- a/ast27x0/Makefile
+++ b/ast27x0/Makefile
@@ -39,24 +39,29 @@
 # Without these flags, function pointers in .data may be NULL or invalid at
 # runtime.
 CFLAGS		= -Os -Wall -Wextra -g -mcpu=cortex-a35 -fno-stack-protector \
-		  -no-pie -fno-pic \
-		  -I ./include -I ../lib/libfdt -I ../lib/libc/minimal/include
+		-no-pie -fno-pic \
+		-I ./include -I ../lib/libfdt -I ../lib/libc/minimal/include
+
 CFLAGS		+= -DGIT_VERSION=\"$(GIT_VERSION)\"
 ASFLAGS		= $(CFLAGS) -Wa,-mcpu=cortex-a35
 LDSCRIPT	= bootrom.ld
 MAPFILE		= bootrom.map
 LDFLAGS		= -Wl,--build-id=none -static -nostdlib -T $(LDSCRIPT) -Wl,-Map=$(MAPFILE)
 
-OBJS		:= start.o image.o fmc_image.o uart_aspeed.o uart_console.o ssp_tsp.o \
-			../lib/libc/minimal/source/string/string.o \
-			../lib/libfdt/fdt.o ../lib/libfdt/fdt_ro.o
+OBJS		:= start.o image.o stor.o ast_loader.o fmc_image.o board_ast2700.o \
+		manifest.o manifest_image.o \
+		uart_aspeed.o uart_console.o ssp_tsp.o \
+		../lib/libc/minimal/source/string/string.o \
+		../lib/libfdt/fdt.o ../lib/libfdt/fdt_ro.o \
+		../lib/libfdt/fdt_rw.o ../lib/libfdt/fdt_wip.o \
+		../lib/libfdt/fdt_addresses.o
 
 .PHONY: all clean
 all: ast27x0_bootrom.bin ast27x0_bootrom.asm
 
 clean:
 	rm -f *.o *.bin *.elf *.asm *.map ../lib/libfdt/*.o \
-		../lib/libc/minimal/source/string/*.o
+	../lib/libc/minimal/source/string/*.o
 
 ast27x0_bootrom.bin: ast27x0_bootrom.elf
 	$(OBJCOPY) -O binary $< $@
diff --git a/ast27x0/ast_loader.c b/ast27x0/ast_loader.c
new file mode 100644
index 0000000..1caa972
--- /dev/null
+++ b/ast27x0/ast_loader.c
@@ -0,0 +1,87 @@
+/*
+ * Firmware image loader helpers.
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <uart_console.h>
+#include <manifest.h>
+#include <stor.h>
+
+#define POLY            0xEDB88320 /* reflection of 0x04C11DB7 */
+#define INITIAL_CRC     0xFFFFFFFF
+#define FINAL_XOR       0xFFFFFFFF
+
+static uint32_t crc_table[256];
+
+void generate_crc32_table(void)
+{
+    uint32_t crc;
+
+    for (uint32_t i = 0; i < 256; i++) {
+        crc = i;
+
+        for (int j = 0; j < 8; j++) {
+            crc = (crc & 1) ? (crc >> 1) ^ POLY : (crc >> 1);
+        }
+
+        crc_table[i] = crc;
+    }
+}
+
+uint32_t calc_checksum(const uint8_t *data, size_t length)
+{
+    uint32_t crc = INITIAL_CRC;
+
+    for (size_t i = 0; i < length; i++) {
+        crc = (crc >> 8) ^ crc_table[(crc ^ data[i]) & 0xFF];
+    }
+
+    return crc ^ FINAL_XOR;
+}
+
+int ast_loader_read(uint64_t *dst, uint64_t src, uint32_t len)
+{
+    uprintf("%s: dst=0x%lx, src=0x%lx, len=%d\n",
+            __func__, dst, src, len);
+    memcpy(dst, (void *)(uintptr_t)src, len);
+
+    return CPTRA_SUCCESS;
+}
+
+int _ast_loader_load_image(uint32_t type, uint32_t *dst)
+{
+    const struct image_info *img_info = stor_get_image_info_table();
+    uint32_t sz = 0;
+
+    uprintf("%s: %s, type=%d, dst=0x%lx\n", __func__,
+            cptra_ime_get_image_name(img_info[type].id), type, dst);
+    stor_load(type, dst, &sz);
+
+    return CPTRA_SUCCESS;
+}
+
+int ast_loader_load_image(uint32_t type, uint32_t *dst)
+{
+    return _ast_loader_load_image(type, dst);
+}
+
+int ast_loader_load_manifest_image(uint32_t type, uint32_t *dst)
+{
+    return _ast_loader_load_image(type, dst);
+}
+
diff --git a/ast27x0/board_ast2700.c b/ast27x0/board_ast2700.c
new file mode 100644
index 0000000..d1f626e
--- /dev/null
+++ b/ast27x0/board_ast2700.c
@@ -0,0 +1,236 @@
+/*
+ * Caliptra manifest boot image loader.
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <libfdt.h>
+#include <image.h>
+#include <uart_console.h>
+#include <manifest.h>
+#include <stor.h>
+#include <ast_loader.h>
+#include <io.h>
+#include <ssp_tsp.h>
+#include <platform_ast2700.h>
+
+#define PSP_TIMEOUT 100000000ULL
+#define MANIFEST_SEARCH_START (ASPEED_FMC_CS0_BASE)
+#define MANIFEST_SEARCH_END   (MANIFEST_SEARCH_START + 0x400000)
+#define MANIFEST_SEARCH_STEP  0x10000
+
+static struct reserved_mem_info reservedinfo = {
+    .ssp = { .addr = 0x42c000000ULL, .size = 0x02000000ULL },
+    .tsp = { .addr = 0x42e000000ULL, .size = 0x02000000ULL },
+    .atf = { .addr = 0x430000000ULL, .size = 0x00080000ULL },
+    .tee = { .addr = 0x430080000ULL, .size = 0x01000000ULL },
+    .ipc_ssp = { .addr = 0x431080000ULL, .size = 0x00800000ULL }
+};
+
+static bool has_pspfw;
+static bool has_sspfw;
+static bool has_tspfw;
+
+extern void panic(const char *);
+
+void board_manifest_image_post_process(uint32_t fw_id)
+{
+    uintptr_t ep = cptra_ime_get_load_addr(fw_id);
+    uint64_t ep_arm = 0;
+
+    /* convert to Arm view */
+    ep_arm = convert_mcu_addr_to_arm_dram((uint64_t)ep);
+
+    switch (fw_id) {
+    case CPTRA_SSP_FW_ID:
+        ssp_init(ep_arm, &reservedinfo);
+        has_sspfw = true;
+        break;
+    case CPTRA_ATF_FW_ID:
+        has_pspfw = true;
+        writel(ep_arm >> 4, SCU0_CA35_RVBAR0);
+        writel(ep_arm >> 4, SCU0_CA35_RVBAR1);
+        writel(ep_arm >> 4, SCU0_CA35_RVBAR2);
+        writel(ep_arm >> 4, SCU0_CA35_RVBAR3);
+        break;
+    case CPTRA_UBOOT_FW_ID:
+        writel((uint32_t)ep_arm, SCU0_CPU_SMP_EP0);
+        writel((uint32_t)(ep_arm >> 32), SCU0_CPU_SMP_EP0 + 4);
+        break;
+    case CPTRA_TSP_FW_ID:
+        tsp_init(ep_arm, &reservedinfo);
+        has_tspfw = true;
+        break;
+    default:
+        break;
+    }
+}
+
+static void board_prepare_for_boot(void)
+{
+    if (has_pspfw) {
+        /* clean up secondary entries */
+        writel(0x0, SCU0_CPU_SMP_EP1);
+        writel(0x0, SCU0_CPU_SMP_EP1 + 4);
+        writel(0x0, SCU0_CPU_SMP_EP2);
+        writel(0x0, SCU0_CPU_SMP_EP2 + 4);
+        writel(0x0, SCU0_CPU_SMP_EP3);
+        writel(0x0, SCU0_CPU_SMP_EP3 + 4);
+
+        /* release CA35 reset */
+        writel(0x1, SCU0_CA35_REL);
+    }
+
+    /* release SSP reset */
+    if (has_sspfw) {
+        ssp_enable();
+    }
+
+    /* release TSP reset */
+    if (has_tspfw) {
+        tsp_enable();
+    }
+}
+
+static int find_manifest_image(uint64_t start_addr, uint64_t end_addr,
+                               uint64_t search_step, uint64_t *manifest_base)
+{
+    struct cptra_checksum_info *chk;
+    struct cptra_manifest_hdr *hdr;
+    uint32_t hdr_crc32 = 0;
+    uint32_t hdr_size;
+    uint32_t chk_size;
+    uint64_t addr;
+
+    hdr_size = sizeof(struct cptra_manifest_hdr);
+    chk_size = sizeof(struct cptra_checksum_info);
+
+    for (addr = start_addr;
+         (addr + hdr_size + chk_size) <= end_addr;
+         addr += search_step) {
+
+        hdr = (struct cptra_manifest_hdr *)addr;
+        chk = (struct cptra_checksum_info *)(addr + hdr_size);
+
+        if (hdr->magic == CPTRA_FLASH_IMG_MAGIC &&
+            hdr->img_count < CPTRA_IMC_ENTRY_COUNT) {
+            /* Check the flash image header crc checksum */
+            generate_crc32_table();
+            hdr_crc32 = calc_checksum((uint8_t *)hdr, hdr_size);
+            if (hdr_crc32 == chk->hdr_checksum) {
+                uprintf("Found valid caliptra flash image at 0x%lx\n", addr);
+                *manifest_base = addr;
+                return CPTRA_SUCCESS;
+            }
+        }
+    }
+
+    uprintf("No valid caliptra flash image found in range");
+    uprintf(" 0x%lx - 0x%lx (step: 0x%lx)\n",
+            start_addr, end_addr, search_step);
+
+    return CPTRA_ERR_IMAGE_OFFSET_INVALID;
+}
+
+static void dump_reserved_memory(struct reserved_mem_info *info)
+{
+    struct {
+        const char *path;
+        struct mem_region *region;
+    } nodes[] = {
+        { SSP_MEMORY_NODE,     &info->ssp       },
+        { TSP_MEMORY_NODE,     &info->tsp       },
+        { ATF_MEMORY_NODE,     &info->atf       },
+        { OPTEE_MEMORY_NODE,   &info->tee       },
+        { IPC_SSP_MEMORY_NODE, &info->ipc_ssp   },
+    };
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(nodes); i++) {
+        uprintf("[reserved] %s base: 0x%lx  size: 0x%lx\n", nodes[i].path,
+                nodes[i].region->addr, nodes[i].region->size);
+    }
+}
+
+int load_manifest_boot_image(uint64_t *jump_addr)
+{
+    const struct image_info *img_info = stor_get_image_info_table();
+    struct cptra_image_context *cptra_ctx = cptra_get_ctx();
+    uint64_t timeout = PSP_TIMEOUT;
+    uint64_t manifest_base = 0;
+    uint64_t uboot_start_addr;
+    uint64_t uboot_end_addr;
+    int ret = CPTRA_SUCCESS;
+    void *dtb_ptr = NULL;
+
+    /* Find manifest image */
+    ret = find_manifest_image(MANIFEST_SEARCH_START, MANIFEST_SEARCH_END,
+                              MANIFEST_SEARCH_STEP, &manifest_base);
+    if (ret != CPTRA_SUCCESS) {
+        return ret;
+    }
+
+    cptra_ctx->manifest_base = manifest_base;
+
+    ret = stor_init();
+    if (ret != CPTRA_SUCCESS) {
+        return ret;
+    }
+
+    if (img_info[CPTRA_UBOOT_FW_ID].size > 0) {
+        uboot_start_addr = img_info[CPTRA_UBOOT_FW_ID].offset;
+        uboot_end_addr = uboot_start_addr + img_info[CPTRA_UBOOT_FW_ID].size;
+        /* Try to find and load a valid U-Boot DTB */
+        dtb_ptr = find_and_load_appended_dtb(uboot_start_addr, uboot_end_addr);
+        if (dtb_ptr) {
+            get_reserved_memory(dtb_ptr, &reservedinfo);
+        }
+    } else {
+        uprintf("[reserved]: No U-Boot image found in caliptra flash image.\n");
+        uprintf("[reserved]: Using default reserved memory layout.\n");
+        dump_reserved_memory(&reservedinfo);
+    }
+
+    ret = cptra_verify_abb_loader();
+    if (ret != CPTRA_SUCCESS) {
+        return ret;
+    }
+
+    ret = cptra_load_abb_image();
+    if (ret != CPTRA_SUCCESS) {
+        return ret;
+    }
+
+    board_prepare_for_boot();
+
+    uprintf("Waiting release PSP ...\n");
+
+    while (!(readl(SCU0_CA35_REL) & BIT(0))) {
+        if (--timeout == 0) {
+            uprintf("ERROR: PSP release timeout!\n");
+            panic("");
+        }
+    }
+
+    uprintf("PSP is released ...\n");
+
+    *jump_addr = (uint64_t)readl(SCU0_CA35_RVBAR0) << 4;
+    uprintf("\nJumping to BL31 (Trusted Firmware-A) at 0x%lx\n\n", *jump_addr);
+
+    return ret;
+}
+
diff --git a/ast27x0/bootrom.ld b/ast27x0/bootrom.ld
index 92e65f4..96a2591 100644
--- a/ast27x0/bootrom.ld
+++ b/ast27x0/bootrom.ld
@@ -19,50 +19,50 @@
 
 MEMORY
 {
-	rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K
-	ram (arwx) : ORIGIN = 0x10000000, LENGTH = 128K
+    rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K
+    ram (arwx) : ORIGIN = 0x10000000, LENGTH = 128K
 }
 
 SECTIONS
 {
-	/* Vectors are loaded into ROM */
-	.text.vectors : {
-		__vectors_vma = .;
-		*(.text.vectors)
-		. = 0x100;
-		__vectors_end = .;
-	} >rom
-	/* The rest of the code follows the vectors.
-	 * The main code and read-only data, located in ROM
-	 */
-	.text : {
-		__text = .;
-		*(.text .text.*)
-		. = ALIGN(32);
-		*(.rodata .rodata.*)
-		. = ALIGN(32);
-		__etext = .;
-	} >rom
-	/*
-	 * Initialized data section
-	 * Data follows the code in ROM, and is copied in RAM.
-	 */
-	.data : {
-		__data = .;
-		*(.data .data.*)
-		. = ALIGN(32);
-		__edata = .;
-	} >ram AT>rom
-	__data_loadaddr = LOADADDR(.data);
+    /* Vectors are loaded into ROM */
+    .text.vectors : {
+        __vectors_vma = .;
+        *(.text.vectors)
+        . = 0x100;
+        __vectors_end = .;
+    } >rom
+    /* The rest of the code follows the vectors.
+     * The main code and read-only data, located in ROM
+     */
+    .text : {
+        __text = .;
+        *(.text .text.*)
+        . = ALIGN(32);
+        *(.rodata .rodata.*)
+        . = ALIGN(32);
+        __etext = .;
+    } >rom
+    /*
+     * Initialized data section
+     * Data follows the code in ROM, and is copied in RAM.
+     */
+    .data : {
+        __data = .;
+        *(.data .data.*)
+        . = ALIGN(32);
+        __edata = .;
+    } >ram AT>rom
+    __data_loadaddr = LOADADDR(.data);
 
-	/* Zero-initialized data (BSS) lives in RAM, after the data section. */
-	.bss : {
-		__bss_start = .;
-		*(.bss .bss.*)
-		. = ALIGN(32);
-		*(COMMON)
-		. = ALIGN(32);
-		__bss_end = .;
-		__end = .;
-	} >ram
+    /* Zero-initialized data (BSS) lives in RAM, after the data section. */
+    .bss : {
+        __bss_start = .;
+        *(.bss .bss.*)
+        . = ALIGN(32);
+        *(COMMON)
+        . = ALIGN(32);
+        __bss_end = .;
+        __end = .;
+    } >ram
 }
diff --git a/ast27x0/fmc_image.c b/ast27x0/fmc_image.c
index 86982cf..d495f3a 100644
--- a/ast27x0/fmc_image.c
+++ b/ast27x0/fmc_image.c
@@ -25,11 +25,12 @@
 #include <image.h>
 #include <fmc_image.h>
 #include <ssp_tsp.h>
+#include <platform_ast2700.h>
 
 #define DEBUG 0
 
-#define FIT_SEARCH_START (FMCCS0)
-#define FIT_SEARCH_END   (FMCCS0 + 0x400000)
+#define FIT_SEARCH_START (ASPEED_FMC_CS0_BASE)
+#define FIT_SEARCH_END   (FIT_SEARCH_START + 0x400000)
 #define FIT_SEARCH_STEP  0x10000
 
 extern void panic(const char *);
@@ -367,38 +368,6 @@
     return 0;
 }
 
-static void *load_dtb_after_fmc(uint64_t fmc_end, uint64_t end_addr)
-{
-    void *dram_dtb_addr = (void *)(uintptr_t)DRAM_ADDR;
-    const uint32_t *magic_ptr;
-    size_t copy_size;
-    uint64_t addr;
-
-    for (addr = ALIGN_UP(fmc_end, 4); addr + 4 < end_addr; addr += 4) {
-        /* Check for DTB magic number (aligned on 4-byte boundary) */
-        magic_ptr = (const uint32_t *)(uintptr_t)addr;
-        if (*magic_ptr != cpu_to_fdt32(FDT_MAGIC)) {
-            continue;
-        }
-
-        /* Copy from flash to DRAM for validation */
-        copy_size = end_addr - addr;
-        memcpy(dram_dtb_addr, (const void *)(uintptr_t)addr, copy_size);
-
-        /* Verify if the copied region is a valid DTB */
-        if (fdt_check_header(dram_dtb_addr) == 0) {
-            uprintf("Valid DTB found at 0x%lx, copied to 0x%lx\n",
-                    addr, (uint64_t)dram_dtb_addr);
-            return dram_dtb_addr;
-        } else {
-            uprintf("FDT_MAGIC at 0x%lx but invalid DTB header\n", addr);
-        }
-    }
-
-    uprintf("No valid DTB found between 0x%lx and 0x%lx\n", fmc_end, end_addr);
-    return NULL;
-}
-
 uint64_t load_fit_boot_image(void)
 {
     struct reserved_mem_info reservedinfo = {0};
@@ -417,8 +386,8 @@
         search_next_addr =  fmcinfo.payload_end;
 
         /* Try to find and load a valid SPL DTB between FMC and U-Boot FIT */
-        dtb_ptr = load_dtb_after_fmc(fmcinfo.payload_start,
-                                     fmcinfo.payload_end);
+        dtb_ptr = find_and_load_appended_dtb(fmcinfo.payload_start,
+                                             fmcinfo.payload_end);
         if (dtb_ptr) {
             get_reserved_memory(dtb_ptr, &reservedinfo);
         }
diff --git a/ast27x0/image.c b/ast27x0/image.c
index b61ab07..76e2c73 100644
--- a/ast27x0/image.c
+++ b/ast27x0/image.c
@@ -18,11 +18,14 @@
  */
 
 #include <string.h>
+#include <libfdt.h>
 #include <uart.h>
 #include <uart_console.h>
 #include <io.h>
 #include <image.h>
 #include <fmc_image.h>
+#include <manifest.h>
+#include <platform_ast2700.h>
 
 /*
  * This global struct is explicitly initialized, so it is placed in the .data
@@ -77,11 +80,54 @@
         return mcu_load_addr;
     }
 
-    return DRAM_ADDR + ((uint64_t)mcu_load_addr - 0x80000000);
+    return SYS_DRAM_BASE + ((uint64_t)mcu_load_addr - 0x80000000);
+}
+
+/*
+ * Scan the image tail for an appended Device Tree Blob (DTB),
+ * copy it to DRAM, and return its DRAM address if valid.
+ *
+ * This is used to locate SPL or U-Boot DTBs that are appended
+ * after their raw images.
+ */
+void *find_and_load_appended_dtb(uint64_t start_addr, uint64_t end_addr)
+{
+    void *dram_dtb_addr = (void *)(uintptr_t)SYS_DRAM_BASE;
+    const uint32_t *magic_ptr;
+    size_t copy_size;
+    uint64_t addr;
+
+    for (addr = ALIGN_UP(start_addr, 4); addr + 4 < end_addr; addr += 4) {
+        /* Check for DTB magic number (aligned on 4-byte boundary) */
+        magic_ptr = (const uint32_t *)(uintptr_t)addr;
+        if (*magic_ptr != cpu_to_fdt32(FDT_MAGIC)) {
+            continue;
+        }
+
+        /* Copy from flash to DRAM for validation */
+        copy_size = end_addr - addr;
+        memcpy(dram_dtb_addr, (const void *)(uintptr_t)addr, copy_size);
+
+        /* Verify if the copied region is a valid DTB */
+        if (fdt_check_header(dram_dtb_addr) == 0) {
+            uprintf("Valid DTB found at 0x%lx, copied to 0x%lx\n",
+                    addr, (uint64_t)dram_dtb_addr);
+            return dram_dtb_addr;
+        } else {
+            uprintf("FDT_MAGIC at 0x%lx but invalid DTB header\n", addr);
+        }
+    }
+
+    uprintf("No valid DTB found between 0x%lx and 0x%lx\n",
+            start_addr, end_addr);
+    return NULL;
 }
 
 uint64_t load_boot_image(void)
 {
+    int ret = CPTRA_SUCCESS;
+    uint64_t jump_addr;
+
     uart_aspeed_init(UART12);
     uart_console_register(&ucons);
     print_build_info();
@@ -89,7 +135,6 @@
     /*
      * Step 1: Try to boot using Caliptra Manifest
      *
-     *
      * If ALL steps above succeed:
      *   - Return BL31 entry address immediately
      *
@@ -101,6 +146,12 @@
      *   Caliptra Manifest boot is treated as the preferred path.
      *   FIT boot is kept as a compatibility mechanism.
      */
+    ret = load_manifest_boot_image(&jump_addr);
+    if (ret == CPTRA_SUCCESS) {
+        return jump_addr;
+    }
+
+    uprintf("Caliptra boot image load failed (%d)\n", ret);
 
     /*
      * Step 2: Fallback to FIT image boot
diff --git a/ast27x0/include/ast_loader.h b/ast27x0/include/ast_loader.h
new file mode 100644
index 0000000..2fe57a7
--- /dev/null
+++ b/ast27x0/include/ast_loader.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AST27X0_INCLUDE_AST_LOADER_H__
+#define __AST27X0_INCLUDE_AST_LOADER_H__
+
+#include <stddef.h>
+
+void generate_crc32_table(void);
+uint32_t calc_checksum(const uint8_t *data, size_t length);
+
+int ast_loader_read(uint64_t *dst, uint64_t src, uint32_t len);
+int ast_loader_load_image(uint32_t type, uint32_t *dst);
+int ast_loader_load_manifest_image(uint32_t type, uint32_t *dst);
+
+#endif /* __AST27X0_INCLUDE_AST_LOADER_H__ */
diff --git a/ast27x0/include/image.h b/ast27x0/include/image.h
index bfea535..5d48767 100644
--- a/ast27x0/include/image.h
+++ b/ast27x0/include/image.h
@@ -19,9 +19,7 @@
 
 #include <stdint.h>
 
-#define DRAM_ADDR   0x400000000ULL
-#define FMCCS0      0x100000000ULL
-
 uint64_t convert_mcu_addr_to_arm_dram(uint64_t mcu_load_addr);
+void *find_and_load_appended_dtb(uint64_t start_addr, uint64_t end_addr);
 
 #endif /* __AST27X0_INCLUDE_IMAGE_H__ */
diff --git a/ast27x0/include/io.h b/ast27x0/include/io.h
index 6813940..40d8297 100644
--- a/ast27x0/include/io.h
+++ b/ast27x0/include/io.h
@@ -35,4 +35,13 @@
 
 #define ALIGN_UP(x, align)  (((x) + ((align) - 1)) & ~((align) - 1))
 
+/* extract the Least Significant Bit */
+#define LSB_GET(value) ((value) & -(value))
+
+/* extract a bitfield element from value corresponding to the field mask */
+#define FIELD_GET(mask, value)  (((value) & (mask)) / LSB_GET(mask))
+
+/* calculate number of array elements */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
 #endif /* __AST27X0_INCLUDE_IO_H__ */
diff --git a/ast27x0/include/manifest.h b/ast27x0/include/manifest.h
new file mode 100644
index 0000000..48b9d7e
--- /dev/null
+++ b/ast27x0/include/manifest.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AST27X0_INCLUDE_MANIFEST_H__
+#define __AST27X0_INCLUDE_MANIFEST_H__
+
+#include <stdint.h>
+#include <image.h>
+
+#define CPTRA_FLASH_IMG_MAGIC (0x48534C46) /* 'FLSH' in little endian */
+#define CPTRA_MBCMD_SET_AUTH_MANIFEST (0x41544D4E) /* "ATMN" */
+#define CPTRA_IMC_ENTRY_COUNT 127
+
+enum {
+    CPTRA_MANIFEST_FW_ID = 0x00,
+    CPTRA_FMC_FW_ID = 0x01,
+    CPTRA_DDR4_IMEM_FW_ID = 0x02,
+    CPTRA_DDR4_DMEM_FW_ID = 0x03,
+    CPTRA_DDR4_2D_IMEM_FW_ID = 0x04,
+    CPTRA_DDR4_2D_DMEM_FW_ID = 0x05,
+    CPTRA_DDR5_IMEM_FW_ID = 0x06,
+    CPTRA_DDR5_DMEM_FW_ID = 0x07,
+    CPTRA_DP_FW_FW_ID = 0x08,
+    CPTRA_UEFI_FW_ID = 0x09,
+    CPTRA_ATF_FW_ID = 0x0a,
+    CPTRA_OPTEE_FW_ID = 0x0b,
+    CPTRA_UBOOT_FW_ID = 0x0c,
+    CPTRA_SSP_FW_ID = 0x0d,
+    CPTRA_TSP_FW_ID = 0x0e,
+};
+
+enum cptra_error_code {
+    CPTRA_SUCCESS = 0,
+    CPTRA_ERR_EXCEED_MEMORY_LIMIT,
+    CPTRA_ERR_INVALID_PARAMETER,
+    CPTRA_ERR_ABB_LOADER_NOT_READY,
+    CPTRA_ERR_READ_HDR,
+    CPTRA_ERR_HDR_MAGIC_MISMATCH,
+    CPTRA_ERR_EXCEED_MAX_IMG_COUNT,
+    CPTRA_ERR_READ_CHKSUM,
+    CPTRA_ERR_READ_IMG_INFO,
+    CPTRA_ERR_READ_FULL_IMG,
+    CPTRA_ERR_HDR_CHKSUM,
+    CPTRA_ERR_PAYLOAD_CHKSUM,
+    CPTRA_ERR_SOC_MANIFEST_NO_INFO,
+    CPTRA_ERR_SOC_MANIFEST_READ_ERROR,
+    CPTRA_ERR_SOC_MANIFEST_MAGIC_MISMATCH,
+    CPTRA_ERR_SOC_MANIFEST_VFY,
+    CPTRA_ERR_SOC_MANIFEST_ECC_SVN_VFY,
+    CPTRA_ERR_SOC_MANIFEST_LMS_SVN_VFY,
+    CPTRA_ERR_SOC_MANIFEST_VER_MISMATCH,
+    CPTRA_ERR_SHA384_CAL,
+    CPTRA_ERR_IMAGE_VFY_UNKNOWN_ERROR,
+    CPTRA_ERR_IMAGE_VFY_MBOX_ERROR,
+    CPTRA_ERR_IMAGE_VFY_FWID_MISMATCH,
+    CPTRA_ERR_IMAGE_VFY_HASH_MISMATCH,
+    CPTRA_ERR_IMAGE_LOAD_INVALID_PARAM,
+    CPTRA_ERR_IMAGE_LOAD,
+    CPTRA_ERR_IMAGE_READ,
+    CPTRA_ERR_IMAGE_SIZE_INVALID = -1,
+    CPTRA_ERR_IMAGE_OFFSET_INVALID = -2,
+};
+
+struct cptra_manifest_ime {
+    uint32_t fw_id;
+    uint32_t flags;
+    uint8_t digest[48]; /* SHA384 */
+};
+
+struct cptra_manifest_hdr {
+    uint32_t magic;
+    uint16_t hdr_ver;
+    uint16_t img_count;
+} __attribute__((__packed__, __aligned__(4)));
+
+struct cptra_checksum_info {
+    uint32_t hdr_checksum;
+    uint32_t payload_checksum;
+} __attribute__((__packed__));
+
+struct cptra_image_info {
+    uint32_t identifier;
+    uint32_t offset;
+    uint32_t size;
+} __attribute__((__packed__));
+
+struct cptra_manifest_aspeed_preamble {
+    uint32_t manifest_marker;
+    uint32_t preamble_size;
+    uint32_t manifest_version;
+    uint32_t manifest_sec_version;
+    uint32_t manifest_flags;
+    uint32_t manifest_vendor_ecc384_key[24];
+    uint32_t manifest_vendor_lms_key[12];
+    uint32_t manifest_vendor_ecc384_sig[24];
+    uint32_t manifest_vendor_LMS_sig[405];
+    uint32_t manifest_owner_ecc384_key[24];
+    uint32_t manifest_owner_lms_key[12];
+    uint32_t manifest_owner_ecc384_sig[24];
+    uint32_t manifest_owner_LMS_sig[405];
+    uint32_t manifest_owner_svn_ecc384_sig[24];
+    uint32_t manifest_owner_svn_LMS_sig[405];
+    uint32_t metadata_vendor_ecc384_sig[24];
+    uint32_t metadata_vendor_LMS_sig[405];
+    uint32_t metadata_owner_ecc384_sig[24];
+    uint32_t metadata_owner_LMS_sig[405];
+} __attribute__((__packed__, __aligned__(4)));
+
+struct cptra_manifest_aspeed_svn {
+    uint32_t ver;
+    uint32_t sec_ver;
+    uint32_t flags;
+    uint32_t manifest_owner_ecc384_key[24];
+    uint32_t manifest_owner_lms_key[12];
+} __attribute__((__packed__));
+
+struct cptra_soc_manifest {
+    struct cptra_manifest_aspeed_preamble preamble;
+    uint32_t ime_count;
+    struct cptra_manifest_ime imc[CPTRA_IMC_ENTRY_COUNT];
+} __attribute__((__packed__, __aligned__(4)));
+
+struct cptra_image_context {
+    struct cptra_manifest_hdr *hdr;
+    struct cptra_checksum_info *chk;
+    struct cptra_image_info *img_info;
+    struct cptra_soc_manifest *soc_manifest;
+    uint64_t manifest_base;
+};
+
+int cptra_verify_abb_loader(void);
+int cptra_load_abb_image(void);
+int cptra_get_abb_imginfo(uint32_t fw_id, uint32_t *ofst, uint32_t *size);
+void board_manifest_image_post_process(uint32_t fw_id);
+
+bool cptra_ime_loadable_image(struct cptra_manifest_ime *ime);
+char *cptra_ime_get_image_name(uint32_t fw_id);
+int cptra_ime_image_offset(struct cptra_image_context *ctx, uint32_t fw_id);
+int cptra_ime_image_size(struct cptra_image_context *ctx, uint32_t fw_id);
+uintptr_t cptra_ime_get_load_addr(uint32_t fw_id);
+
+int load_manifest_boot_image(uint64_t *jump_addr);
+struct cptra_image_context *cptra_get_ctx(void);
+
+#endif /* __AST27X0_INCLUDE_MANIFEST_H__ */
diff --git a/ast27x0/include/platform_ast2700.h b/ast27x0/include/platform_ast2700.h
new file mode 100644
index 0000000..a5e4ddf
--- /dev/null
+++ b/ast27x0/include/platform_ast2700.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AST27X0_INCLUDE_PLATFORM_AST2700_H__
+#define __AST27X0_INCLUDE_PLATFORM_AST2700_H__
+
+/* DRAM base address from the hardware's perspective */
+#define SYS_DRAM_BASE           0x400000000ULL
+#define ASPEED_FMC_CS0_BASE     0x100000000ULL
+
+#define SCU0_REG            0x12c02000
+#define SCU0_CA35_REL           (SCU0_REG + 0x10c)
+#define SCU0_CA35_RVBAR0        (SCU0_REG + 0x110)
+#define SCU0_CA35_RVBAR1        (SCU0_REG + 0x114)
+#define SCU0_CA35_RVBAR2        (SCU0_REG + 0x118)
+#define SCU0_CA35_RVBAR3        (SCU0_REG + 0x11c)
+#define SCU0_CPU_SMP_EP0        (SCU0_REG + 0x780)
+#define SCU0_CPU_SMP_EP1        (SCU0_REG + 0x788)
+#define SCU0_CPU_SMP_EP2        (SCU0_REG + 0x790)
+#define SCU0_CPU_SMP_EP3        (SCU0_REG + 0x798)
+
+#endif /* __AST27X0_INCLUDE_PLATFORM_AST2700_H__ */
diff --git a/ast27x0/include/ssp_tsp.h b/ast27x0/include/ssp_tsp.h
index 571cc0e..eff7197 100644
--- a/ast27x0/include/ssp_tsp.h
+++ b/ast27x0/include/ssp_tsp.h
@@ -18,6 +18,7 @@
 #define __AST27X0_INCLUDE_SSP_TSP_H__
 
 #include <stdint.h>
+#include <platform_ast2700.h>
 
 /* MAX visible range is 512M for SSP and TSP */
 #define MAX_I_D_ADDRESS (512 * 1024 * 1024)
@@ -30,7 +31,7 @@
 #define IPC_SSP_MEMORY_NODE "/reserved-memory/ipc-ssp-share"
 
 /* SCU */
-#define ASPEED_CPU_SCU_BASE 0x12C02000
+#define ASPEED_CPU_SCU_BASE SCU0_REG
 #define SCU_CPU_RST_SSP     BIT(30)
 #define SCU_CPU_RST2_TSP    BIT(9)
 
@@ -89,7 +90,7 @@
 
 struct mem_region {
     uint64_t addr;
-    uint32_t size;
+    uint64_t size;
 };
 
 struct reserved_mem_info {
diff --git a/ast27x0/include/stor.h b/ast27x0/include/stor.h
new file mode 100644
index 0000000..69543b9
--- /dev/null
+++ b/ast27x0/include/stor.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AST27X0_INCLUDE_STOR_H__
+#define __AST27X0_INCLUDE_STOR_H__
+
+#include <stdint.h>
+
+struct image_info {
+    int id;
+    uint64_t offset;
+    uint32_t size;
+};
+
+void stor_load(uint32_t type, uint32_t *dst, uint32_t *len);
+const struct image_info *stor_get_image_info_table(void);
+int stor_init(void);
+
+#endif /* __AST27X0_INCLUDE_STOR_H__ */
diff --git a/ast27x0/include/uart.h b/ast27x0/include/uart.h
index a227cb6..8f16bf7 100644
--- a/ast27x0/include/uart.h
+++ b/ast27x0/include/uart.h
@@ -33,28 +33,28 @@
 
 /** @brief Parity modes */
 enum uart_config_parity {
-	UART_CFG_PARITY_NONE,   /**< No parity */
-	UART_CFG_PARITY_ODD,    /**< Odd parity */
-	UART_CFG_PARITY_EVEN,   /**< Even parity */
-	UART_CFG_PARITY_MARK,   /**< Mark parity */
-	UART_CFG_PARITY_SPACE,  /**< Space parity */
+    UART_CFG_PARITY_NONE,   /**< No parity */
+    UART_CFG_PARITY_ODD,    /**< Odd parity */
+    UART_CFG_PARITY_EVEN,   /**< Even parity */
+    UART_CFG_PARITY_MARK,   /**< Mark parity */
+    UART_CFG_PARITY_SPACE,  /**< Space parity */
 };
 
 /** @brief Number of stop bits. */
 enum uart_config_stop_bits {
-	UART_CFG_STOP_BITS_0_5,  /**< 0.5 stop bit */
-	UART_CFG_STOP_BITS_1,    /**< 1 stop bit */
-	UART_CFG_STOP_BITS_1_5,  /**< 1.5 stop bits */
-	UART_CFG_STOP_BITS_2,    /**< 2 stop bits */
+    UART_CFG_STOP_BITS_0_5,  /**< 0.5 stop bit */
+    UART_CFG_STOP_BITS_1,    /**< 1 stop bit */
+    UART_CFG_STOP_BITS_1_5,  /**< 1.5 stop bits */
+    UART_CFG_STOP_BITS_2,    /**< 2 stop bits */
 };
 
 /** @brief Number of data bits. */
 enum uart_config_data_bits {
-	UART_CFG_DATA_BITS_5,    /**< 5 data bits */
-	UART_CFG_DATA_BITS_6,    /**< 6 data bits */
-	UART_CFG_DATA_BITS_7,    /**< 7 data bits */
-	UART_CFG_DATA_BITS_8,    /**< 8 data bits */
-	UART_CFG_DATA_BITS_9,    /**< 9 data bits */
+    UART_CFG_DATA_BITS_5,    /**< 5 data bits */
+    UART_CFG_DATA_BITS_6,    /**< 6 data bits */
+    UART_CFG_DATA_BITS_7,    /**< 7 data bits */
+    UART_CFG_DATA_BITS_8,    /**< 8 data bits */
+    UART_CFG_DATA_BITS_9,    /**< 9 data bits */
 };
 
 /**
@@ -65,21 +65,21 @@
  * In other cases, flow control is managed by hardware/driver.
  */
 enum uart_config_flow_control {
-	UART_CFG_FLOW_CTRL_NONE,
-	UART_CFG_FLOW_CTRL_RTS_CTS,
-	UART_CFG_FLOW_CTRL_DTR_DSR,
-	UART_CFG_FLOW_CTRL_RS485,
+    UART_CFG_FLOW_CTRL_NONE,
+    UART_CFG_FLOW_CTRL_RTS_CTS,
+    UART_CFG_FLOW_CTRL_DTR_DSR,
+    UART_CFG_FLOW_CTRL_RS485,
 };
 
 /**
  * @brief UART controller configuration structure
  */
 struct uart_config {
-	uint32_t baudrate;  /**< Baudrate setting in bps */
-	uint8_t parity;     /**< Parity bit, use @ref uart_config_parity */
-	uint8_t stop_bits;  /**< Stop bits, use @ref uart_config_stop_bits */
-	uint8_t data_bits;  /**< Data bits, use @ref uart_config_data_bits */
-	uint8_t flow_ctrl;  /**< Flow control setting, use @ref uart_config_flow_control */
+    uint32_t baudrate;  /**< Baudrate setting in bps */
+    uint8_t parity;     /**< Parity bit, use @ref uart_config_parity */
+    uint8_t stop_bits;  /**< Stop bits, use @ref uart_config_stop_bits */
+    uint8_t data_bits;  /**< Data bits, use @ref uart_config_data_bits */
+    uint8_t flow_ctrl;  /**< Flow control setting, use @ref uart_config_flow_control */
 };
 
 void uart_aspeed_poll_out(unsigned char c);
diff --git a/ast27x0/manifest.c b/ast27x0/manifest.c
new file mode 100644
index 0000000..3d2d475
--- /dev/null
+++ b/ast27x0/manifest.c
@@ -0,0 +1,279 @@
+/*
+ * Caliptra manifest image parsing and loading.
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <io.h>
+#include <manifest.h>
+#include <uart_console.h>
+#include <ast_loader.h>
+
+static struct cptra_image_context cptra_ctx;
+
+static int cptra_crc32_check(struct cptra_image_context *ctx)
+{
+    uint32_t hdr_crc32 = 0;
+
+    /* Check the flash image header crc checksum */
+    generate_crc32_table();
+    hdr_crc32 = calc_checksum((uint8_t *)ctx->hdr,
+                              sizeof(struct cptra_manifest_hdr));
+    if (hdr_crc32 != ctx->chk->hdr_checksum) {
+        uprintf("Check flash image header checksum ...fail.\n");
+        return CPTRA_ERR_HDR_CHKSUM;
+    }
+
+    uprintf("Check flash image checksum ...pass.\n");
+
+    return CPTRA_SUCCESS;
+}
+
+static int cptra_read_header(struct cptra_image_context *ctx)
+{
+    int ret = 0;
+    static struct cptra_manifest_hdr hdr = { 0 };
+
+    /* Read the manfiest header */
+    ret = ast_loader_read((uint64_t *)&hdr, ctx->manifest_base,
+                          sizeof(struct cptra_manifest_hdr));
+    if (ret) {
+        uprintf("Failed to read manifest header.\n");
+        return CPTRA_ERR_READ_HDR;
+    }
+
+    if (hdr.magic != CPTRA_FLASH_IMG_MAGIC) {
+        uprintf("Manifest header magic mismatch: 0x%x.\n", hdr.magic);
+        return CPTRA_ERR_HDR_MAGIC_MISMATCH;
+    }
+
+    if (hdr.img_count > CPTRA_IMC_ENTRY_COUNT) {
+        uprintf("Manifest image count exceeds maximum limit: %d.\n",
+                hdr.img_count);
+        return CPTRA_ERR_EXCEED_MAX_IMG_COUNT;
+    }
+
+    /* The image header is correct, keep it in context */
+    ctx->hdr = &hdr;
+
+    return CPTRA_SUCCESS;
+}
+
+static int cptra_read_chk_img_info(struct cptra_image_context *ctx)
+{
+    int ret = 0;
+    int img_num = ctx->hdr->img_count;
+    static uint8_t chk_img[sizeof(struct cptra_checksum_info) +
+                           sizeof(struct cptra_image_info) *
+                           CPTRA_IMC_ENTRY_COUNT] = { 0 };
+    uint32_t ofst = sizeof(struct cptra_manifest_hdr);
+    uint32_t size = 0;
+
+    if (img_num > CPTRA_IMC_ENTRY_COUNT) {
+        return CPTRA_ERR_EXCEED_MAX_IMG_COUNT;
+    }
+
+    size = sizeof(struct cptra_checksum_info);
+    size += sizeof(struct cptra_image_info) * img_num;
+    ret = ast_loader_read((uint64_t *)chk_img, ctx->manifest_base + ofst,
+                          size);
+    if (ret) {
+        return CPTRA_ERR_READ_IMG_INFO;
+    }
+
+    ctx->chk = (struct cptra_checksum_info *)chk_img;
+    ctx->img_info = (struct cptra_image_info *)(chk_img +
+                     sizeof(struct cptra_checksum_info));
+
+    return CPTRA_SUCCESS;
+}
+
+static int cptra_read_abb_header(struct cptra_image_context *ctx)
+{
+    int ret = 0;
+
+    /* header, checksum, img_info has been read exit directly */
+    if (ctx->hdr && ctx->chk && ctx->img_info) {
+        return ret;
+    }
+
+    ret = cptra_read_header(ctx);
+    if (ret) {
+        goto fail;
+    }
+
+    ret = cptra_read_chk_img_info(ctx);
+    if (ret) {
+        goto fail;
+    }
+
+    ret = cptra_crc32_check(ctx);
+    if (ret) {
+        goto fail;
+    }
+
+fail:
+    return ret;
+}
+
+static int cptra_read_abb_soc_manifest(struct cptra_image_context *ctx)
+{
+    int ret = 0;
+    static struct cptra_soc_manifest soc_manifest;
+
+    ret = ast_loader_load_image(CPTRA_MANIFEST_FW_ID,
+                                (uint32_t *)&soc_manifest);
+    if (ret) {
+        return CPTRA_ERR_SOC_MANIFEST_READ_ERROR;
+    }
+
+    if (soc_manifest.preamble.manifest_marker !=
+        CPTRA_MBCMD_SET_AUTH_MANIFEST)
+        return CPTRA_ERR_SOC_MANIFEST_MAGIC_MISMATCH;
+
+    ctx->soc_manifest = &soc_manifest;
+
+    uprintf("ver: %x, flags: %x, soc_ver: %x\n",
+        soc_manifest.preamble.manifest_version,
+        soc_manifest.preamble.manifest_flags,
+        soc_manifest.preamble.manifest_sec_version);
+    uprintf("manfiest vendor pubk: 0x%08x\n",
+        soc_manifest.preamble.manifest_vendor_ecc384_key[0]);
+    uprintf("manfiest owner pubk: 0x%08x\n",
+        soc_manifest.preamble.manifest_owner_ecc384_key[0]);
+
+    return CPTRA_SUCCESS;
+}
+
+static void cptra_deinit_abb_loader(int ret)
+{
+    if (ret && cptra_ctx.hdr) {
+        memset(cptra_ctx.hdr, 0, sizeof(struct cptra_manifest_hdr));
+    }
+
+    if (ret && cptra_ctx.chk) {
+        memset(cptra_ctx.chk, 0, sizeof(struct cptra_checksum_info));
+    }
+
+    if (ret && cptra_ctx.img_info) {
+        memset(cptra_ctx.img_info, 0,
+               sizeof(struct cptra_image_info) * CPTRA_IMC_ENTRY_COUNT);
+    }
+
+    if (ret && cptra_ctx.soc_manifest) {
+        memset(cptra_ctx.soc_manifest, 0,
+               sizeof(struct cptra_soc_manifest));
+    }
+}
+
+static int cptra_init_abb_loader(void)
+{
+    int ret = 0;
+
+    if (cptra_ctx.hdr && cptra_ctx.img_info && cptra_ctx.chk) {
+        return ret;
+    }
+
+    ret = cptra_read_abb_header(&cptra_ctx);
+    uprintf("Read abb header... %s (0x%x)\n", ret ? "fail" : "pass", ret);
+
+    cptra_deinit_abb_loader(ret);
+    return ret;
+}
+
+int cptra_verify_abb_loader(void)
+{
+    int ret = 0;
+
+    if (cptra_ctx.soc_manifest) {
+        return ret;
+    }
+
+    ret = cptra_read_abb_soc_manifest(&cptra_ctx);
+    uprintf("Read soc manifest... %s (0x%x)\n", ret ? "fail" : "pass", ret);
+
+    cptra_deinit_abb_loader(ret);
+    return ret;
+}
+
+int cptra_load_abb_image(void)
+{
+    int ret = 0;
+    uint32_t *load_addr = 0;
+    struct cptra_soc_manifest *man = cptra_ctx.soc_manifest;
+    struct cptra_manifest_ime *ime = &man->imc[0];
+
+    if (!man) {
+        return CPTRA_ERR_ABB_LOADER_NOT_READY;
+    }
+
+    for (ime = &man->imc[0]; ime < man->imc + man->ime_count && !ret; ime++) {
+        /* Check the whether ime denote image should be loaded */
+        if (!cptra_ime_loadable_image(ime)) {
+            continue;
+        }
+
+        load_addr = (uint32_t *)cptra_ime_get_load_addr(ime->fw_id);
+        if (!load_addr) {
+            continue;
+        }
+
+        ret = ast_loader_load_manifest_image(ime->fw_id, load_addr);
+        uprintf("Load %s image... %s (0x%x)\n",
+                cptra_ime_get_image_name(ime->fw_id),
+                ret ? "fail" : "pass", ret);
+
+        if (!ret) {
+            board_manifest_image_post_process(ime->fw_id);
+        }
+    }
+
+    cptra_deinit_abb_loader(ret);
+    return ret;
+}
+
+int cptra_get_abb_imginfo(uint32_t fw_id, uint32_t *ofst, uint32_t *size)
+{
+    int ret = 0;
+    int image_offset = 0;
+    int image_size = 0;
+
+    ret = cptra_init_abb_loader();
+    if (ret) {
+        return ret;
+    }
+
+    image_offset = cptra_ime_image_offset(&cptra_ctx, fw_id);
+    if (image_offset < 0) {
+        return CPTRA_ERR_IMAGE_READ;
+    }
+
+    image_size = cptra_ime_image_size(&cptra_ctx, fw_id);
+    if (image_size < 0) {
+        return CPTRA_ERR_IMAGE_READ;
+    }
+
+    *ofst = image_offset;
+    *size = image_size;
+
+    return CPTRA_SUCCESS;
+}
+
+struct cptra_image_context *cptra_get_ctx(void)
+{
+    return &cptra_ctx;
+}
+
diff --git a/ast27x0/manifest_image.c b/ast27x0/manifest_image.c
new file mode 100644
index 0000000..f7f2560
--- /dev/null
+++ b/ast27x0/manifest_image.c
@@ -0,0 +1,219 @@
+/*
+ * Caliptra manifest image lookup and load address helpers.
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <io.h>
+#include <manifest.h>
+#include <uart_console.h>
+
+/* Define caliptra image identifier */
+#define CPTRA_SOC_MANIFEST_HDR_ID (0x0002)
+#define CPTRA_FMC_HDR_ID          (0x0003)
+#define CPTRA_DDR4_IMEM_HDR_ID    (0x1000)
+#define CPTRA_DDR4_DMEM_HDR_ID    (0x1001)
+#define CPTRA_DDR4_2D_IMEM_HDR_ID (0x1002)
+#define CPTRA_DDR4_2D_DMEM_HDR_ID (0x1003)
+#define CPTRA_DDR5_IMEM_HDR_ID    (0x1004)
+#define CPTRA_DDR5_DMEM_HDR_ID    (0x1005)
+#define CPTRA_DP_FW_HDR_ID        (0x1006)
+#define CPTRA_UEFI_HDR_ID         (0x1007)
+#define CPTRA_ATF_HDR_ID          (0x1008)
+#define CPTRA_OPTEE_HDR_ID        (0x1009)
+#define CPTRA_UBOOT_HDR_ID        (0x100A)
+#define CPTRA_SSP_HDR_ID          (0x100B)
+#define CPTRA_TSP_HDR_ID          (0x100C)
+
+/* Define caliptra image load address */
+#define CPTRA_NO_LOAD_ADDR    (0x00000000)
+#define CPTRA_FMC_LOAD_ADDR   (0x14b80000)
+#define CPTRA_ATF_LOAD_ADDR   (0xb0000000)
+#define CPTRA_OPTEE_LOAD_ADDR (0xb0080000)
+#define CPTRA_UBOOT_LOAD_ADDR (0x80000000)
+#define CPTRA_SSP_LOAD_ADDR   (0xac000000)
+#define CPTRA_TSP_LOAD_ADDR   (0xae000000)
+
+/* Define caliptra image loadable property */
+#define CPTRA_LOADABLE_MASK GENMASK(31, 30)
+#define CPTRA_BOOTMCU_LOADABLE (1)
+#define CPTRA_SSP_LOADABLE (2)
+
+struct cptra_load_image {
+    char *name;
+    uint32_t identifier;
+    uint32_t fw_id;
+    uintptr_t load_addr;
+};
+
+static struct cptra_load_image image_list[] = {
+    { "manifest",
+      CPTRA_SOC_MANIFEST_HDR_ID, CPTRA_MANIFEST_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "mcu_fmc",
+      CPTRA_FMC_HDR_ID, CPTRA_FMC_FW_ID, CPTRA_FMC_LOAD_ADDR},
+    { "ddr4_imem",
+      CPTRA_DDR4_IMEM_HDR_ID, CPTRA_DDR4_IMEM_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "ddr4_dmem",
+      CPTRA_DDR4_DMEM_HDR_ID, CPTRA_DDR4_DMEM_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "ddr4_2d_imem",
+      CPTRA_DDR4_2D_IMEM_HDR_ID, CPTRA_DDR4_2D_IMEM_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "ddr4_2d_dmem",
+      CPTRA_DDR4_2D_DMEM_HDR_ID, CPTRA_DDR4_2D_DMEM_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "ddr5_imem",
+      CPTRA_DDR5_IMEM_HDR_ID, CPTRA_DDR5_IMEM_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "ddr5_dmem",
+      CPTRA_DDR5_DMEM_HDR_ID, CPTRA_DDR5_DMEM_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "dp_fw",
+      CPTRA_DP_FW_HDR_ID, CPTRA_DP_FW_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "uefi",
+      CPTRA_UEFI_HDR_ID, CPTRA_UEFI_FW_ID, CPTRA_NO_LOAD_ADDR},
+    { "atf",
+      CPTRA_ATF_HDR_ID, CPTRA_ATF_FW_ID, CPTRA_ATF_LOAD_ADDR},
+    { "optee",
+      CPTRA_OPTEE_HDR_ID, CPTRA_OPTEE_FW_ID, CPTRA_OPTEE_LOAD_ADDR},
+    { "uboot",
+      CPTRA_UBOOT_HDR_ID, CPTRA_UBOOT_FW_ID, CPTRA_UBOOT_LOAD_ADDR},
+    { "ssp",
+      CPTRA_SSP_HDR_ID, CPTRA_SSP_FW_ID, CPTRA_SSP_LOAD_ADDR},
+    { "tsp",
+      CPTRA_TSP_HDR_ID, CPTRA_TSP_FW_ID, CPTRA_TSP_LOAD_ADDR},
+};
+
+static struct cptra_image_info *cptra_find_image_info(
+            struct cptra_image_context *ctx, uint32_t identifier)
+{
+    bool match = false;
+    uint32_t img_num = ctx->hdr->img_count;
+    struct cptra_image_info *img_info = ctx->img_info;
+
+    if (img_num > CPTRA_IMC_ENTRY_COUNT + 2) {
+        return NULL;
+    }
+
+    for (img_info = ctx->img_info;
+         img_info < ctx->img_info + img_num;
+         img_info++) {
+        if (img_info->identifier == identifier) {
+            match = true;
+            break;
+        }
+    }
+
+    return match ? img_info : NULL;
+}
+
+static int cptra_img_info_get_offset(struct cptra_image_context *ctx,
+                                     uint32_t identifier)
+{
+    struct cptra_image_info *img_info = NULL;
+
+    img_info = cptra_find_image_info(ctx, identifier);
+
+    return img_info ? (int)img_info->offset : CPTRA_ERR_IMAGE_OFFSET_INVALID;
+}
+
+static int cptra_img_info_get_size(struct cptra_image_context *ctx,
+                                   uint32_t identifier)
+{
+    struct cptra_image_info *img_info = NULL;
+
+    img_info = cptra_find_image_info(ctx, identifier);
+
+    return img_info ? (int)img_info->size : CPTRA_ERR_IMAGE_SIZE_INVALID;
+}
+
+static struct cptra_load_image *cptra_find_load_image(uint32_t fw_id)
+{
+    int i = 0;
+    int match = -1;
+
+    for (i = 0; i < (int)ARRAY_SIZE(image_list) && match == -1; i++) {
+        match = image_list[i].fw_id == fw_id ? i : match;
+    }
+
+    return match != -1 ? &image_list[match] : NULL;
+}
+
+bool cptra_ime_loadable_image(struct cptra_manifest_ime *ime)
+{
+    if (!ime) {
+        return false;
+    }
+
+    if (FIELD_GET(CPTRA_LOADABLE_MASK, ime->flags) == CPTRA_BOOTMCU_LOADABLE) {
+        return true;
+    }
+
+    return false;
+}
+
+int cptra_ime_image_offset(struct cptra_image_context *ctx, uint32_t fw_id)
+{
+    struct cptra_load_image *img = NULL;
+
+    if (!ctx) {
+        return CPTRA_ERR_IMAGE_OFFSET_INVALID;
+    }
+
+    img = cptra_find_load_image(fw_id);
+    if (!img) {
+        uprintf("Cannot find image with fw_id 0x%x.\n", fw_id);
+        return CPTRA_ERR_IMAGE_OFFSET_INVALID;
+    }
+
+    return cptra_img_info_get_offset(ctx, img->identifier);
+}
+
+char *cptra_ime_get_image_name(uint32_t fw_id)
+{
+    struct cptra_load_image *img = NULL;
+
+    img = cptra_find_load_image(fw_id);
+
+    return img ? img->name : "Unknown";
+}
+
+int cptra_ime_image_size(struct cptra_image_context *ctx, uint32_t fw_id)
+{
+    struct cptra_load_image *img = NULL;
+
+    if (!ctx) {
+        return CPTRA_ERR_IMAGE_SIZE_INVALID;
+    }
+
+    img = cptra_find_load_image(fw_id);
+    if (!img) {
+        uprintf("Cannot find image with fw_id 0x%x.\n", fw_id);
+        return CPTRA_ERR_IMAGE_SIZE_INVALID;
+    }
+
+    return cptra_img_info_get_size(ctx, img->identifier);
+}
+
+uintptr_t cptra_ime_get_load_addr(uint32_t fw_id)
+{
+    struct cptra_load_image *img = NULL;
+
+    img = cptra_find_load_image(fw_id);
+    if (!img) {
+        uprintf("Cannot find image with fw_id 0x%x.\n", fw_id);
+        return (uintptr_t)NULL;
+    }
+
+    return img->load_addr;
+}
+
diff --git a/ast27x0/ssp_tsp.c b/ast27x0/ssp_tsp.c
index 6bbeef0..7d790a3 100644
--- a/ast27x0/ssp_tsp.c
+++ b/ast27x0/ssp_tsp.c
@@ -19,6 +19,7 @@
 #include <uart_console.h>
 #include <image.h>
 #include <ssp_tsp.h>
+#include <platform_ast2700.h>
 
 void get_reserved_memory(const void *fdt_blob, struct reserved_mem_info *info)
 {
@@ -35,30 +36,69 @@
 
     const fdt32_t *reg;
     const char *path;
+    int size_cells;
+    int addr_cells;
+    uint64_t addr;
+    uint64_t size;
     int offset;
+    int parent;
     size_t i;
+    int len;
+    int j;
 
-    for (i = 0; i < sizeof(nodes) / sizeof(nodes[0]); i++) {
+    for (i = 0; i < ARRAY_SIZE(nodes); i++) {
         path = nodes[i].path;
+        nodes[i].region->addr = 0;
+        nodes[i].region->size = 0;
+        addr = 0;
+        size = 0;
 
         offset = fdt_path_offset(fdt_blob, path);
         if (offset < 0) {
             uprintf("Cannot find node %s in the device tree.\n", path);
-            nodes[i].region->addr = 0;
-            nodes[i].region->size = 0;
             continue;
         }
 
-        reg = fdt_getprop(fdt_blob, offset, "reg", NULL);
+        parent = fdt_parent_offset(fdt_blob, offset);
+        addr_cells = fdt_address_cells(fdt_blob, parent);
+        size_cells = fdt_size_cells(fdt_blob, parent);
+
+        if (addr_cells < 0 || addr_cells > 2) {
+            uprintf("unsupported addr_cells: %d\n", addr_cells);
+            continue;
+        }
+
+        if (size_cells < 0 || size_cells > 2) {
+            uprintf("unsupported size_cells: %d\n", size_cells);
+            continue;
+        }
+
+        reg = fdt_getprop(fdt_blob, offset, "reg", &len);
         if (!reg) {
             uprintf("No reg property found in %s\n", path);
-            nodes[i].region->addr = 0;
-            nodes[i].region->size = 0;
             continue;
         }
 
-        nodes[i].region->addr = fdt32_to_cpu(reg[0]);
-        nodes[i].region->size = fdt32_to_cpu(reg[1]);
+        if (len < (addr_cells + size_cells) * (int)sizeof(fdt32_t)) {
+            uprintf("reg too short in %s (len=%d)\n", path, len);
+            continue;
+        }
+
+        for (j = 0; j < addr_cells; j++) {
+            addr = (addr << 32) | (uint64_t)fdt32_to_cpu(reg[j]);
+        }
+
+        for (j = 0; j < size_cells; j++) {
+            size = (size << 32) | (uint64_t)fdt32_to_cpu(reg[j + addr_cells]);
+        }
+
+        /* convert to Arm view */
+        if (addr_cells == 1) {
+            addr = convert_mcu_addr_to_arm_dram(addr);
+        }
+
+        nodes[i].region->addr = addr;
+        nodes[i].region->size = size;
 
         uprintf("[reserved] %s base: 0x%lx  size: 0x%lx\n", path,
                 nodes[i].region->addr, nodes[i].region->size);
@@ -70,6 +110,12 @@
     struct ast2700_scu0 *scu;
     uint32_t reg_val;
 
+    if (load_addr != info->ssp.addr) {
+        uprintf("load address %lx doesn't match SSP reserved memory %lx\n",
+                load_addr, info->ssp.addr);
+        return 1;
+    }
+
     scu = (struct ast2700_scu0 *)ASPEED_CPU_SCU_BASE;
 
     reg_val = readl((void *)&scu->ssp_ctrl_0);
@@ -113,7 +159,7 @@
     writel(TCM_SIZE, (void *)&scu->ssp_tcm_size);
 
     /* Configure physical AHB remap: through H2M, mapped to SYS_DRAM_BASE */
-    writel((uint32_t)(DRAM_ADDR >> 4), (void *)&scu->ssp_ctrl_1);
+    writel((uint32_t)(SYS_DRAM_BASE >> 4), (void *)&scu->ssp_ctrl_1);
 
     /* Configure physical DRAM remap */
     reg_val = (uint32_t)(load_addr >> 4);
@@ -151,6 +197,12 @@
     struct ast2700_scu0 *scu;
     uint32_t reg_val;
 
+    if (load_addr != info->tsp.addr) {
+        uprintf("load address %lx doesn't match TSP reserved memory %lx\n",
+                load_addr, info->tsp.addr);
+        return 1;
+    }
+
     scu = (struct ast2700_scu0 *)ASPEED_CPU_SCU_BASE;
 
     reg_val = readl((void *)&scu->tsp_ctrl_0);
diff --git a/ast27x0/stor.c b/ast27x0/stor.c
new file mode 100644
index 0000000..832a72c
--- /dev/null
+++ b/ast27x0/stor.c
@@ -0,0 +1,106 @@
+/*
+ * Caliptra manifest storage helpers.
+ *
+ * Query firmware image offset and size from Caliptra Manifest and provide
+ * stor_load() to load images from manifest storage into destination memory.
+ *
+ * Copyright (C) 2026 ASPEED Technology Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <uart_console.h>
+#include <stor.h>
+#include <manifest.h>
+
+#define DEBUG 0
+
+static struct image_info img_info[] = {
+    {CPTRA_MANIFEST_FW_ID, 0, 0},
+    {CPTRA_FMC_FW_ID, 0, 0},
+    {CPTRA_DDR4_IMEM_FW_ID, 0, 0},
+    {CPTRA_DDR4_DMEM_FW_ID, 0, 0},
+    {CPTRA_DDR4_2D_IMEM_FW_ID, 0, 0},
+    {CPTRA_DDR4_2D_DMEM_FW_ID, 0, 0},
+    {CPTRA_DDR5_IMEM_FW_ID, 0, 0},
+    {CPTRA_DDR5_DMEM_FW_ID, 0, 0},
+    {CPTRA_DP_FW_FW_ID, 0, 0},
+    {CPTRA_UEFI_FW_ID, 0, 0},
+    {CPTRA_ATF_FW_ID, 0, 0},
+    {CPTRA_OPTEE_FW_ID, 0, 0},
+    {CPTRA_UBOOT_FW_ID, 0, 0},
+    {CPTRA_SSP_FW_ID, 0, 0},
+    {CPTRA_TSP_FW_ID, 0, 0},
+};
+
+static int stor_get_image_info(struct image_info *info)
+{
+    struct cptra_image_context *cptra_ctx = cptra_get_ctx();
+    uint32_t offset, sz;
+    int err;
+
+    if (!info) {
+        uprintf("Image info pointer is NULL.\n");
+        return -1;
+    }
+
+    for (int i = 0; i < CPTRA_TSP_FW_ID + 1; i++) {
+        /* Call cptra's service to get the image info */
+        err = cptra_get_abb_imginfo(info[i].id, &offset, &sz);
+        if (err) {
+            uprintf("Failed to get image info for ID %d, err=%d\n",
+                    info[i].id, err);
+            return err;
+        }
+
+        info[i].offset = offset + cptra_ctx->manifest_base;
+        info[i].size = sz;
+#if DEBUG
+        uprintf("%s: offset=0x%lx, size=%d\n",
+                cptra_ime_get_image_name(info[i].id),
+                info[i].offset, info[i].size);
+#endif
+    }
+
+    return err;
+}
+
+void stor_load(uint32_t type, uint32_t *dst, uint32_t *len)
+{
+    uint64_t src;
+    uint64_t dram_addr;
+    uint32_t sz = 0;
+
+    src = img_info[type].offset;
+    sz = img_info[type].size;
+
+    /* convert to Arm view */
+    dram_addr = convert_mcu_addr_to_arm_dram((uint64_t)dst);
+    uprintf("%s: %s, dst=0x%lx, src=0x%lx, len=%d\n", __func__,
+            cptra_ime_get_image_name(img_info[type].id),
+            dram_addr, (unsigned long long)src, sz);
+    memcpy((void *)(uintptr_t)dram_addr, (void *)(uintptr_t)src, sz);
+    *len = sz;
+}
+
+const struct image_info *stor_get_image_info_table(void)
+{
+    return img_info;
+}
+
+int stor_init(void)
+{
+    return stor_get_image_info(img_info);
+}
+