feat(pm5): report AT32 MCU, flash size and PM5 target in hw version/status

`hw version` and `hw status` decoded the reported chip id as an Atmel AT91
CIDR. On PM5 (AT32) the id is an ARM DBGMCU IDCODE, so it showed
"MCU Unknown", a bogus flash size (32 KB / 973% used) and "PM3 GENERIC".

- Client: when IfPm5(), print MCU "AT32F437" and the real flash size, and add
  a "PM5" target line, instead of running the AT91 decode.
- Firmware: the flash size can't be derived from the AT32 IDCODE, so the device
  now sends GetChipFlashSize(). It is appended AFTER the version string in the
  CMD_VERSION reply, so the wire layout is unchanged and it stays compatible in
  both directions (old client ignores the trailing bytes; new client length-
  guards and treats a missing value as 0). GetChipFlashSize() already exists for
  both AT91 and AT32.
This commit is contained in:
Nemanja Nedeljkovic
2026-08-19 21:49:43 +02:00
committed by Philippe Teuwen
parent 06bc3239ab
commit 6da7c77427
4 changed files with 67 additions and 6 deletions
+1
View File
@@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added Proxmark5 (PM5/AT32) reporting in `hw version` / `hw status`: correct MCU (AT32F437), flash size and `PM5` target, instead of the AT91-decoded "Unknown / 32 KB / PM3 GENERIC". The device now also reports its on-chip flash size, appended to the `CMD_VERSION` reply in a backward-compatible way (@nemanjan00)
- Fixed `hf mf dump` preserving readable sector trailer Key B data instead of overwriting it with values from the supplied key file (@oSPANNERo)
- Changed `magic_cards_notes.md` - documented the USCUID-UL helper scripts (`hf_mfu_uscuid` / `hf_mf_uscuid_prog`) and how to set the tag signature, replacing the outdated "No implemented commands" note (@c-barron)
- Fixed `hf_mf_uscuid_prog.lua` - corrected the script name shown in its usage text (@c-barron)
+4
View File
@@ -18,6 +18,10 @@ PLATFORM=PM3RDV4
# uncomment the line below
#PLATFORM=PM3ULTIMATE
# For Proxmark5 (PM5, Artery AT32F435/437):
# uncomment the line below
#PLATFORM=PM5
# If you want more than one PLATFORM_EXTRAS option, separate them by spaces:
#PLATFORM_EXTRAS=BTADDON
#PLATFORM_EXTRAS=FLASH
+15 -1
View File
@@ -396,7 +396,21 @@ static void SendVersion(void) {
payload.versionstr_len = strlen(VersionString) + 1;
memcpy(payload.versionstr, VersionString, payload.versionstr_len);
reply_ng(CMD_VERSION, PM3_SUCCESS, (uint8_t *)&payload, 12 + payload.versionstr_len);
uint32_t reply_len = 12 + payload.versionstr_len;
// Append the total on-chip flash size (bytes) AFTER the version string. This is
// backward compatible: older clients stop at versionstr and ignore the trailing
// bytes, and this stays valid when talking to older firmware that omits it. It
// lets the client report memory usage on MCUs whose size can't be derived from
// the chip id (e.g. AT32). Keep the header layout unchanged (do not break the
// CMD_VERSION protocol).
if (reply_len + sizeof(uint32_t) <= sizeof(payload)) {
uint32_t flash_size = GetChipFlashSize();
memcpy(payload.versionstr + payload.versionstr_len, &flash_size, sizeof(flash_size));
reply_len += sizeof(flash_size);
}
reply_ng(CMD_VERSION, PM3_SUCCESS, (uint8_t *)&payload, reply_len);
}
#ifdef CHIP_AT91SAM7S // Only AT91SAM7S chip series need calibration.
+47 -5
View File
@@ -53,7 +53,20 @@
static int CmdHelp(const char *Cmd);
static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) {
static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used, uint32_t flash_size) {
// AT32 (PM5): the chip id is an ARM DBGMCU IDCODE, not an Atmel CIDR, so the
// AT91 decode below does not apply (it would print "Unknown" and a bogus flash
// size). Report the MCU and use the real flash size the device reported.
if (IfPm5()) {
PrintAndLogEx(NORMAL, " MCU....... " _YELLOW_("%s"), "AT32F437");
uint32_t mem_kb = flash_size / 1024;
PrintAndLogEx(NORMAL, " Memory.... " _YELLOW_("%u") " KB ( " _YELLOW_("%2.0f%%") " used )"
, mem_kb
, mem_kb == 0 ? 0.0f : (float)mem_used / (mem_kb * 1024) * 100
);
return;
}
const char *asBuff;
switch (iChipID) {
case 0x270B0A40:
@@ -157,11 +170,24 @@ static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) {
);
}
static void lookupChipID(uint32_t iChipID, uint32_t mem_used) {
static void lookupChipID(uint32_t iChipID, uint32_t mem_used, uint32_t flash_size) {
const char *asBuff;
uint32_t mem_avail = 0;
PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Hardware") " ]");
// AT32 (PM5): the chip id is an ARM DBGMCU IDCODE, not an Atmel CIDR, so the
// verbose AT91 decode below does not apply. Print a short AT32 summary instead.
if (IfPm5()) {
PrintAndLogEx(NORMAL, " --= uC: AT32F437");
uint32_t mem_kb = flash_size / 1024;
PrintAndLogEx(NORMAL, " --= Nonvolatile Program Memory Size: %u KB, Used: %u bytes (%2.0f%%)"
, mem_kb
, mem_used
, mem_kb == 0 ? 0.0f : (float)mem_used / (mem_kb * 1024) * 100
);
return;
}
switch (iChipID) {
case 0x270B0A40:
asBuff = "AT91SAM7S512 Rev A";
@@ -2029,9 +2055,18 @@ void pm3_version_short(void) {
struct p *payload = (struct p *)&resp.data.asBytes;
lookup_chipid_short(payload->id, payload->section_size);
// Flash size (bytes) is appended after the version string by newer
// firmware; 0 if the device didn't send it (older firmware).
uint32_t flash_size = 0;
if (resp.length >= 12 + payload->versionstr_len + sizeof(uint32_t)) {
memcpy(&flash_size, payload->versionstr + payload->versionstr_len, sizeof(flash_size));
}
if (IfPm3Rdv4Fw()) {
lookup_chipid_short(payload->id, payload->section_size, flash_size);
if (IfPm5()) {
PrintAndLogEx(NORMAL, " Target.... %s", _YELLOW_("PM5"));
} else if (IfPm3Rdv4Fw()) {
// validate signature data
rdv40_validation_t mem;
@@ -2234,7 +2269,14 @@ void pm3_version(bool verbose, bool oneliner) {
PrintAndLogEx(NORMAL, " FPGA firmware... %s", _RED_("chip mismatch"));
}
lookupChipID(payload->id, payload->section_size);
// Flash size (bytes) is appended after the version string by newer
// firmware; 0 if the device didn't send it (older firmware).
uint32_t flash_size = 0;
if (resp.length >= 12 + payload->versionstr_len + sizeof(uint32_t)) {
memcpy(&flash_size, payload->versionstr + payload->versionstr_len, sizeof(flash_size));
}
lookupChipID(payload->id, payload->section_size, flash_size);
// Get unique id of mainchip
clearCommandBuffer();