From: gilles grimaud <[email protected]>

Accept RP2040-family UF2 files through the Raspberry Pi Pico -kernel path. 
Validate block framing, family IDs, payload sizes and XIP flash bounds before 
overlaying payloads onto flash storage.\n\nAdd a focused qtest for multi-block 
UF2 loading and provide a host-side converter for producing raw flash images 
from Pico UF2 files.

Signed-off-by: gilles grimaud <[email protected]>
---
 MAINTAINERS                   |   1 +
 hw/ssi/rp2040_xip.c           | 109 ++++++++
 scripts/uf2-to-flash.py       | 462 ++++++++++++++++++++++++++++++++++
 tests/qtest/rp2040-xip-test.c |  61 +++++
 4 files changed, 633 insertions(+)
 create mode 100755 scripts/uf2-to-flash.py

diff --git a/MAINTAINERS b/MAINTAINERS
index fd35521947..41494eff2a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1042,6 +1042,7 @@ S: Maintained
 F: hw/*/rp2040*
 F: include/hw/*/rp2040*
 F: hw/arm/raspi_pico.c
+F: scripts/uf2-to-flash.py
 F: tests/qtest/rp2040*
 F: tests/tcg/arm/system/rp2040*
 
diff --git a/hw/ssi/rp2040_xip.c b/hw/ssi/rp2040_xip.c
index d517df41ff..57cdd3c3c7 100644
--- a/hw/ssi/rp2040_xip.c
+++ b/hw/ssi/rp2040_xip.c
@@ -90,6 +90,17 @@
 #define FLASH_PAGE_SIZE  256
 #define FLASH_SECTOR_SIZE 4096
 
+#define UF2_MAGIC_START0 0x0a324655
+#define UF2_MAGIC_START1 0x9e5d5157
+#define UF2_MAGIC_END    0x0ab16f30
+#define UF2_BLOCK_SIZE   512
+#define UF2_HEADER_SIZE  32
+#define UF2_MAX_PAYLOAD  476
+
+#define UF2_FLAG_NOT_MAIN_FLASH       BIT(0)
+#define UF2_FLAG_FAMILY_ID_PRESENT    BIT(13)
+#define UF2_RP2040_FAMILY_ID          0xe48bff56
+
 #define ATOMIC_ALIAS_MASK 0x3000
 #define ATOMIC_XOR        0x1000
 #define ATOMIC_SET        0x2000
@@ -1121,6 +1132,92 @@ static bool rp2040_xip_load_elf(RP2040XipState *s, const 
char *filename,
     return true;
 }
 
+static bool rp2040_xip_load_uf2(RP2040XipState *s, const char *filename,
+                                Error **errp)
+{
+    g_autofree gchar *contents = NULL;
+    gsize len;
+    unsigned blocks;
+    unsigned i;
+    bool copied = false;
+
+    if (!g_file_get_contents(filename, &contents, &len, NULL)) {
+        error_setg(errp, "could not load flash image '%s'", filename);
+        return true;
+    }
+
+    if (len < UF2_BLOCK_SIZE ||
+        ldl_le_p(contents) != UF2_MAGIC_START0 ||
+        ldl_le_p(contents + 4) != UF2_MAGIC_START1) {
+        return false;
+    }
+
+    if (len % UF2_BLOCK_SIZE != 0) {
+        error_setg(errp, "invalid UF2 image '%s': size is not a multiple "
+                   "of %u bytes", filename, UF2_BLOCK_SIZE);
+        return true;
+    }
+
+    blocks = len / UF2_BLOCK_SIZE;
+    for (i = 0; i < blocks; i++) {
+        const uint8_t *block = (uint8_t *)contents + i * UF2_BLOCK_SIZE;
+        uint32_t flags;
+        uint32_t target;
+        uint32_t payload_size;
+        uint32_t family_id;
+        uint32_t offset;
+
+        if (ldl_le_p(block) != UF2_MAGIC_START0 ||
+            ldl_le_p(block + 4) != UF2_MAGIC_START1 ||
+            ldl_le_p(block + UF2_BLOCK_SIZE - 4) != UF2_MAGIC_END) {
+            error_setg(errp, "invalid UF2 image '%s': bad magic in block %u",
+                       filename, i);
+            return true;
+        }
+
+        flags = ldl_le_p(block + 8);
+        if (flags & UF2_FLAG_NOT_MAIN_FLASH) {
+            continue;
+        }
+
+        if (!(flags & UF2_FLAG_FAMILY_ID_PRESENT)) {
+            error_setg(errp, "invalid RP2040 UF2 image '%s': block %u has "
+                       "no family ID", filename, i);
+            return true;
+        }
+
+        family_id = ldl_le_p(block + 28);
+        if (family_id != UF2_RP2040_FAMILY_ID) {
+            error_setg(errp, "unsupported UF2 family ID 0x%08" PRIx32
+                       " in '%s'", family_id, filename);
+            return true;
+        }
+
+        target = ldl_le_p(block + 12);
+        payload_size = ldl_le_p(block + 16);
+        if (payload_size > UF2_MAX_PAYLOAD ||
+            target < RP2040_XIP_FLASH_BASE ||
+            target - RP2040_XIP_FLASH_BASE > s->flash_size ||
+            payload_size > s->flash_size -
+                           (target - RP2040_XIP_FLASH_BASE)) {
+            error_setg(errp, "UF2 block %u in '%s' is outside XIP flash",
+                       i, filename);
+            return true;
+        }
+
+        offset = target - RP2040_XIP_FLASH_BASE;
+        memcpy(s->storage + offset, block + UF2_HEADER_SIZE, payload_size);
+        copied = true;
+    }
+
+    if (!copied) {
+        error_setg(errp, "UF2 image '%s' contains no RP2040 flash payload",
+                   filename);
+    }
+
+    return true;
+}
+
 void rp2040_xip_load_image(RP2040XipState *s, const char *filename,
                            Error **errp)
 {
@@ -1143,6 +1240,18 @@ void rp2040_xip_load_image(RP2040XipState *s, const char 
*filename,
         return;
     }
 
+    if (rp2040_xip_load_uf2(s, filename, &local_err)) {
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return;
+        }
+        if (!rp2040_xip_fixup_boot2(s, filename, errp)) {
+            return;
+        }
+        rp2040_xip_writeback(s, errp);
+        return;
+    }
+
     image_size = load_image_size(filename, s->storage, s->flash_size);
     if (image_size < 0) {
         error_setg(errp, "could not load flash image '%s'", filename);
diff --git a/scripts/uf2-to-flash.py b/scripts/uf2-to-flash.py
new file mode 100755
index 0000000000..f1c0fcbbbb
--- /dev/null
+++ b/scripts/uf2-to-flash.py
@@ -0,0 +1,462 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+"""
+uf2-to-flash.py
+
+Convert a Raspberry Pi Pico UF2 file into a raw flash image.
+
+This tool implements the simple and practical conversion model used for
+QEMU-style emulation:
+
+    UF2 target address 0x10000000 + offset  ->  flash image offset
+
+The output file is not a FAT image. It is a raw representation of the
+external QSPI flash contents after UF2 programming.
+
+Supported input model:
+    - RP2040 / Pico 1 UF2 files, family ID 0xE48BFF56.
+    - RP2350 / Pico 2 UF2 files are detected and converted linearly,
+      but RP2350 partition remapping is not implemented.
+
+Usage:
+    scripts/uf2-to-flash.py firmware.uf2 flash.img
+    scripts/uf2-to-flash.py flash.img firmware.uf2
+
+If the two arguments are provided in reverse order, the .uf2 extension is
+used to detect the input file.
+"""
+
+import os
+import sys
+import struct
+
+
+UF2_MAGIC_START0 = 0x0A324655
+UF2_MAGIC_START1 = 0x9E5D5157
+UF2_MAGIC_END = 0x0AB16F30
+
+UF2_BLOCK_SIZE = 512
+UF2_HEADER_SIZE = 32
+
+FLAG_NOT_MAIN_FLASH = 0x00000001
+FLAG_FAMILY_ID_PRESENT = 0x00002000
+
+RP2040_FAMILY_ID = 0xE48BFF56
+
+# Usual RP2350 / Pico 2 family IDs.
+# They are detected for diagnostics. This script does not implement
+# RP2350 partition table remapping.
+RP2350_FAMILY_IDS = {
+    0xE48BFF58: "rp2350-arm-secure",
+    0xE48BFF59: "rp2350-arm-nonsecure",
+    0xE48BFF5A: "rp2350-riscv",
+    0xE48BFF5B: "data-or-absolute",
+}
+
+XIP_BASE = 0x10000000
+
+# RP2040 UF2 flash window: 0x10000000 .. 0x11000000
+PICO1_FLASH_WINDOW_SIZE = 16 * 1024 * 1024
+
+# RP2350 UF2 flash window: 0x10000000 .. 0x12000000
+PICO2_FLASH_WINDOW_SIZE = 32 * 1024 * 1024
+
+
+def print_help() -> None:
+    """Print a minimal usage message."""
+
+    program_name = os.path.basename(sys.argv[0])
+
+    print("Usage:")
+    print(f"  {program_name} firmware.uf2 flash.img")
+    print()
+    print("Convert a Raspberry Pi Pico UF2 file into a raw flash image.")
+    print("Exactly one argument must have the .uf2 extension, the other")
+    print("is used as the raw flash output file.")
+    print()
+    print("Examples:")
+    print(f"  {program_name} blink.uf2 flash.img")
+    print(f"  {program_name} flash.raw blink.uf2")
+
+
+def get_extension(path: str) -> str:
+    """Return the lowercase extension of a path."""
+
+    return os.path.splitext(path)[1].lower()
+
+
+def detect_input_output(arguments: list[str]) -> tuple[str, str]:
+    """
+    Detect the UF2 input file and the raw image output file.
+
+    The arguments may be passed in either order, provided exactly one of them
+    has the .uf2 extension.
+    """
+
+    if len(arguments) != 2:
+        print_help()
+        sys.exit(2)
+
+    first_path, second_path = arguments
+    first_ext = get_extension(first_path)
+    second_ext = get_extension(second_path)
+
+    if first_ext == ".uf2" and second_ext != ".uf2":
+        return first_path, second_path
+
+    if second_ext == ".uf2" and first_ext != ".uf2":
+        print("Reversed arguments detected: using the .uf2 file as input.")
+        return second_path, first_path
+
+    print("Error: unable to determine which file is the UF2 input.")
+    print("Exactly one argument must have the .uf2 extension.")
+    print()
+    print_help()
+    sys.exit(2)
+
+
+def read_uf2_blocks(path: str):
+    """
+    Yield all 512-byte blocks from a UF2 file.
+
+    Raises ValueError if the file size is not a multiple of 512 bytes.
+    """
+
+    file_size = os.path.getsize(path)
+
+    if file_size == 0:
+        raise ValueError("empty file")
+
+    if file_size % UF2_BLOCK_SIZE != 0:
+        raise ValueError(
+            f"invalid UF2 file size: {file_size} bytes; "
+            f"expected a multiple of {UF2_BLOCK_SIZE}"
+        )
+
+    with open(path, "rb") as input_file:
+        physical_block_index = 0
+
+        while True:
+            block = input_file.read(UF2_BLOCK_SIZE)
+
+            if not block:
+                break
+
+            physical_block_index += 1
+
+            if len(block) != UF2_BLOCK_SIZE:
+                raise ValueError(
+                    f"incomplete block at physical index 
{physical_block_index}"
+                )
+
+            yield physical_block_index, block
+
+
+def analyze_uf2(uf2_path: str) -> dict:
+    """
+    Analyze a UF2 file and extract main-flash pages.
+
+    Returns a dictionary containing:
+        - pages: mapping from flash offset to payload bytes
+        - family_ids: detected family IDs
+        - warnings: non-fatal problems
+        - errors: fatal format problems
+        - min_offset / max_end_offset: occupied flash range
+    """
+
+    pages: dict[int, bytes] = {}
+    warnings: list[str] = []
+    errors: list[str] = []
+
+    family_ids: set[int] = set()
+    valid_block_count = 0
+    non_uf2_block_count = 0
+    ignored_not_main_flash_count = 0
+
+    min_offset = None
+    max_end_offset = 0
+
+    announced_num_blocks = None
+    seen_block_numbers: set[int] = set()
+
+    for physical_index, block in read_uf2_blocks(uf2_path):
+        (
+            magic_start0,
+            magic_start1,
+            flags,
+            target_address,
+            payload_size,
+            block_number,
+            num_blocks,
+            family_or_file_size,
+        ) = struct.unpack_from("<IIIIIIII", block, 0)
+
+        (magic_end,) = struct.unpack_from("<I", block, UF2_BLOCK_SIZE - 4)
+
+        if (
+            magic_start0 != UF2_MAGIC_START0
+            or magic_start1 != UF2_MAGIC_START1
+            or magic_end != UF2_MAGIC_END
+        ):
+            non_uf2_block_count += 1
+            errors.append(
+                f"block {physical_index}: invalid UF2 magic values"
+            )
+            continue
+
+        valid_block_count += 1
+
+        if announced_num_blocks is None:
+            announced_num_blocks = num_blocks
+        elif num_blocks != announced_num_blocks:
+            warnings.append(
+                f"block {physical_index}: inconsistent numBlocks "
+                f"({num_blocks}, expected {announced_num_blocks})"
+            )
+
+        if block_number in seen_block_numbers:
+            warnings.append(
+                f"block {physical_index}: duplicated blockNo {block_number}"
+            )
+
+        seen_block_numbers.add(block_number)
+
+        if flags & FLAG_NOT_MAIN_FLASH:
+            ignored_not_main_flash_count += 1
+            continue
+
+        if not (flags & FLAG_FAMILY_ID_PRESENT):
+            errors.append(
+                f"block {physical_index}: missing family ID; Pico boot ROM "
+                f"expects a family ID"
+            )
+            continue
+
+        family_id = family_or_file_size
+        family_ids.add(family_id)
+
+        if payload_size != 256:
+            errors.append(
+                f"block {physical_index}: payloadSize={payload_size}; "
+                f"expected 256 for Pico UF2 files"
+            )
+            continue
+
+        if target_address % 256 != 0:
+            errors.append(
+                f"block {physical_index}: target address is not "
+                f"256-byte aligned: 0x{target_address:08x}"
+            )
+            continue
+
+        pico2_window_end = XIP_BASE + PICO2_FLASH_WINDOW_SIZE
+        if not (XIP_BASE <= target_address < pico2_window_end):
+            errors.append(
+                f"block {physical_index}: target address outside Pico "
+                f"flash window: 0x{target_address:08x}"
+            )
+            continue
+
+        flash_offset = target_address - XIP_BASE
+
+        if flash_offset + payload_size > PICO2_FLASH_WINDOW_SIZE:
+            errors.append(
+                f"block {physical_index}: page exceeds Pico 2 flash window: "
+                f"offset=0x{flash_offset:x}, size={payload_size}"
+            )
+            continue
+
+        if flash_offset in pages:
+            warnings.append(
+                f"block {physical_index}: duplicated flash page at offset "
+                f"0x{flash_offset:x}; last occurrence wins"
+            )
+
+        payload = block[UF2_HEADER_SIZE:UF2_HEADER_SIZE + payload_size]
+        pages[flash_offset] = payload
+
+        if min_offset is None or flash_offset < min_offset:
+            min_offset = flash_offset
+
+        if flash_offset + payload_size > max_end_offset:
+            max_end_offset = flash_offset + payload_size
+
+    if valid_block_count == 0:
+        errors.append("no valid UF2 block found")
+
+    if (announced_num_blocks is not None
+            and len(seen_block_numbers) != announced_num_blocks):
+        warnings.append(
+            f"observed {len(seen_block_numbers)} distinct blockNo values, "
+            f"but numBlocks announces {announced_num_blocks}"
+        )
+
+    return {
+        "pages": pages,
+        "family_ids": family_ids,
+        "warnings": warnings,
+        "errors": errors,
+        "valid_block_count": valid_block_count,
+        "non_uf2_block_count": non_uf2_block_count,
+        "ignored_not_main_flash_count": ignored_not_main_flash_count,
+        "min_offset": min_offset,
+        "max_end_offset": max_end_offset,
+    }
+
+
+def diagnose_family_ids(
+    family_ids: set[int],
+    warnings: list[str],
+    errors: list[str],
+) -> None:
+    """
+    Diagnose UF2 family IDs with respect to Pico 1 and Pico 2.
+
+    RP2040 is accepted as the strict Pico 1 case.
+    RP2350 IDs are accepted with a warning because partition remapping is not
+    implemented.
+    """
+
+    if not family_ids:
+        errors.append("no usable family ID found")
+        return
+
+    if len(family_ids) > 1:
+        family_list = ", ".join(
+            f"0x{value:08x}" for value in sorted(family_ids)
+        )
+        warnings.append(f"multiple family IDs found: {family_list}")
+
+    for family_id in sorted(family_ids):
+        if family_id == RP2040_FAMILY_ID:
+            continue
+
+        if family_id in RP2350_FAMILY_IDS:
+            warnings.append(
+                f"family ID 0x{family_id:08x} detected "
+                f"({RP2350_FAMILY_IDS[family_id]}): this is probably a "
+                f"Pico 2 / RP2350 UF2 file. Linear conversion is performed, "
+                f"but RP2350 partition remapping is not implemented."
+            )
+        else:
+            warnings.append(
+                f"unknown or non-Pico family ID: 0x{family_id:08x}"
+            )
+
+    if RP2040_FAMILY_ID not in family_ids:
+        warnings.append(
+            "no RP2040 / Pico 1 family ID found (0xe48bff56); "
+            "the output may not be a strict Pico 1 flash image."
+        )
+
+
+def build_flash_image(pages: dict[int, bytes], occupied_size: int) -> 
bytearray:
+    """
+    Build a raw flash image initialized with erased flash bytes, 0xff.
+
+    Only the occupied part is emitted:
+        output size = highest written offset + payload size
+    """
+
+    image = bytearray([0xFF] * occupied_size)
+
+    for flash_offset, payload in pages.items():
+        image[flash_offset:flash_offset + len(payload)] = payload
+
+    return image
+
+
+def human_size(byte_count: int) -> str:
+    """Return a human-readable size string."""
+
+    if byte_count < 1024:
+        return f"{byte_count} B"
+
+    if byte_count < 1024 * 1024:
+        return f"{byte_count / 1024:.1f} KiB"
+
+    return f"{byte_count / (1024 * 1024):.2f} MiB"
+
+
+def main() -> None:
+    """Program entry point."""
+
+    uf2_path, image_path = detect_input_output(sys.argv[1:])
+
+    try:
+        analysis = analyze_uf2(uf2_path)
+    except OSError as error:
+        print(f"UF2 file access error: {error}")
+        sys.exit(1)
+    except ValueError as error:
+        print(f"UF2 format error: {error}")
+        sys.exit(1)
+
+    pages = analysis["pages"]
+    family_ids = analysis["family_ids"]
+    warnings = analysis["warnings"]
+    errors = analysis["errors"]
+
+    diagnose_family_ids(family_ids, warnings, errors)
+
+    if not pages:
+        errors.append("no main-flash page to write")
+
+    if analysis["max_end_offset"] > PICO1_FLASH_WINDOW_SIZE:
+        warnings.append(
+            "image exceeds the Pico 1 flash window "
+            "0x10000000..0x11000000; it is not strict Pico 1 compatible"
+        )
+
+    if errors:
+        print("Conversion refused: UF2 format problems were found.")
+        for error in errors:
+            print(f"ERROR: {error}")
+        for warning in warnings:
+            print(f"WARNING: {warning}")
+        sys.exit(1)
+
+    occupied_size = analysis["max_end_offset"]
+    image = build_flash_image(pages, occupied_size)
+
+    try:
+        with open(image_path, "wb") as output_file:
+            output_file.write(image)
+    except OSError as error:
+        print(f"Output image write error: {error}")
+        sys.exit(1)
+
+    programmed_size = sum(len(payload) for payload in pages.values())
+
+    print(f"UF2 input: {uf2_path}")
+    print(f"Raw output: {image_path}")
+    print(f"Valid UF2 blocks: {analysis['valid_block_count']}")
+    print("Ignored NOT_MAIN_FLASH blocks: "
+          f"{analysis['ignored_not_main_flash_count']}")
+    print(f"Written flash pages: {len(pages)}")
+    print(
+        f"Actually programmed bytes: {programmed_size} "
+        f"({human_size(programmed_size)})"
+    )
+    print(
+        f"Occupied flash size: {occupied_size} "
+        f"({human_size(occupied_size)})"
+    )
+
+    if family_ids:
+        family_id_list = ", ".join(
+            f"0x{value:08x}" for value in sorted(family_ids)
+        )
+        print(f"Family ID(s): {family_id_list}")
+
+    if warnings:
+        print()
+        print("Warnings:")
+        for warning in warnings:
+            print(f"- {warning}")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/qtest/rp2040-xip-test.c b/tests/qtest/rp2040-xip-test.c
index 8e5b719332..351c8ef706 100644
--- a/tests/qtest/rp2040-xip-test.c
+++ b/tests/qtest/rp2040-xip-test.c
@@ -31,6 +31,15 @@
 #define XIP_STAT_FIFO_EMPTY BIT(1)
 #define XIP_STAT_FLUSH_READY BIT(0)
 
+#define UF2_BLOCK_SIZE          512
+#define UF2_PAYLOAD_OFFSET      32
+#define UF2_PAYLOAD_SIZE        256
+#define UF2_MAGIC_START0        0x0a324655
+#define UF2_MAGIC_START1        0x9e5d5157
+#define UF2_MAGIC_END           0x0ab16f30
+#define UF2_FLAG_FAMILY_ID      0x00002000
+#define UF2_RP2040_FAMILY_ID    0xe48bff56
+
 static QTestState *rp2040_start(const char *machine_args)
 {
     if (machine_args) {
@@ -39,6 +48,57 @@ static QTestState *rp2040_start(const char *machine_args)
     return qtest_init("-machine raspi-pico");
 }
 
+static void build_uf2_block(uint8_t *block, uint32_t target, uint32_t index,
+                            const uint8_t *payload)
+{
+    memset(block, 0, UF2_BLOCK_SIZE);
+    stl_le_p(block, UF2_MAGIC_START0);
+    stl_le_p(block + 4, UF2_MAGIC_START1);
+    stl_le_p(block + 8, UF2_FLAG_FAMILY_ID);
+    stl_le_p(block + 12, target);
+    stl_le_p(block + 16, UF2_PAYLOAD_SIZE);
+    stl_le_p(block + 20, index);
+    stl_le_p(block + 24, 2);
+    stl_le_p(block + 28, UF2_RP2040_FAMILY_ID);
+    memcpy(block + UF2_PAYLOAD_OFFSET, payload, UF2_PAYLOAD_SIZE);
+    stl_le_p(block + UF2_BLOCK_SIZE - 4, UF2_MAGIC_END);
+}
+
+static void test_uf2_kernel_load(void)
+{
+    g_autoptr(GError) error = NULL;
+    g_autofree char *path = NULL;
+    uint8_t uf2[2 * UF2_BLOCK_SIZE];
+    uint8_t boot2[UF2_PAYLOAD_SIZE];
+    uint8_t payload[UF2_PAYLOAD_SIZE];
+    uint8_t actual[UF2_PAYLOAD_SIZE];
+    QTestState *qts;
+    int fd;
+    size_t i;
+
+    memset(boot2, 0x5a, sizeof(boot2));
+    for (i = 0; i < ARRAY_SIZE(payload); i++) {
+        payload[i] = i ^ 0xa5;
+    }
+    build_uf2_block(uf2, XIP_BASE, 0, boot2);
+    build_uf2_block(uf2 + UF2_BLOCK_SIZE, XIP_BASE + 0x2000, 1, payload);
+
+    fd = g_file_open_tmp("qemu-rp2040-uf2-XXXXXX", &path, &error);
+    g_assert_no_error(error);
+    g_assert_cmpint(fd, >=, 0);
+    close(fd);
+    g_assert_true(g_file_set_contents(path, (const char *)uf2, sizeof(uf2),
+                                      &error));
+    g_assert_no_error(error);
+
+    qts = qtest_initf("-machine raspi-pico -kernel %s", path);
+    qtest_memread(qts, XIP_BASE + 0x2000, actual, sizeof(actual));
+    g_assert_cmpmem(actual, sizeof(actual), payload, sizeof(payload));
+    qtest_quit(qts);
+
+    unlink(path);
+}
+
 static void read_flash_uid(QTestState *qts, uint8_t *uid)
 {
     int i;
@@ -280,6 +340,7 @@ int main(int argc, char **argv)
                    test_flash_uid_default);
     qtest_add_func("/rp2040-xip/flash-uid-machine-option",
                    test_flash_uid_machine_option);
+    qtest_add_func("/rp2040-xip/uf2-kernel-load", test_uf2_kernel_load);
     qtest_add_func("/rp2040-xip/flash-program-erase",
                    test_flash_program_erase);
     qtest_add_func("/rp2040-xip/flash-persistence",
-- 
2.55.0


Reply via email to