mirror of https://github.com/wolfSSL/wolfBoot.git
282 lines
8.3 KiB
C
282 lines
8.3 KiB
C
/* unit-fit-fpga.c
|
|
*
|
|
* Unit tests for the FIT-image FPGA-subimage discovery added to
|
|
* fit_find_images() and fit_get_compatible() in src/fdt.c. The tests
|
|
* build minimal FIT blobs in memory (a tiny DTB writer below) and check
|
|
* that the fpga node is found via the configuration "fpga" property, via
|
|
* the type=="fpga" fallback, and that it is NULL when absent.
|
|
*
|
|
* Copyright (C) 2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <check.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../../include/fdt.h"
|
|
|
|
/* fdt.c calls wolfBoot_printf; supply a silent stub. */
|
|
void wolfBoot_printf(const char *fmt, ...)
|
|
{
|
|
(void)fmt;
|
|
}
|
|
|
|
/* Pull in the production code under test (gated on WOLFBOOT_FDT). */
|
|
#include "../../src/fdt.c"
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Minimal big-endian DTB writer (FDT_* tokens come from fdt.h) */
|
|
/* ------------------------------------------------------------------------- */
|
|
static uint8_t g_struct[4096];
|
|
static uint32_t g_struct_len;
|
|
static char g_strings[1024];
|
|
static uint32_t g_strings_len;
|
|
static uint8_t g_blob[8192] __attribute__((aligned(4)));
|
|
|
|
static void be32_put(uint8_t* p, uint32_t v)
|
|
{
|
|
p[0] = (uint8_t)(v >> 24);
|
|
p[1] = (uint8_t)(v >> 16);
|
|
p[2] = (uint8_t)(v >> 8);
|
|
p[3] = (uint8_t)(v);
|
|
}
|
|
|
|
static void struct_u32(uint32_t v)
|
|
{
|
|
be32_put(&g_struct[g_struct_len], v);
|
|
g_struct_len += 4;
|
|
}
|
|
|
|
/* Append name+nul, 4-byte aligned (used by node names). */
|
|
static void struct_str(const char* s)
|
|
{
|
|
uint32_t n = (uint32_t)strlen(s) + 1U;
|
|
memcpy(&g_struct[g_struct_len], s, n);
|
|
g_struct_len += n;
|
|
while (g_struct_len & 3U) {
|
|
g_struct[g_struct_len++] = 0;
|
|
}
|
|
}
|
|
|
|
/* Intern a property name into the strings block, return its offset. */
|
|
static uint32_t strings_off(const char* name)
|
|
{
|
|
uint32_t n = (uint32_t)strlen(name) + 1U;
|
|
uint32_t off = g_strings_len;
|
|
memcpy(&g_strings[g_strings_len], name, n);
|
|
g_strings_len += n;
|
|
return off;
|
|
}
|
|
|
|
static void node_begin(const char* name)
|
|
{
|
|
struct_u32(FDT_BEGIN_NODE);
|
|
struct_str(name);
|
|
}
|
|
|
|
static void node_end(void)
|
|
{
|
|
struct_u32(FDT_END_NODE);
|
|
}
|
|
|
|
static void prop_str(const char* name, const char* val)
|
|
{
|
|
uint32_t vlen = (uint32_t)strlen(val) + 1U;
|
|
struct_u32(FDT_PROP);
|
|
struct_u32(vlen);
|
|
struct_u32(strings_off(name));
|
|
memcpy(&g_struct[g_struct_len], val, vlen);
|
|
g_struct_len += vlen;
|
|
while (g_struct_len & 3U) {
|
|
g_struct[g_struct_len++] = 0;
|
|
}
|
|
}
|
|
|
|
/* Assemble the header + reserve map + struct + strings into g_blob. */
|
|
/* Returns a validated view of the built blob, or NULL if it did not
|
|
* pass fdt_open() - which is itself a useful assertion for these
|
|
* fixtures. */
|
|
static fdt_ctx* fit_finish(void)
|
|
{
|
|
static fdt_ctx ctx;
|
|
uint32_t off_rsv = 40; /* header is 40 bytes (v17) */
|
|
uint32_t off_struct = off_rsv + 16; /* one terminating rsv entry */
|
|
uint32_t off_strings = off_struct + g_struct_len;
|
|
uint32_t total = off_strings + g_strings_len;
|
|
uint8_t* h = g_blob;
|
|
|
|
memset(g_blob, 0, sizeof(g_blob));
|
|
be32_put(h + 0, 0xD00DFEEDU); /* magic */
|
|
be32_put(h + 4, total); /* totalsize */
|
|
be32_put(h + 8, off_struct); /* off_dt_struct */
|
|
be32_put(h + 12, off_strings); /* off_dt_strings */
|
|
be32_put(h + 16, off_rsv); /* off_mem_rsvmap */
|
|
be32_put(h + 20, 17); /* version */
|
|
be32_put(h + 24, 16); /* last_comp_version */
|
|
be32_put(h + 28, 0); /* boot_cpuid_phys */
|
|
be32_put(h + 32, g_strings_len); /* size_dt_strings */
|
|
be32_put(h + 36, g_struct_len); /* size_dt_struct */
|
|
/* reserve map terminator already zeroed */
|
|
memcpy(g_blob + off_struct, g_struct, g_struct_len);
|
|
memcpy(g_blob + off_strings, g_strings, g_strings_len);
|
|
if (fdt_open(&ctx, g_blob, (uint32_t)sizeof(g_blob)) != 0) {
|
|
return NULL;
|
|
}
|
|
return &ctx;
|
|
}
|
|
|
|
static void fit_reset(void)
|
|
{
|
|
g_struct_len = 0;
|
|
g_strings_len = 0;
|
|
memset(g_struct, 0, sizeof(g_struct));
|
|
memset(g_strings, 0, sizeof(g_strings));
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Tests */
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Configuration node carries an explicit "fpga" reference. */
|
|
START_TEST(test_fit_fpga_via_config)
|
|
{
|
|
const char *kernel = NULL, *flat_dt = NULL, *ramdisk = NULL, *fpga = NULL;
|
|
const char* comp;
|
|
fdt_ctx* fit;
|
|
|
|
fit_reset();
|
|
struct_u32(FDT_BEGIN_NODE); struct_str(""); /* root */
|
|
node_begin("images");
|
|
node_begin("fpga-1");
|
|
prop_str("type", "fpga");
|
|
prop_str("compatible", "u-boot,zynqmp-fpga-ddrauth");
|
|
node_end();
|
|
node_end();
|
|
node_begin("configurations");
|
|
prop_str("default", "conf1");
|
|
node_begin("conf1");
|
|
prop_str("kernel", "kernel-1");
|
|
prop_str("fpga", "fpga-1");
|
|
node_end();
|
|
node_end();
|
|
node_end();
|
|
struct_u32(FDT_END);
|
|
fit = fit_finish();
|
|
|
|
fit_find_images(fit, &kernel, &flat_dt, &ramdisk, &fpga);
|
|
ck_assert_ptr_nonnull(fpga);
|
|
ck_assert_str_eq(fpga, "fpga-1");
|
|
|
|
comp = fit_get_compatible(fit, fpga);
|
|
ck_assert_ptr_nonnull(comp);
|
|
ck_assert_str_eq(comp, "u-boot,zynqmp-fpga-ddrauth");
|
|
}
|
|
END_TEST
|
|
|
|
/* No "fpga" config property: fall back to a node with type=="fpga". */
|
|
START_TEST(test_fit_fpga_via_type_fallback)
|
|
{
|
|
const char *fpga = NULL;
|
|
fdt_ctx* fit;
|
|
|
|
fit_reset();
|
|
struct_u32(FDT_BEGIN_NODE); struct_str("");
|
|
node_begin("images");
|
|
node_begin("the-bitstream");
|
|
prop_str("type", "fpga");
|
|
node_end();
|
|
node_end();
|
|
node_end();
|
|
struct_u32(FDT_END);
|
|
fit = fit_finish();
|
|
|
|
fit_find_images(fit, NULL, NULL, NULL, &fpga);
|
|
ck_assert_ptr_nonnull(fpga);
|
|
ck_assert_str_eq(fpga, "the-bitstream");
|
|
}
|
|
END_TEST
|
|
|
|
/* No fpga subimage at all: pfpga must be left NULL. */
|
|
START_TEST(test_fit_fpga_absent)
|
|
{
|
|
const char *fpga = (const char*)0x1; /* poison */
|
|
fdt_ctx* fit;
|
|
|
|
fit_reset();
|
|
struct_u32(FDT_BEGIN_NODE); struct_str("");
|
|
node_begin("images");
|
|
node_begin("kernel-1");
|
|
prop_str("type", "kernel");
|
|
node_end();
|
|
node_end();
|
|
node_end();
|
|
struct_u32(FDT_END);
|
|
fit = fit_finish();
|
|
|
|
fit_find_images(fit, NULL, NULL, NULL, &fpga);
|
|
ck_assert_ptr_null(fpga);
|
|
}
|
|
END_TEST
|
|
|
|
/* fit_get_compatible returns NULL when the property is absent. */
|
|
START_TEST(test_fit_compatible_absent)
|
|
{
|
|
const char* comp;
|
|
fdt_ctx* fit;
|
|
|
|
fit_reset();
|
|
struct_u32(FDT_BEGIN_NODE); struct_str("");
|
|
node_begin("images");
|
|
node_begin("fpga-1");
|
|
prop_str("type", "fpga");
|
|
node_end();
|
|
node_end();
|
|
node_end();
|
|
struct_u32(FDT_END);
|
|
fit = fit_finish();
|
|
|
|
comp = fit_get_compatible(fit, "fpga-1");
|
|
ck_assert_ptr_null(comp);
|
|
}
|
|
END_TEST
|
|
|
|
static Suite* fit_fpga_suite(void)
|
|
{
|
|
Suite* s = suite_create("fit-fpga");
|
|
TCase* tc = tcase_create("discovery");
|
|
tcase_add_test(tc, test_fit_fpga_via_config);
|
|
tcase_add_test(tc, test_fit_fpga_via_type_fallback);
|
|
tcase_add_test(tc, test_fit_fpga_absent);
|
|
tcase_add_test(tc, test_fit_compatible_absent);
|
|
suite_add_tcase(s, tc);
|
|
return s;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
int failed;
|
|
SRunner* sr = srunner_create(fit_fpga_suite());
|
|
srunner_run_all(sr, CK_NORMAL);
|
|
failed = srunner_ntests_failed(sr);
|
|
srunner_free(sr);
|
|
return (failed == 0) ? 0 : 1;
|
|
}
|