diff --git a/src/rp2_common/pico_cyw43_driver/BUILD.bazel b/src/rp2_common/pico_cyw43_driver/BUILD.bazel index 23bec36a..3810b27c 100644 --- a/src/rp2_common/pico_cyw43_driver/BUILD.bazel +++ b/src/rp2_common/pico_cyw43_driver/BUILD.bazel @@ -83,3 +83,13 @@ pico_generate_pio_header( name = "cyw43_bus_pio", srcs = ["cyw43_bus_pio_spi.pio"], ) + +# TODO: https://github.com/raspberrypi/pico-sdk/issues/2055 Support storing +# Wi-Fi firmware in a separate partition +filegroup( + name = "pico_use_partition_firmware", + srcs = [ + "wifi_firmware.S", + "include/cyw43_partition_firmware.h", + ] +) diff --git a/src/rp2_common/pico_cyw43_driver/CMakeLists.txt b/src/rp2_common/pico_cyw43_driver/CMakeLists.txt index 13ac2043..2b068192 100644 --- a/src/rp2_common/pico_cyw43_driver/CMakeLists.txt +++ b/src/rp2_common/pico_cyw43_driver/CMakeLists.txt @@ -159,5 +159,97 @@ if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE}) ) endfunction() + set(PICO_CYW43_DRIVER_CURRENT_PATH ${CMAKE_CURRENT_LIST_DIR}) + pico_register_common_scope_var(PICO_CYW43_DRIVER_CURRENT_PATH) + pico_promote_common_scope_vars() + + function(pico_use_wifi_firmware_partition TARGET) + if (PICO_PLATFORM STREQUAL "rp2040") + message(FATAL_ERROR "RP2040 does not support storing wi-fi firmware in partitions") + endif() + target_compile_definitions(${TARGET} PRIVATE CYW43_USE_FIRMWARE_PARTITION=1) + get_target_property(picotool_embed_pt ${TARGET} PICOTOOL_EMBED_PT) + if (NOT picotool_embed_pt) + pico_embed_pt_in_binary(${TARGET} ${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_pt.json) + endif() + + find_package (Python3 REQUIRED COMPONENTS Interpreter) + + # Wifi firmware blob + add_custom_target(${TARGET}_firmware_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} ${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 regular and TBYB firmwares + add_executable(${TARGET}_firmware + ${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S) + add_executable(${TARGET}_firmware_tbyb + ${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S) + + add_dependencies(${TARGET}_firmware ${TARGET}_firmware_blob) + add_dependencies(${TARGET}_firmware_tbyb ${TARGET}_firmware_blob) + + target_include_directories(${TARGET}_firmware PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(${TARGET}_firmware_tbyb PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + target_compile_definitions(${TARGET}_firmware PRIVATE + NO_PICO_PLATFORM=1 + WB_FIRMWARE=1 + ) + target_compile_definitions(${TARGET}_firmware_tbyb PRIVATE + NO_PICO_PLATFORM=1 + PICO_CRT0_IMAGE_TYPE_TBYB=1 + WB_FIRMWARE=1 + ) + + target_link_options(${TARGET}_firmware PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000) + target_link_options(${TARGET}_firmware_tbyb PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000) + + target_link_libraries(${TARGET}_firmware boot_picobin_headers) + target_link_libraries(${TARGET}_firmware_tbyb boot_picobin_headers) + + if (PICO_RISCV) + # Use pre-generated firmware elfs on Risc-V, as the compiler has issues with the assembly + add_custom_command(TARGET ${TARGET}_firmware POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PICO_CYW43_DRIVER_CURRENT_PATH}/firmware.elf $ + ) + add_custom_command(TARGET ${TARGET}_firmware_tbyb POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PICO_CYW43_DRIVER_CURRENT_PATH}/firmware_tbyb.elf $ + ) + endif() + + get_target_property(hasSigfile ${TARGET} PICOTOOL_SIGFILE) + if (hasSigfile) + pico_sign_binary(${TARGET}_firmware ${sigfile}) + pico_sign_binary(${TARGET}_firmware_tbyb ${sigfile}) + endif() + + pico_hash_binary(${TARGET}_firmware) + pico_hash_binary(${TARGET}_firmware_tbyb) + + pico_set_uf2_family(${TARGET}_firmware data) + pico_set_uf2_family(${TARGET}_firmware_tbyb data) + + pico_package_uf2_output(${TARGET}_firmware 0x10000000) + pico_package_uf2_output(${TARGET}_firmware_tbyb 0x10000000) + + if (PICO_RISCV) + # As Arm ELFs are being used for firmware, the bin & dis output generation will fail + # with a Risc-V toolchain + picotool_postprocess_binary(${TARGET}_firmware) + picotool_postprocess_binary(${TARGET}_firmware_tbyb) + if (NOT (PICO_NO_UF2 OR PICO_NO_PICOTOOL)) + pico_add_uf2_output(${TARGET}_firmware) + pico_add_uf2_output(${TARGET}_firmware_tbyb) + endif() + else() + pico_add_extra_outputs(${TARGET}_firmware) + pico_add_extra_outputs(${TARGET}_firmware_tbyb) + endif() + + add_dependencies(${TARGET} + ${TARGET}_firmware ${TARGET}_firmware_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 308d649d..4ae5438f 100644 --- a/src/rp2_common/pico_cyw43_driver/cyw43_driver.c +++ b/src/rp2_common/pico_cyw43_driver/cyw43_driver.c @@ -19,7 +19,18 @@ #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_FIRMWARE_PARTITION + #include "pico/bootrom.h" + #include "hardware/flash.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 +115,83 @@ static void cyw43_sleep_timeout_reached(async_context_t *context, __unused async } bool cyw43_driver_init(async_context_t *context) { +#if CYW43_USE_FIRMWARE_PARTITION + uint32_t buffer[(16 * 4) + 1] = {}; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1 + int ret = rom_get_partition_table_info(buffer, count_of(buffer), 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 == CYW43_FIRMWARE_PARTITION_ID) { + picked_p = p; + break; + } + } + + p++; + } + + if (picked_p >= 0) { + #ifdef __riscv + // Increased bootrom stack is required for this function + bootrom_stack_t stack = { + .base = malloc(0x400), + .size = 0x400 + }; + rom_set_bootrom_stack(&stack); + #endif + uint32_t* workarea = malloc(0x1000); + picked_p = rom_pick_ab_update_partition(workarea, 0x1000, picked_p); + free(workarea); + #ifdef __riscv + // Reset bootrom stack + rom_set_bootrom_stack(&stack); + #endif + + 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, count_of(buffer), 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) * FLASH_SECTOR_SIZE; + uint32_t eaddr = (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * FLASH_SECTOR_SIZE; + // 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; + } + } else { + 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 +202,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 00000000..e669a169 --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/cyw43_firmware.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024 Raspberry Pi (Trading) Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause + +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/firmware.elf b/src/rp2_common/pico_cyw43_driver/firmware.elf new file mode 100755 index 00000000..44243cf5 Binary files /dev/null and b/src/rp2_common/pico_cyw43_driver/firmware.elf differ diff --git a/src/rp2_common/pico_cyw43_driver/firmware_tbyb.elf b/src/rp2_common/pico_cyw43_driver/firmware_tbyb.elf new file mode 100755 index 00000000..6491d27d Binary files /dev/null and b/src/rp2_common/pico_cyw43_driver/firmware_tbyb.elf differ 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 f1c00c2f..0590e7a5 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_FIRMWARE_PARTITION +#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 00000000..5df9eb5e --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/include/cyw43_partition_firmware.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +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/include/pico/cyw43_driver.h b/src/rp2_common/pico_cyw43_driver/include/pico/cyw43_driver.h index 4d17c4b3..4cf4c421 100644 --- a/src/rp2_common/pico_cyw43_driver/include/pico/cyw43_driver.h +++ b/src/rp2_common/pico_cyw43_driver/include/pico/cyw43_driver.h @@ -20,6 +20,13 @@ #include "cyw43_configport.h" #endif +#if CYW43_USE_FIRMWARE_PARTITION +// PICO_CONFIG: CYW43_FIRMWARE_PARTITION_ID, ID of Wi-Fi firmware partition, type=int, default=0x776966696669726d (wififirm), group=pico_cyw43_driver +#ifndef CYW43_FIRMWARE_PARTITION_ID +#define CYW43_FIRMWARE_PARTITION_ID 0x776966696669726d // wififirm +#endif +#endif + #ifdef __cplusplus extern "C" { #endif 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 00000000..412f4373 --- /dev/null +++ b/src/rp2_common/pico_cyw43_driver/wifi_firmware.S @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 00000000..1330ed38 --- /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": "0x776966696669726d", + "start": "3500k", + "size": "240K", + "families": ["data"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "ignored_during_riscv_boot": true, + "no_reboot_on_uf2_download": true + }, + { + "start": "3500k", + "size": "240k", + "families": ["data"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "link": ["a", 1], + "ignored_during_riscv_boot": true, + "no_reboot_on_uf2_download": true + } + ] +}