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

Feature: ArteryTek AT32F437/435 Read protection unlocking #1956

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
126 changes: 125 additions & 1 deletion src/target/at32f43x.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,127 @@ static bool at32f43_mass_erase(target_s *target)
return true;
}

/* Borrow macros from adiv5_jtag.c */
#define JTAGDP_ACK_OK 0x02U
#define JTAGDP_ACK_WAIT 0x01U

#define IR_DPACC 0xaU
#define IR_APACC 0xbU

uint32_t adiv5_jtag_raw_access_noabort(adiv5_debug_port_s *dp, uint8_t rnw, uint16_t addr, uint32_t value)
{
const bool is_ap = addr & ADIV5_APnDP;
addr &= 0xffU;

const uint64_t request = ((uint64_t)value << 3U) | ((addr >> 1U) & 0x06U) | (rnw ? 1U : 0U);

uint32_t result = 0U;
uint8_t ack = JTAGDP_ACK_WAIT;

jtag_dev_write_ir(dp->dev_index, is_ap ? IR_APACC : IR_DPACC);

platform_timeout_s timeout_progressbar;
platform_timeout_set(&timeout_progressbar, 500U);
platform_timeout_s timeout_erase;
platform_timeout_set(&timeout_erase, 15000U);
while (ack == JTAGDP_ACK_WAIT && !platform_timeout_is_expired(&timeout_erase)) {
uint64_t response;
jtag_dev_shift_dr(dp->dev_index, (uint8_t *)&response, (const uint8_t *)&request, 35);
result = response >> 3U;
ack = response & 0x07U;
platform_delay(5U);
target_print_progress(&timeout_progressbar);
}

if (ack != JTAGDP_ACK_OK) {
DEBUG_ERROR("JTAG access resulted in: %" PRIx32 ":%x\n", result, ack);
raise_exception(EXCEPTION_ERROR, "JTAG-DP invalid ACK");
}

return result;
}

static bool adiv5_swd_raw_access_noabort(adiv5_debug_port_s *dp, uint8_t rnw, uint16_t addr, uint32_t value)
{
const uint8_t request = make_packet_request(rnw, addr);
uint32_t response = 0;
uint8_t ack = SWDP_ACK_WAIT;
platform_timeout_s timeout_progressbar;
platform_timeout_set(&timeout_progressbar, 500U);
platform_timeout_s timeout_erase;
platform_timeout_set(&timeout_erase, 15000U);
while ((ack == SWDP_ACK_WAIT) && !platform_timeout_is_expired(&timeout_erase)) {
swd_proc.seq_out(request, 8U);
ack = swd_proc.seq_in(3U);
/* No data phase */
platform_delay(5U);
target_print_progress(&timeout_progressbar);
}

if (ack != SWDP_ACK_OK) {
DEBUG_ERROR("SWD access has invalid ack %x\n", ack);
raise_exception(EXCEPTION_ERROR, "SWD invalid ACK");
}

if (platform_timeout_is_expired(&timeout_erase)) {
DEBUG_ERROR("%s timed out after %u ms\n", __func__, 15000U);
raise_exception(EXCEPTION_TIMEOUT, "SWD WAIT");
}

if (rnw) {
if (!swd_proc.seq_in_parity(&response, 32U)) {
dp->fault = 1U;
DEBUG_ERROR("SWD access resulted in parity error\n");
raise_exception(EXCEPTION_ERROR, "SWD parity error");
}
} else
swd_proc.seq_out_parity(value, 32U);
/* Idle cycles */
swd_proc.seq_out(0, 8U);
return response;
}

static bool at32f43x_mem_write_noabort(target_s *target, target_addr32_t dest, uint16_t val)
{
const uint32_t src_bytes = val;
const void *src = &src_bytes;
const align_e align = ALIGN_16BIT;
adiv5_access_port_s *ap = cortex_ap(target);

//adi_ap_mem_access_setup(ap, dest, align);
uint32_t csw = ap->csw | ADIV5_AP_CSW_ADDRINC_SINGLE | ADIV5_AP_CSW_SIZE_HALFWORD;
adiv5_ap_write(ap, ADIV5_AP_CSW, csw);
adiv5_dp_write(ap->dp, ADIV5_AP_TAR_LOW, (uint32_t)dest);

uint32_t value = 0;
adiv5_pack_data(dest, src, &value, align);
/* Submit the memory write */
adiv5_dp_write(ap->dp, ADIV5_AP_DRW, value);

/* Poll for completion (RDBUFF will be responding with WAITs) */
volatile uint32_t rdbuff = 0;
TRY (EXCEPTION_ALL) {
//ack = ap->dp->low_access(dp, rnw, addr, value)
if (ap->dp->low_access == adiv5_swd_raw_access)
rdbuff = adiv5_swd_raw_access_noabort(ap->dp, ADIV5_LOW_READ, ADIV5_DP_RDBUFF, 0);
else if (ap->dp->low_access == adiv5_jtag_raw_access)
rdbuff = adiv5_jtag_raw_access_noabort(ap->dp, ADIV5_LOW_READ, ADIV5_DP_RDBUFF, 0);
}
CATCH () {
case EXCEPTION_TIMEOUT:
DEBUG_TARGET("Timeout during scan. Is target stuck in WFI?\n");
break;
case EXCEPTION_ERROR:
DEBUG_TARGET("Exception: %s\n", exception_frame.msg);
break;
default:
return false;
}
(void)rdbuff;

return true;
}

static bool at32f43_option_erase(target_s *target)
{
/* bank_reg_offset is 0, option bytes belong to first bank */
Expand Down Expand Up @@ -683,8 +804,11 @@ static bool at32f43_cmd_option(target_s *target, int argc, const char **argv)
* FIXME: this transaction only completes after typ. 15 seconds (mass erase of both banks of 4032 KiB chip)
* and if BMD ABORTs it after 250 ms, then chip considers erase as incomplete and stays read-protected.
*/
if (!at32f43_option_write_erased(target, 0U, AT32F43x_USD_RDP_KEY))
at32f43_flash_clear_eop(target, 0);
target_mem32_write32(target, AT32F43x_FLASH_CTRL, AT32F43x_FLASH_CTRL_USDPRGM | AT32F43x_FLASH_CTRL_USDULKS);
if (!at32f43x_mem_write_noabort(target, AT32F43x_USD_BASE, AT32F43x_USD_RDP_KEY))
return false;

/* Set EOPB0 to default 0b010 for 384 KB SRAM */
if (!at32f43_option_write_erased(target, 8U, AT32F43x_USD_EOPB0_DEFAULT))
return false;
Expand Down
Loading