From f109303de3bd5c96f7515c14e251f94d8f2c04b8 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 16 Aug 2024 16:32:34 +0100 Subject: [PATCH] Initial commit using partition firmware Add rom_pick_ab_update_partition to do all the checking Only allows picking verified images, and can be safely called before explicit_buy Add pico_use_partition_firmware function Also embed pt by default Minor bug fixes, and tidy up returns --- .../pico_cyw43_driver/CMakeLists.txt | 100 ++++++++++++++++++ .../pico_cyw43_driver/cyw43_driver.c | 95 +++++++++++++++-- .../pico_cyw43_driver/cyw43_firmware.py | 32 ++++++ .../include/cyw43_configport.h | 4 + .../include/cyw43_partition_firmware.h | 10 ++ .../pico_cyw43_driver/wifi_firmware.S | 63 +++++++++++ src/rp2_common/pico_cyw43_driver/wifi_pt.json | 52 +++++++++ 7 files changed, 348 insertions(+), 8 deletions(-) create mode 100644 src/rp2_common/pico_cyw43_driver/cyw43_firmware.py create mode 100644 src/rp2_common/pico_cyw43_driver/include/cyw43_partition_firmware.h create mode 100644 src/rp2_common/pico_cyw43_driver/wifi_firmware.S create mode 100644 src/rp2_common/pico_cyw43_driver/wifi_pt.json diff --git a/src/rp2_common/pico_cyw43_driver/CMakeLists.txt b/src/rp2_common/pico_cyw43_driver/CMakeLists.txt index d4a06aa77..b82b9c0f0 100644 --- a/src/rp2_common/pico_cyw43_driver/CMakeLists.txt +++ b/src/rp2_common/pico_cyw43_driver/CMakeLists.txt @@ -155,4 +155,104 @@ if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE}) endfunction() pico_promote_common_scope_vars() + + # Set env var, so it can be accessed in the function + set(ENV{PICO_CYW43_DRIVER_CURRENT_PATH} ${CMAKE_CURRENT_LIST_DIR}) + function(pico_use_partition_firmware TARGET) + target_compile_definitions(${TARGET} PRIVATE CYW43_USE_PARTITION_FIRMWARE=1) + pico_embed_pt_in_binary(${TARGET} $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_pt.json) + + find_package (Python3 REQUIRED COMPONENTS Interpreter) + + # Wifi firmware blob + add_custom_target(${TARGET}_firmware_w_blob DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/firmware_w_blob.S) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/firmware_w_blob.S + COMMAND ${Python3_EXECUTABLE} $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/cyw43_firmware.py ${PICO_CYW43_DRIVER_PATH}/firmware/w43439A0_7_95_49_00_combined.h ${CMAKE_CURRENT_BINARY_DIR}/firmware_w_blob.S + ) + + # Wifi and bluetooth firmware blob + add_custom_target(${TARGET}_firmware_wb_blob DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/firmware_wb_blob.S) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/firmware_wb_blob.S + COMMAND ${Python3_EXECUTABLE} $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/cyw43_firmware.py ${PICO_CYW43_DRIVER_PATH}/firmware/wb43439A0_7_95_49_00_combined.h ${CMAKE_CURRENT_BINARY_DIR}/firmware_wb_blob.S + ) + + # Create UF2s for all the variants - Wifi vs Wifi+Bluetooth, and TBYB + add_executable(${TARGET}_firmware_w + $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S) + add_executable(${TARGET}_firmware_w_tbyb + $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S) + add_executable(${TARGET}_firmware_wb + $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S) + add_executable(${TARGET}_firmware_wb_tbyb + $ENV{PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S) + + add_dependencies(${TARGET}_firmware_w ${TARGET}_firmware_w_blob) + add_dependencies(${TARGET}_firmware_w_tbyb ${TARGET}_firmware_w_blob) + add_dependencies(${TARGET}_firmware_wb ${TARGET}_firmware_wb_blob) + add_dependencies(${TARGET}_firmware_wb_tbyb ${TARGET}_firmware_wb_blob) + + target_include_directories(${TARGET}_firmware_w PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(${TARGET}_firmware_w_tbyb PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(${TARGET}_firmware_wb PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(${TARGET}_firmware_wb_tbyb PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + target_compile_definitions(${TARGET}_firmware_w PRIVATE + NO_PICO_PLATFORM=1 + ) + target_compile_definitions(${TARGET}_firmware_w_tbyb PRIVATE + NO_PICO_PLATFORM=1 + PICO_CRT0_IMAGE_TYPE_TBYB=1 + ) + target_compile_definitions(${TARGET}_firmware_wb PRIVATE + NO_PICO_PLATFORM=1 + WB_FIRMWARE=1 + ) + target_compile_definitions(${TARGET}_firmware_wb_tbyb PRIVATE + NO_PICO_PLATFORM=1 + PICO_CRT0_IMAGE_TYPE_TBYB=1 + WB_FIRMWARE=1 + ) + + target_link_options(${TARGET}_firmware_w PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000) + target_link_options(${TARGET}_firmware_w_tbyb PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000) + target_link_options(${TARGET}_firmware_wb PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000) + target_link_options(${TARGET}_firmware_wb_tbyb PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000) + + target_link_libraries(${TARGET}_firmware_w boot_picobin_headers) + target_link_libraries(${TARGET}_firmware_w_tbyb boot_picobin_headers) + target_link_libraries(${TARGET}_firmware_wb boot_picobin_headers) + target_link_libraries(${TARGET}_firmware_wb_tbyb boot_picobin_headers) + + get_target_property(hasSigfile ${TARGET} PICOTOOL_SIGFILE) + if (hasSigfile) + pico_sign_binary(${TARGET}_firmware_w ${sigfile}) + pico_sign_binary(${TARGET}_firmware_w_tbyb ${sigfile}) + pico_sign_binary(${TARGET}_firmware_wb ${sigfile}) + pico_sign_binary(${TARGET}_firmware_wb_tbyb ${sigfile}) + endif() + + pico_hash_binary(${TARGET}_firmware_w) + pico_hash_binary(${TARGET}_firmware_w_tbyb) + pico_hash_binary(${TARGET}_firmware_wb) + pico_hash_binary(${TARGET}_firmware_wb_tbyb) + + pico_set_uf2_family(${TARGET}_firmware_w 0x12345678) + pico_set_uf2_family(${TARGET}_firmware_w_tbyb 0x12345678) + pico_set_uf2_family(${TARGET}_firmware_wb 0x12345678) + pico_set_uf2_family(${TARGET}_firmware_wb_tbyb 0x12345678) + + pico_package_uf2_output(${TARGET}_firmware_w 0x10000000) + pico_package_uf2_output(${TARGET}_firmware_w_tbyb 0x10000000) + pico_package_uf2_output(${TARGET}_firmware_wb 0x10000000) + pico_package_uf2_output(${TARGET}_firmware_wb_tbyb 0x10000000) + + pico_add_extra_outputs(${TARGET}_firmware_w) + pico_add_extra_outputs(${TARGET}_firmware_w_tbyb) + pico_add_extra_outputs(${TARGET}_firmware_wb) + pico_add_extra_outputs(${TARGET}_firmware_wb_tbyb) + + add_dependencies(${TARGET} + ${TARGET}_firmware_w ${TARGET}_firmware_w_tbyb + ${TARGET}_firmware_wb ${TARGET}_firmware_wb_tbyb) + endfunction() endif() diff --git a/src/rp2_common/pico_cyw43_driver/cyw43_driver.c b/src/rp2_common/pico_cyw43_driver/cyw43_driver.c index 11d2652e0..9a6aaf49e 100644 --- a/src/rp2_common/pico_cyw43_driver/cyw43_driver.c +++ b/src/rp2_common/pico_cyw43_driver/cyw43_driver.c @@ -19,7 +19,17 @@ #define CYW43_SLEEP_CHECK_MS 50 #endif -static async_context_t *cyw43_async_context; +static async_context_t *cyw43_async_context = NULL; + +#if CYW43_USE_PARTITION_FIRMWARE + #include "pico/bootrom.h" + #include "boot/picobin.h" + #include + + int32_t cyw43_wifi_fw_len; + int32_t cyw43_clm_len; + uintptr_t fw_data; +#endif static void cyw43_sleep_timeout_reached(async_context_t *context, async_at_time_worker_t *worker); static void cyw43_do_poll(async_context_t *context, async_when_pending_worker_t *worker); @@ -104,6 +114,73 @@ static void cyw43_sleep_timeout_reached(async_context_t *context, __unused async } bool cyw43_driver_init(async_context_t *context) { +#if CYW43_USE_PARTITION_FIRMWARE + const int buf_words = (16 * 4) + 1; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1 + uint32_t* buffer = malloc(buf_words * 4); + int ret = rom_get_partition_table_info(buffer, buf_words, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID); + + assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID)); + + if (ret > 0) { + int i = 1; + int p = 0; + int picked_p = -1; + while (i < ret) { + i++; + uint32_t flags_and_permissions = buffer[i++]; + bool has_id = (flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS); + if (has_id) { + uint64_t id = 0; + id |= buffer[i++]; + id |= ((uint64_t)(buffer[i++]) << 32ull); + if (id == 0x123456789abcdef0) { + picked_p = p; + } + } + + p++; + } + + if (picked_p >= 0) { + uint32_t* workarea = malloc(0x1000); + picked_p = rom_pick_ab_update_partition(workarea, 0x1000, picked_p); + free(workarea); + + if (picked_p < 0) { + if (picked_p == BOOTROM_ERROR_NOT_FOUND) { + CYW43_DEBUG("Chosen CYW43 firmware partition was not verified\n"); + } else if (picked_p == BOOTROM_ERROR_NOT_PERMITTED) { + CYW43_DEBUG("Too many update boots going on at once\n"); + } + return false; + } + + CYW43_DEBUG("Chosen CYW43 firmware in partition %d\n", picked_p); + int ret = rom_get_partition_table_info(buffer, buf_words, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (picked_p << 24)); + assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION)); + assert(ret == 3); + + uint32_t location_and_permissions = buffer[1]; + uint32_t saddr = ((location_and_permissions >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & 0x1fffu) * 4096; + uint32_t eaddr = (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * 4096; + // Starts with metadata block + while(saddr < eaddr && *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr) != PICOBIN_BLOCK_MARKER_END) { + saddr += 4; + } + saddr += 4; + // Now onto data + cyw43_wifi_fw_len = *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr); + cyw43_clm_len = *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr + 4); + fw_data = XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr + 8; + } + free(buffer); + } else { + free(buffer); + CYW43_DEBUG("No partition table, so cannot get firmware from partition - get_partition_table_info returned %d\n", ret); + return false; + } + +#endif cyw43_init(&cyw43_state); cyw43_async_context = context; // we need the IRQ to be on the same core as the context, because we need to be able to enable/disable the IRQ @@ -114,13 +191,15 @@ bool cyw43_driver_init(async_context_t *context) { } void cyw43_driver_deinit(async_context_t *context) { - assert(context == cyw43_async_context); - async_context_remove_at_time_worker(context, &sleep_timeout_worker); - async_context_remove_when_pending_worker(context, &cyw43_poll_worker); - // the IRQ IS on the same core as the context, so must be de-initialized there - async_context_execute_sync(context, cyw43_irq_deinit, NULL); - cyw43_deinit(&cyw43_state); - cyw43_async_context = NULL; + if (cyw43_async_context != NULL) { + assert(context == cyw43_async_context); + async_context_remove_at_time_worker(context, &sleep_timeout_worker); + async_context_remove_when_pending_worker(context, &cyw43_poll_worker); + // the IRQ IS on the same core as the context, so must be de-initialized there + async_context_execute_sync(context, cyw43_irq_deinit, NULL); + cyw43_deinit(&cyw43_state); + cyw43_async_context = NULL; + } } // todo maybe add an #ifdef in cyw43_driver diff --git a/src/rp2_common/pico_cyw43_driver/cyw43_firmware.py b/src/rp2_common/pico_cyw43_driver/cyw43_firmware.py new file mode 100644 index 000000000..e32fe80ff --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/cyw43_firmware.py @@ -0,0 +1,32 @@ +import sys + +with open(sys.argv[1], "r") as f: + data = f.read() + lines = data.split(";") + for line in lines[1].split("\n"): + if "#define CYW43_WIFI_FW_LEN" in line: + cyw43_wifi_fw_len = int(line.split(")")[0].split("(")[-1]) + if "#define CYW43_CLM_LEN" in line: + cyw43_clm_len = int(line.split(")")[0].split("(")[-1]) + data = lines[0] + bits = data.split(",") + bits[0] = bits[0].split("{")[-1] + bits[-1] = bits[-1].split("}")[0] + for i in range(len(bits)): + bits[i] = bits[i].strip() + bits[i] = bits[i].strip(',') + bits[i] = int(bits[i], base=0) + print(f"Start {bits[4]}, end {bits[-1]}, num {len(bits)}") + print(bits[:10]) + +print(f"Wifi {cyw43_wifi_fw_len}, clm {cyw43_clm_len}") + +data = ( + cyw43_wifi_fw_len.to_bytes(4, 'little', signed=True) + + cyw43_clm_len.to_bytes(4, 'little', signed=True) + + bytearray(bits) +) + +with open(sys.argv[2], "w") as f: + for b in data: + f.write(f".byte 0x{b:02x}\n") diff --git a/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h index 704871ee2..1c480f980 100644 --- a/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h +++ b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h @@ -63,12 +63,16 @@ extern "C" { #endif #ifndef CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE +#if CYW43_USE_PARTITION_FIRMWARE +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "cyw43_partition_firmware.h" +#else #if CYW43_ENABLE_BLUETOOTH #define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "wb43439A0_7_95_49_00_combined.h" #else #define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "w43439A0_7_95_49_00_combined.h" #endif #endif +#endif #ifndef CYW43_WIFI_NVRAM_INCLUDE_FILE #define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h" diff --git a/src/rp2_common/pico_cyw43_driver/include/cyw43_partition_firmware.h b/src/rp2_common/pico_cyw43_driver/include/cyw43_partition_firmware.h new file mode 100644 index 000000000..afec44245 --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/include/cyw43_partition_firmware.h @@ -0,0 +1,10 @@ + +extern int cyw43_wifi_fw_len; +extern int cyw43_clm_len; + +#define CYW43_WIFI_FW_LEN (cyw43_wifi_fw_len) +#define CYW43_CLM_LEN (cyw43_clm_len) +extern uintptr_t fw_data; + +#include "boot/picobin.h" +#include "pico/bootrom.h" diff --git a/src/rp2_common/pico_cyw43_driver/wifi_firmware.S b/src/rp2_common/pico_cyw43_driver/wifi_firmware.S new file mode 100644 index 000000000..bca9fddbf --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/wifi_firmware.S @@ -0,0 +1,63 @@ +#include "boot/picobin.h" + +#if PICO_CRT0_IMAGE_TYPE_TBYB +#define CRT0_TBYB_FLAG PICOBIN_IMAGE_TYPE_EXE_TBYB_BITS +#else +#define CRT0_TBYB_FLAG 0 +#endif + +.section .text +.global _start +_start: +.word 0 +.word 0 +.word 0 +.word 0 + +.p2align 2 +embedded_block: +.word PICOBIN_BLOCK_MARKER_START + +.byte PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE +.byte 0x1 // 1 word +.hword PICOBIN_IMAGE_TYPE_IMAGE_TYPE_AS_BITS(EXE) | \ + PICOBIN_IMAGE_TYPE_EXE_CPU_AS_BITS(RISCV) | \ + PICOBIN_IMAGE_TYPE_EXE_CHIP_AS_BITS(RP2350) | \ + CRT0_TBYB_FLAG + +// Entry point into SRAM +.byte PICOBIN_BLOCK_ITEM_1BS_ENTRY_POINT +.byte 0x3 // word size to next item +.byte 0 // pad +.byte 0 // pad +.word _start +.word 0x20082000 // stack pointer + +_lm_item: +.byte PICOBIN_BLOCK_ITEM_LOAD_MAP +.byte 7 +.byte 0 // pad +.byte 2 // 2 entries +// To sign the firmware +.word (_start - _lm_item) +.word _start +.word (firmware_end - _start) +// But clear SRAM if actually running this, so it doesn't boot +.word 0 +.word _start +.word 0x00082000 + +.byte PICOBIN_BLOCK_ITEM_2BS_LAST +.hword (embedded_block_end - embedded_block - 16 ) / 4 // total size of all +.byte 0 +.word 0 +.word PICOBIN_BLOCK_MARKER_END +embedded_block_end: + +#if WB_FIRMWARE +#include "firmware_wb_blob.S" +#else +#include "firmware_w_blob.S" +#endif + +firmware_end: diff --git a/src/rp2_common/pico_cyw43_driver/wifi_pt.json b/src/rp2_common/pico_cyw43_driver/wifi_pt.json new file mode 100644 index 000000000..6b17a855b --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/wifi_pt.json @@ -0,0 +1,52 @@ +{ + "version": [1, 0], + "unpartitioned": { + "families": ["absolute"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + "partitions": [ + { + "name": "Main", + "id": 0, + "start": 0, + "size": "3500K", + "families": ["rp2350-arm-s", "rp2350-riscv"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + { + "name": "Wi-Fi Firmware", + "id": "0x123456789abcdef0", + "start": "3500k", + "size": "240K", + "families": ["0x12345678"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "ignored_during_riscv_boot": true, + "no_reboot_on_uf2_download": true + }, + { + "start": "3500k", + "size": "240k", + "families": ["0x12345678"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "link": ["a", 1], + "ignored_during_riscv_boot": true, + "no_reboot_on_uf2_download": true + } + ] +}