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

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 @@ -59,6 +62,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
#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 @@ -99,4 +119,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
16 changes: 16 additions & 0 deletions src/rp2_common/pico_bootrom/include/pico/bootrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,22 @@ 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 for a separate partition
* \ingroup pico_bootrom
*
* This will perform extra checks to prevent disrupting a main image TBYB, and return errors
*
* Also checks that the chosen partition contained a valid image
*
* \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
* @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
100 changes: 100 additions & 0 deletions src/rp2_common/pico_cyw43_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
95 changes: 87 additions & 8 deletions src/rp2_common/pico_cyw43_driver/cyw43_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdlib.h>

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);
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
32 changes: 32 additions & 0 deletions src/rp2_common/pico_cyw43_driver/cyw43_firmware.py
Original file line number Diff line number Diff line change
@@ -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")
4 changes: 4 additions & 0 deletions src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading
Loading