flash/nor/rp2040: fix memory leak of target stack workarea

While on it restore memory-mapped mode also after flash erase
(originally was restored after flash write only).

Change-Id: I5e153b79ac27a8439f57239ce90ce8a79c0bb8a1
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: https://review.openocd.org/c/openocd/+/7186
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
Tomas Vanek 2022-09-11 11:18:29 +02:00
parent d25f0ae263
commit 47ed1c1eab
1 changed files with 57 additions and 25 deletions

View File

@ -138,6 +138,43 @@ static int rp2040_call_rom_func(struct target *target, struct rp2040_flash_bank
} }
/* Finalize flash write/erase/read ID
* - flush cache
* - enters memory-mapped (XIP) mode to make flash data visible
* - deallocates target ROM func stack if previously allocated
*/
static int rp2040_finalize_stack_free(struct flash_bank *bank)
{
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
/* Always flush before returning to execute-in-place, to invalidate stale
* cache contents. The flush call also restores regular hardware-controlled
* chip select following a rp2040_flash_exit_xip().
*/
LOG_DEBUG("Flushing flash cache after write behind");
int err = rp2040_call_rom_func(target, priv, priv->jump_flush_cache, NULL, 0);
if (err != ERROR_OK) {
LOG_ERROR("Failed to flush flash cache");
/* Intentionally continue after error and try to setup xip anyway */
}
LOG_DEBUG("Configuring SSI for execute-in-place");
err = rp2040_call_rom_func(target, priv, priv->jump_enter_cmd_xip, NULL, 0);
if (err != ERROR_OK)
LOG_ERROR("Failed to set SSI to XIP mode");
target_free_working_area(target, priv->stack);
priv->stack = NULL;
return err;
}
/* Prepare flash write/erase/read ID
* - allocates a stack for target ROM func
* - switches the SPI interface from memory-mapped mode to direct command mode
* Always pair with a call of rp2040_finalize_stack_free()
* after flash operation finishes or fails.
*/
static int rp2040_stack_grab_and_prep(struct flash_bank *bank) static int rp2040_stack_grab_and_prep(struct flash_bank *bank)
{ {
struct rp2040_flash_bank *priv = bank->driver_priv; struct rp2040_flash_bank *priv = bank->driver_priv;
@ -154,14 +191,14 @@ static int rp2040_stack_grab_and_prep(struct flash_bank *bank)
LOG_DEBUG("Connecting internal flash"); LOG_DEBUG("Connecting internal flash");
err = rp2040_call_rom_func(target, priv, priv->jump_connect_internal_flash, NULL, 0); err = rp2040_call_rom_func(target, priv, priv->jump_connect_internal_flash, NULL, 0);
if (err != ERROR_OK) { if (err != ERROR_OK) {
LOG_ERROR("RP2040 erase: failed to connect internal flash"); LOG_ERROR("Failed to connect internal flash");
return err; return err;
} }
LOG_DEBUG("Kicking flash out of XIP mode"); LOG_DEBUG("Kicking flash out of XIP mode");
err = rp2040_call_rom_func(target, priv, priv->jump_flash_exit_xip, NULL, 0); err = rp2040_call_rom_func(target, priv, priv->jump_flash_exit_xip, NULL, 0);
if (err != ERROR_OK) { if (err != ERROR_OK) {
LOG_ERROR("RP2040 erase: failed to exit flash XIP mode"); LOG_ERROR("Failed to exit flash XIP mode");
return err; return err;
} }
@ -174,16 +211,17 @@ static int rp2040_flash_write(struct flash_bank *bank, const uint8_t *buffer, ui
struct rp2040_flash_bank *priv = bank->driver_priv; struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target; struct target *target = bank->target;
struct working_area *bounce; struct working_area *bounce = NULL;
int err = rp2040_stack_grab_and_prep(bank); int err = rp2040_stack_grab_and_prep(bank);
if (err != ERROR_OK) if (err != ERROR_OK)
return err; goto cleanup;
const unsigned int chunk_size = target_get_working_area_avail(target); const unsigned int chunk_size = target_get_working_area_avail(target);
if (target_alloc_working_area(target, chunk_size, &bounce) != ERROR_OK) { err = target_alloc_working_area(target, chunk_size, &bounce);
if (err != ERROR_OK) {
LOG_ERROR("Could not allocate bounce buffer for flash programming. Can't continue"); LOG_ERROR("Could not allocate bounce buffer for flash programming. Can't continue");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; goto cleanup;
} }
LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, bounce->address); LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, bounce->address);
@ -211,23 +249,12 @@ static int rp2040_flash_write(struct flash_bank *bank, const uint8_t *buffer, ui
offset += write_size; offset += write_size;
count -= write_size; count -= write_size;
} }
cleanup:
target_free_working_area(target, bounce); target_free_working_area(target, bounce);
if (err != ERROR_OK) rp2040_finalize_stack_free(bank);
return err;
/* Flash is successfully programmed. We can now do a bit of poking to make the flash
contents visible to us via memory-mapped (XIP) interface in the 0x1... memory region */
LOG_DEBUG("Flushing flash cache after write behind");
err = rp2040_call_rom_func(bank->target, priv, priv->jump_flush_cache, NULL, 0);
if (err != ERROR_OK) {
LOG_ERROR("RP2040 write: failed to flush flash cache");
return err;
}
LOG_DEBUG("Configuring SSI for execute-in-place");
err = rp2040_call_rom_func(bank->target, priv, priv->jump_enter_cmd_xip, NULL, 0);
if (err != ERROR_OK)
LOG_ERROR("RP2040 write: failed to flush flash cache");
return err; return err;
} }
@ -240,7 +267,7 @@ static int rp2040_flash_erase(struct flash_bank *bank, unsigned int first, unsig
int err = rp2040_stack_grab_and_prep(bank); int err = rp2040_stack_grab_and_prep(bank);
if (err != ERROR_OK) if (err != ERROR_OK)
return err; goto cleanup;
LOG_DEBUG("Remote call flash_range_erase"); LOG_DEBUG("Remote call flash_range_erase");
@ -258,11 +285,14 @@ static int rp2040_flash_erase(struct flash_bank *bank, unsigned int first, unsig
https://github.com/raspberrypi/pico-bootrom/blob/master/bootrom/program_flash_generic.c https://github.com/raspberrypi/pico-bootrom/blob/master/bootrom/program_flash_generic.c
In theory, the function algorithm provides for erasing both a smaller "sector" (4096 bytes) and In theory, the function algorithm provides for erasing both a smaller "sector" (4096 bytes) and
an optional larger "block" (size and command provided in args). OpenOCD's spi.c only uses "block" sizes. an optional larger "block" (size and command provided in args).
*/ */
err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_range_erase, args, ARRAY_SIZE(args)); err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_range_erase, args, ARRAY_SIZE(args));
cleanup:
rp2040_finalize_stack_free(bank);
return err; return err;
} }
@ -376,11 +406,13 @@ static int rp2040_flash_probe(struct flash_bank *bank)
} }
err = rp2040_stack_grab_and_prep(bank); err = rp2040_stack_grab_and_prep(bank);
if (err != ERROR_OK)
return err;
uint32_t device_id = 0; uint32_t device_id = 0;
if (err == ERROR_OK)
err = rp2040_spi_read_flash_id(target, &device_id); err = rp2040_spi_read_flash_id(target, &device_id);
rp2040_finalize_stack_free(bank);
if (err != ERROR_OK) if (err != ERROR_OK)
return err; return err;