Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Wi-Fi firmware partition support for Pico 2 W #1969

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ alias(
"//src/rp2_common/boot_bootrom_headers:__pkg__",
"//src/rp2_common/hardware_boot_lock:__pkg__",
"//src/rp2_common/pico_flash:__pkg__",
"//src/rp2_common/hardware_rcp:__pkg__",
],
)

Expand Down
13 changes: 13 additions & 0 deletions src/rp2_common/hardware_rcp/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ load("//bazel:defs.bzl", "compatible_with_rp2")

package(default_visibility = ["//visibility:public"])

# Picotool needs this (transitively through
# //src/rp2_common/pico_bootrom:pico_bootrom_headers), so we can't strictly
# constrain compatibility.
cc_library(
name = "hardware_rcp_headers",
hdrs = ["include/hardware/rcp.h"],
includes = ["include"],
visibility = ["//src/rp2_common/pico_bootrom:__pkg__"],
deps = [
"//src:pico_platform_internal",
],
)

cc_library(
name = "hardware_rcp",
hdrs = ["include/hardware/rcp.h"],
Expand Down
7 changes: 5 additions & 2 deletions src/rp2_common/hardware_rcp/include/hardware/rcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
*/

// ----------------------------------------------------------------------------
// RCP instructions (this header is Arm-only)
#if defined(PICO_RP2350) && !defined(__riscv)
// RCP masks (this header is RP2350 only)
#if defined(PICO_RP2350)

#define RCP_MASK_TRUE _u(0xa500a500)
#define RCP_MASK_FALSE _u(0x00c300c3)
#define RCP_MASK_INTXOR _u(0x96009600)

// RCP instructions (this header is Arm-only)
#if !defined(__riscv)
// ----------------------------------------------------------------------------
// Macros and inline functions for use in C files
#ifndef __ASSEMBLER__
Expand Down Expand Up @@ -995,6 +997,7 @@ rcp_switch_u8_to_ch_cl rcp_canary_check_nodelay_impl \tag, \x
.endm

#endif // !__riscv
#endif // PICO_RP2350
#endif // __ASSEMBLER__
// ----------------------------------------------------------------------------

Expand Down
1 change: 1 addition & 0 deletions src/rp2_common/pico_bootrom/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ cc_library(
"//src/rp2_common/boot_bootrom_headers",
"//src/rp2_common/hardware_boot_lock:hardware_boot_lock_headers",
"//src/rp2_common/pico_flash:pico_flash_headers",
"//src/rp2_common/hardware_rcp:hardware_rcp_headers",
] + select({
"//bazel/constraint:host": ["//src/host/hardware_sync"],
"//conditions:default": ["//src/rp2_common/hardware_sync"],
Expand Down
78 changes: 78 additions & 0 deletions src/rp2_common/pico_bootrom/bootrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include "pico/bootrom.h"
#include "boot/picoboot.h"
#include "boot/picobin.h"
#if !PICO_RP2040
#include "hardware/rcp.h"
#endif

/// \tag::table_lookup[]

Expand Down Expand Up @@ -64,6 +67,23 @@ void __attribute__((noreturn)) rom_reset_usb_boot(uint32_t usb_activity_gpio_pin
}

#if !PICO_RP2040


// Generated from adding the following code into the bootrom
// scan_workarea_t* scan_workarea = (scan_workarea_t*)workarea;
// printf("VERSION_DOWNGRADE_ERASE_ADDR %08x\n", &(always->zero_init.version_downgrade_erase_flash_addr));
// printf("TBYB_FLAG_ADDR %08x\n", &(always->zero_init.tbyb_flag_flash_addr));
// printf("IMAGE_DEF_VERIFIED %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.verified) - (uint32_t)scan_workarea);
// printf("IMAGE_DEF_TBYB_FLAGGED %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.tbyb_flagged) - (uint32_t)scan_workarea);
// printf("IMAGE_DEF_BASE %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.enclosing_window.base) - (uint32_t)scan_workarea);
// printf("IMAGE_DEF_REL_BLOCK_OFFSET %08x\n", (uint32_t)&(scan_workarea->parsed_block_loops[0].image_def.core.window_rel_block_offset) - (uint32_t)scan_workarea);
#define VERSION_DOWNGRADE_ERASE_ADDR *(uint32_t*)0x400e0338
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arguably these are not addresses. i would leave the leading * off and use in code references

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically they are addresses, eg the version downgrade erase address (ie the address in flash to erase) is stored at 0x400e0338

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Page 31 of the RP2350 datasheet says BOOTRAM_BASE 0x400e0000

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, more of a stylistic thing I guess; it's weird to see a CONSTANT as a lhs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but i'm ok

#define TBYB_FLAG_ADDR *(uint32_t*)0x400e0348
#define IMAGE_DEF_VERIFIED(scan_workarea) *(uint32_t*)(0x64 + (uint32_t)scan_workarea)
#define IMAGE_DEF_TBYB_FLAGGED(scan_workarea) *(bool*)(0x4c + (uint32_t)scan_workarea)
#define IMAGE_DEF_BASE(scan_workarea) *(uint32_t*)(0x54 + (uint32_t)scan_workarea)
#define IMAGE_DEF_REL_BLOCK_OFFSET(scan_workarea) *(uint32_t*)(0x5c + (uint32_t)scan_workarea)

bool rom_get_boot_random(uint32_t out[4]) {
uint32_t result[5];
rom_get_sys_info_fn func = (rom_get_sys_info_fn) rom_func_lookup_inline(ROM_FUNC_GET_SYS_INFO);
Expand Down Expand Up @@ -104,4 +124,62 @@ int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32
}
return PICO_ERROR_INSUFFICIENT_RESOURCES;
}

int rom_pick_ab_update_partition(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num) {
uint32_t flash_update_base = 0;
bool tbyb_boot = false;
uint32_t saved_erase_addr = 0;
if (rom_get_last_boot_type() == BOOT_TYPE_FLASH_UPDATE) {
// For a flash update boot, get the flash update base
boot_info_t boot_info = {};
int ret = rom_get_boot_info(&boot_info);
if (ret) {
flash_update_base = boot_info.reboot_params[0];
if (boot_info.tbyb_and_update_info & BOOT_TBYB_AND_UPDATE_FLAG_BUY_PENDING) {
// A buy is pending, so the main software has not been bought
tbyb_boot = true;
// Save the erase address, as this will be overwritten by rom_pick_ab_partition
saved_erase_addr = VERSION_DOWNGRADE_ERASE_ADDR;
}
}
}

int rc = rom_pick_ab_partition((uint8_t*)workarea_base, workarea_size, partition_a_num, flash_update_base);

if (IMAGE_DEF_VERIFIED(workarea_base) != RCP_MASK_TRUE) {
// Chosen partition failed verification
return BOOTROM_ERROR_NOT_FOUND;
}

if (IMAGE_DEF_TBYB_FLAGGED(workarea_base)) {
// The chosen partition is TBYB
if (tbyb_boot) {
// The boot partition is also TBYB - cannot update both, so prioritise boot partition
// Restore the erase address saved earlier
VERSION_DOWNGRADE_ERASE_ADDR = saved_erase_addr;
return BOOTROM_ERROR_NOT_PERMITTED;
} else {
// Update the tbyb flash address, so that explicit_buy will clear the flag for the chosen partition
TBYB_FLAG_ADDR =
IMAGE_DEF_BASE(workarea_base)
+ IMAGE_DEF_REL_BLOCK_OFFSET(workarea_base) + 4;
}
} else {
// The chosen partition is not TBYB
if (tbyb_boot && saved_erase_addr) {
// The boot partition was TBYB, and requires an erase
if (VERSION_DOWNGRADE_ERASE_ADDR) {
// But both the chosen partition requires an erase too
// As before, prioritise the boot partition, and restore it's saved erase_address
VERSION_DOWNGRADE_ERASE_ADDR = saved_erase_addr;
return BOOTROM_ERROR_NOT_PERMITTED;
} else {
// The chosen partition doesn't require an erase, so we're fine
VERSION_DOWNGRADE_ERASE_ADDR = saved_erase_addr;
}
}
}

return rc;
}
#endif
27 changes: 27 additions & 0 deletions src/rp2_common/pico_bootrom/include/pico/bootrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,9 @@ static inline int rom_load_partition_table(uint8_t *workarea_base, uint32_t work
*
* NOTE: This method does not look at owner partitions, only the A partition passed and it's corresponding B partition.
*
* NOTE: You should not call this method directly when performing a Flash Update Boot before calling `explicit_buy`, as it may prevent
* any version downgrade from occuring - instead \see rom_pick_ab_update_partition() which wraps this function.
*
* \param workarea_base base address of work area
* \param workarea_size size of work area
* \param partition_a_num the A partition of the pair
Expand Down Expand Up @@ -1046,6 +1049,30 @@ static inline int rom_get_last_boot_type(void) {
*/
int rom_add_flash_runtime_partition(uint32_t start_offset, uint32_t size, uint32_t permissions);

/*! \brief Pick A/B partition without disturbing any in progress update or TBYB boot
* \ingroup pico_bootrom
*
* This will call `rom_pick_ab_partition` using the `flash_update_boot_window_base` from the current boot, while performing extra checks to prevent disrupting
* a main image TBYB boot. It requires the same minimum workarea size as `rom_pick_ab_partition`.
* \see rom_pick_ab_partition()
*
* For example, if an `explicit_buy` is pending then calling `pick_ab_partition` would normally clear the saved flash erase address for the version downgrade,
* so the required erase of the other partition would not occur when `explicit_buy` is called - this function saves and restores that address to prevent this
* issue, and returns `BOOTROM_ERROR_NOT_PERMITTED` if the partition chosen by `pick_ab_partition` also requires a flash erase version downgrade (as you can't
* erase 2 partitions with one `explicit_buy` call).
*
* It also checks that the chosen partition contained a valid image (eg a signed image when using secure boot), and returns `BOOTROM_ERROR_NOT_FOUND`
* if it does not.
*
* \param workarea_base base address of work area
* \param workarea_size size of work area
lurch marked this conversation as resolved.
Show resolved Hide resolved
* \param partition_a_num the A partition of the pair
* \return >= 0 the partition number picked
* BOOTROM_ERROR_NOT_PERMITTED if not possible to do an update correctly, eg if both main image and data image are TBYB
* BOOTROM_ERROR_NOT_FOUND if the chosen partition failed verification
*/
int rom_pick_ab_update_partition(uint32_t *workarea_base, uint32_t workarea_size, uint partition_a_num);

#endif

#ifdef __cplusplus
Expand Down
10 changes: 10 additions & 0 deletions src/rp2_common/pico_cyw43_driver/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,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",
]
)
89 changes: 89 additions & 0 deletions src/rp2_common/pico_cyw43_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,94 @@ 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)
pico_embed_pt_in_binary(${TARGET} ${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_pt.json)

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 $<TARGET_FILE:${TARGET}_firmware>
)
add_custom_command(TARGET ${TARGET}_firmware_tbyb POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${PICO_CYW43_DRIVER_CURRENT_PATH}/firmware_tbyb.elf $<TARGET_FILE:${TARGET}_firmware_tbyb>
)
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()
Loading