#include "plsr_persistence.h" #include #include #define PLSR_HSD_BACKUP_MAGIC (0x504C4853UL) #define PLSR_HSD_BACKUP_VERSION (1U) #define PLSR_BACKUP_SLOT_A_OFFSET (0x0100UL) #define PLSR_BACKUP_SLOT_STRIDE (0x0200UL) #define PLSR_SFD_FLASH_MAGIC (0x504C5346UL) #define PLSR_SFD_FLASH_VERSION (1U) #define PLSR_SFD_FLASH_SLOT_A_ADDRESS (0x080C0000UL) #define PLSR_SFD_FLASH_SLOT_B_ADDRESS (0x080E0000UL) #define PLSR_SFD_FLASH_SECTOR_SIZE (0x00020000UL) #define PLSR_SFD_AXIS_STRIDE (130U) #define PLSR_SFD_PARAMETER_SET_COUNT (4U) #define PLSR_SFD_PARAMETER_SET_OFFSET (50U) #define PLSR_SFD_PARAMETER_SET_STRIDE (20U) #define PLSR_SFD_DEFAULT_MAX_SPEED (100000UL) typedef struct { uint32_t magic; uint16_t version; uint16_t payloadLength; uint32_t generation; PLSR_HSD_DATA data; uint32_t crc32; } PLSR_HSD_BACKUP_RECORD; typedef struct { uint32_t magic; uint16_t version; uint16_t payloadLength; uint32_t generation; } PLSR_SFD_FLASH_HEADER; typedef struct { uint32_t magic; uint16_t version; uint16_t payloadLength; uint32_t generation; PLSR_SFD_DATA data; uint32_t crc32; } PLSR_SFD_FLASH_RECORD; #ifdef PLSR_HOST_TEST static PLSR_HSD_BACKUP_RECORD PlsrHostBackupSlots[2]; static PLSR_SFD_FLASH_RECORD PlsrHostSfdSlots[2]; static PLSR_TEST_SFD_FAULT PlsrHostSfdFault; #else #include "stm32f4xx.h" #include "stm32f4xx_hal.h" #include "stm32f4xx_hal_flash_ex.h" #endif static uint32_t PlsrPersistenceCrc32Update(uint32_t crc, const volatile uint8_t *data, uint32_t length) { uint32_t index; uint8_t bit; for (index = 0U; index < length; index++) { crc ^= data[index]; for (bit = 0U; bit < 8U; bit++) { if ((crc & 1UL) != 0UL) { crc = (crc >> 1U) ^ 0xEDB88320UL; } else { crc >>= 1U; } } } return crc; } static uint32_t PlsrPersistenceCrc32(const volatile uint8_t *data, uint32_t length) { return ~PlsrPersistenceCrc32Update(0xFFFFFFFFUL, data, length); } static volatile PLSR_HSD_BACKUP_RECORD *PlsrPersistenceGetHsdSlot( uint8_t slot) { #ifdef PLSR_HOST_TEST return &PlsrHostBackupSlots[slot]; #else uint32_t offset = PLSR_BACKUP_SLOT_A_OFFSET + (uint32_t)slot * PLSR_BACKUP_SLOT_STRIDE; return (volatile PLSR_HSD_BACKUP_RECORD *)(BKPSRAM_BASE + offset); #endif } static uint8_t PlsrPersistenceHsdRecordIsValid( const volatile PLSR_HSD_BACKUP_RECORD *record) { uint32_t expectedCrc; if ((record->magic != PLSR_HSD_BACKUP_MAGIC) || (record->version != PLSR_HSD_BACKUP_VERSION) || (record->payloadLength != sizeof(PLSR_HSD_DATA))) { return 0U; } expectedCrc = PlsrPersistenceCrc32( (const volatile uint8_t *)record, (uint32_t)offsetof(PLSR_HSD_BACKUP_RECORD, crc32)); return (expectedCrc == record->crc32) ? 1U : 0U; } static uint8_t PlsrPersistenceGenerationIsNewer(uint32_t left, uint32_t right) { return (((int32_t)(left - right)) > 0) ? 1U : 0U; } static volatile PLSR_SFD_FLASH_RECORD *PlsrPersistenceGetSfdSlot( uint8_t slot) { #ifdef PLSR_HOST_TEST return &PlsrHostSfdSlots[slot]; #else uint32_t address = (slot == 0U) ? PLSR_SFD_FLASH_SLOT_A_ADDRESS : PLSR_SFD_FLASH_SLOT_B_ADDRESS; return (volatile PLSR_SFD_FLASH_RECORD *)address; #endif } static uint8_t PlsrPersistenceSfdRecordIsValid( const volatile PLSR_SFD_FLASH_RECORD *record) { uint32_t expectedCrc; if ((record->magic != PLSR_SFD_FLASH_MAGIC) || (record->version != PLSR_SFD_FLASH_VERSION) || (record->payloadLength != sizeof(PLSR_SFD_DATA))) { return 0U; } expectedCrc = PlsrPersistenceCrc32( (const volatile uint8_t *)record, (uint32_t)offsetof(PLSR_SFD_FLASH_RECORD, crc32)); return (expectedCrc == record->crc32) ? 1U : 0U; } static void PlsrPersistenceCopySfdFromVolatile( PLSR_SFD_DATA *destination, const volatile PLSR_SFD_DATA *source) { uint16_t index; for (index = 0U; index < PLSR_SFD_CONFIG_COUNT; index++) { destination->config[index] = source->config[index]; } } static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) { uint16_t axisOffset; uint16_t parameterOffset; uint8_t axis; uint8_t parameterSet; (void)memset(data, 0, sizeof(*data)); for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { axisOffset = (uint16_t)((uint16_t)axis * PLSR_SFD_AXIS_STRIDE); /* Explicit Xinje defaults from the SFD parameter table. */ data->config[axisOffset + 7U] = 10; data->config[axisOffset + 27U] = 20; data->config[axisOffset + 43U] = 0x0201; for (parameterSet = 0U; parameterSet < PLSR_SFD_PARAMETER_SET_COUNT; parameterSet++) { parameterOffset = (uint16_t)( axisOffset + PLSR_SFD_PARAMETER_SET_OFFSET + (uint16_t)parameterSet * PLSR_SFD_PARAMETER_SET_STRIDE); data->config[parameterOffset + 6U] = (int32_t)(PLSR_SFD_DEFAULT_MAX_SPEED & 0xFFFFUL); data->config[parameterOffset + 7U] = (int32_t)(PLSR_SFD_DEFAULT_MAX_SPEED >> 16U); } } } static PLSR_PERSISTENCE_RESULT PlsrPersistenceBeginSfdOperation(void) { #ifdef PLSR_HOST_TEST return PLSR_PERSISTENCE_OK; #else if (HAL_FLASH_Unlock() != HAL_OK) { return PLSR_PERSISTENCE_PROGRAM_FAILED; } __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); return PLSR_PERSISTENCE_OK; #endif } static PLSR_PERSISTENCE_RESULT PlsrPersistenceEndSfdOperation( PLSR_PERSISTENCE_RESULT result) { #ifdef PLSR_HOST_TEST return result; #else if ((HAL_FLASH_Lock() != HAL_OK) && (result == PLSR_PERSISTENCE_OK)) { return PLSR_PERSISTENCE_PROGRAM_FAILED; } return result; #endif } static PLSR_PERSISTENCE_RESULT PlsrPersistenceEraseSfdSlot(uint8_t slot) { #ifdef PLSR_HOST_TEST if (PlsrHostSfdFault == PLSR_TEST_SFD_FAULT_ERASE) { PlsrHostSfdFault = PLSR_TEST_SFD_FAULT_NONE; return PLSR_PERSISTENCE_ERASE_FAILED; } (void)memset(&PlsrHostSfdSlots[slot], 0xFF, sizeof(PlsrHostSfdSlots[slot])); return PLSR_PERSISTENCE_OK; #else FLASH_EraseInitTypeDef erase; uint32_t sectorError = 0xFFFFFFFFUL; erase.TypeErase = FLASH_TYPEERASE_SECTORS; erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; erase.Sector = (slot == 0U) ? FLASH_SECTOR_10 : FLASH_SECTOR_11; erase.NbSectors = 1U; return (HAL_FLASHEx_Erase(&erase, §orError) == HAL_OK) ? PLSR_PERSISTENCE_OK : PLSR_PERSISTENCE_ERASE_FAILED; #endif } static PLSR_PERSISTENCE_RESULT PlsrPersistenceProgramSfdWord( volatile uint32_t *destination, uint32_t value) { #ifdef PLSR_HOST_TEST if (PlsrHostSfdFault == PLSR_TEST_SFD_FAULT_PROGRAM) { PlsrHostSfdFault = PLSR_TEST_SFD_FAULT_NONE; return PLSR_PERSISTENCE_PROGRAM_FAILED; } if ((*destination & value) != value) { return PLSR_PERSISTENCE_PROGRAM_FAILED; } *destination &= value; return PLSR_PERSISTENCE_OK; #else return (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)destination, value) == HAL_OK) ? PLSR_PERSISTENCE_OK : PLSR_PERSISTENCE_PROGRAM_FAILED; #endif } static uint8_t PlsrPersistenceSfdBodyMatches( const volatile uint32_t *destination, const PLSR_SFD_FLASH_HEADER *header, const PLSR_SFD_DATA *data, uint32_t crc32) { const uint32_t *headerWords = (const uint32_t *)header; const uint32_t *dataWords = (const uint32_t *)data; uint32_t headerWordCount = (uint32_t)(sizeof(*header) / sizeof(uint32_t)); uint32_t dataWordCount = (uint32_t)(sizeof(*data) / sizeof(uint32_t)); uint32_t index; for (index = 1U; index < headerWordCount; index++) { if (destination[index] != headerWords[index]) { return 0U; } } for (index = 0U; index < dataWordCount; index++) { if (destination[headerWordCount + index] != dataWords[index]) { return 0U; } } if (destination[headerWordCount + dataWordCount] != crc32) { return 0U; } return 1U; } static void PlsrPersistenceCopyHsdFromVolatile( PLSR_HSD_DATA *destination, const volatile PLSR_HSD_DATA *source) { uint16_t index; for (index = 0U; index < PLSR_HSD_RUNTIME_COUNT; index++) { destination->runtime[index] = source->runtime[index]; } for (index = 0U; index < PLSR_HSD_CONFIG_COUNT; index++) { destination->config[index] = source->config[index]; } } static void PlsrPersistenceWriteRecord( volatile PLSR_HSD_BACKUP_RECORD *destination, const PLSR_HSD_BACKUP_RECORD *source) { const uint32_t *sourceWords = (const uint32_t *)source; volatile uint32_t *destinationWords = (volatile uint32_t *)destination; uint32_t wordCount = (uint32_t)(sizeof(PLSR_HSD_BACKUP_RECORD) / sizeof(uint32_t)); uint32_t index; destination->magic = 0UL; #ifndef PLSR_HOST_TEST __DMB(); #endif for (index = 1U; index < wordCount; index++) { destinationWords[index] = sourceWords[index]; } #ifndef PLSR_HOST_TEST __DMB(); #endif destination->magic = PLSR_HSD_BACKUP_MAGIC; #ifndef PLSR_HOST_TEST __DMB(); #endif } PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadHsd(PLSR_HSD_DATA *data) { volatile PLSR_HSD_BACKUP_RECORD *slotA; volatile PLSR_HSD_BACKUP_RECORD *slotB; const volatile PLSR_HSD_BACKUP_RECORD *selected; uint32_t generationA; uint32_t generationB; uint8_t validA; uint8_t validB; if (data == NULL) { return PLSR_PERSISTENCE_INVALID_ARGUMENT; } slotA = PlsrPersistenceGetHsdSlot(0U); slotB = PlsrPersistenceGetHsdSlot(1U); validA = PlsrPersistenceHsdRecordIsValid(slotA); validB = PlsrPersistenceHsdRecordIsValid(slotB); if ((validA == 0U) && (validB == 0U)) { (void)memset(data, 0, sizeof(*data)); return PLSR_PERSISTENCE_DEFAULTED; } if ((validA != 0U) && (validB != 0U)) { generationA = slotA->generation; generationB = slotB->generation; selected = (PlsrPersistenceGenerationIsNewer(generationB, generationA) != 0U) ? slotB : slotA; } else { selected = (validA != 0U) ? slotA : slotB; } PlsrPersistenceCopyHsdFromVolatile(data, &selected->data); return PLSR_PERSISTENCE_OK; } PLSR_PERSISTENCE_RESULT PlsrPersistenceSaveHsd(const PLSR_HSD_DATA *data) { volatile PLSR_HSD_BACKUP_RECORD *slotA; volatile PLSR_HSD_BACKUP_RECORD *slotB; volatile PLSR_HSD_BACKUP_RECORD *target; PLSR_HSD_BACKUP_RECORD record; uint32_t newestGeneration = 0UL; uint32_t generationA; uint32_t generationB; uint8_t validA; uint8_t validB; if (data == NULL) { return PLSR_PERSISTENCE_INVALID_ARGUMENT; } if (sizeof(PLSR_HSD_BACKUP_RECORD) > PLSR_BACKUP_SLOT_STRIDE) { return PLSR_PERSISTENCE_VERIFY_FAILED; } slotA = PlsrPersistenceGetHsdSlot(0U); slotB = PlsrPersistenceGetHsdSlot(1U); validA = PlsrPersistenceHsdRecordIsValid(slotA); validB = PlsrPersistenceHsdRecordIsValid(slotB); if ((validA != 0U) && (validB != 0U)) { generationA = slotA->generation; generationB = slotB->generation; if (PlsrPersistenceGenerationIsNewer(generationB, generationA) != 0U) { newestGeneration = generationB; target = slotA; } else { newestGeneration = generationA; target = slotB; } } else if (validA != 0U) { newestGeneration = slotA->generation; target = slotB; } else if (validB != 0U) { newestGeneration = slotB->generation; target = slotA; } else { target = slotA; } (void)memset(&record, 0, sizeof(record)); record.magic = PLSR_HSD_BACKUP_MAGIC; record.version = PLSR_HSD_BACKUP_VERSION; record.payloadLength = (uint16_t)sizeof(PLSR_HSD_DATA); record.generation = newestGeneration + 1UL; record.data = *data; record.crc32 = PlsrPersistenceCrc32( (const volatile uint8_t *)&record, (uint32_t)offsetof(PLSR_HSD_BACKUP_RECORD, crc32)); PlsrPersistenceWriteRecord(target, &record); return (PlsrPersistenceHsdRecordIsValid(target) != 0U) ? PLSR_PERSISTENCE_OK : PLSR_PERSISTENCE_VERIFY_FAILED; } void PlsrPersistenceResetHsd(void) { PlsrPersistenceGetHsdSlot(0U)->magic = 0UL; PlsrPersistenceGetHsdSlot(1U)->magic = 0UL; #ifndef PLSR_HOST_TEST __DMB(); #endif } PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadSfd(PLSR_SFD_DATA *data) { volatile PLSR_SFD_FLASH_RECORD *slotA; volatile PLSR_SFD_FLASH_RECORD *slotB; const volatile PLSR_SFD_FLASH_RECORD *selected; uint32_t generationA; uint32_t generationB; uint8_t validA; uint8_t validB; if (data == NULL) { return PLSR_PERSISTENCE_INVALID_ARGUMENT; } slotA = PlsrPersistenceGetSfdSlot(0U); slotB = PlsrPersistenceGetSfdSlot(1U); validA = PlsrPersistenceSfdRecordIsValid(slotA); validB = PlsrPersistenceSfdRecordIsValid(slotB); if ((validA == 0U) && (validB == 0U)) { PlsrPersistenceApplySfdDefaults(data); return PLSR_PERSISTENCE_DEFAULTED; } if ((validA != 0U) && (validB != 0U)) { generationA = slotA->generation; generationB = slotB->generation; selected = (PlsrPersistenceGenerationIsNewer(generationB, generationA) != 0U) ? slotB : slotA; } else { selected = (validA != 0U) ? slotA : slotB; } PlsrPersistenceCopySfdFromVolatile(data, &selected->data); return PLSR_PERSISTENCE_OK; } PLSR_PERSISTENCE_RESULT PlsrPersistenceSaveSfd(const PLSR_SFD_DATA *data) { volatile PLSR_SFD_FLASH_RECORD *slotA; volatile PLSR_SFD_FLASH_RECORD *slotB; volatile PLSR_SFD_FLASH_RECORD *target; volatile uint32_t *targetWords; const uint32_t *headerWords; const uint32_t *dataWords; PLSR_SFD_FLASH_HEADER header; PLSR_PERSISTENCE_RESULT result; uint32_t newestGeneration = 0UL; uint32_t generationA; uint32_t generationB; uint32_t headerWordCount; uint32_t dataWordCount; uint32_t crcState; uint32_t crc32; uint32_t index; uint8_t targetSlot; uint8_t validA; uint8_t validB; if (data == NULL) { return PLSR_PERSISTENCE_INVALID_ARGUMENT; } if (sizeof(PLSR_SFD_FLASH_RECORD) > PLSR_SFD_FLASH_SECTOR_SIZE) { return PLSR_PERSISTENCE_VERIFY_FAILED; } slotA = PlsrPersistenceGetSfdSlot(0U); slotB = PlsrPersistenceGetSfdSlot(1U); validA = PlsrPersistenceSfdRecordIsValid(slotA); validB = PlsrPersistenceSfdRecordIsValid(slotB); if ((validA != 0U) && (validB != 0U)) { generationA = slotA->generation; generationB = slotB->generation; if (PlsrPersistenceGenerationIsNewer(generationB, generationA) != 0U) { newestGeneration = generationB; target = slotA; targetSlot = 0U; } else { newestGeneration = generationA; target = slotB; targetSlot = 1U; } } else if (validA != 0U) { newestGeneration = slotA->generation; target = slotB; targetSlot = 1U; } else if (validB != 0U) { newestGeneration = slotB->generation; target = slotA; targetSlot = 0U; } else { target = slotA; targetSlot = 0U; } header.magic = PLSR_SFD_FLASH_MAGIC; header.version = PLSR_SFD_FLASH_VERSION; header.payloadLength = (uint16_t)sizeof(PLSR_SFD_DATA); header.generation = newestGeneration + 1UL; crcState = PlsrPersistenceCrc32Update( 0xFFFFFFFFUL, (const volatile uint8_t *)&header, (uint32_t)sizeof(header)); crcState = PlsrPersistenceCrc32Update( crcState, (const volatile uint8_t *)data, (uint32_t)sizeof(*data)); crc32 = ~crcState; headerWordCount = (uint32_t)(sizeof(header) / sizeof(uint32_t)); dataWordCount = (uint32_t)(sizeof(*data) / sizeof(uint32_t)); headerWords = (const uint32_t *)&header; dataWords = (const uint32_t *)data; targetWords = (volatile uint32_t *)target; result = PlsrPersistenceBeginSfdOperation(); if (result != PLSR_PERSISTENCE_OK) { return result; } result = PlsrPersistenceEraseSfdSlot(targetSlot); if (result != PLSR_PERSISTENCE_OK) { return PlsrPersistenceEndSfdOperation(result); } /* The valid magic is committed last so an interrupted write stays invalid. */ for (index = 1U; index < headerWordCount; index++) { result = PlsrPersistenceProgramSfdWord(&targetWords[index], headerWords[index]); if (result != PLSR_PERSISTENCE_OK) { return PlsrPersistenceEndSfdOperation(result); } } for (index = 0U; index < dataWordCount; index++) { result = PlsrPersistenceProgramSfdWord( &targetWords[headerWordCount + index], dataWords[index]); if (result != PLSR_PERSISTENCE_OK) { return PlsrPersistenceEndSfdOperation(result); } } result = PlsrPersistenceProgramSfdWord( &targetWords[headerWordCount + dataWordCount], crc32); if (result != PLSR_PERSISTENCE_OK) { return PlsrPersistenceEndSfdOperation(result); } #ifdef PLSR_HOST_TEST if (PlsrHostSfdFault == PLSR_TEST_SFD_FAULT_VERIFY) { targetWords[1] ^= 1UL; PlsrHostSfdFault = PLSR_TEST_SFD_FAULT_NONE; } #endif if (PlsrPersistenceSfdBodyMatches(targetWords, &header, data, crc32) == 0U) { return PlsrPersistenceEndSfdOperation( PLSR_PERSISTENCE_VERIFY_FAILED); } #ifdef PLSR_HOST_TEST if (PlsrHostSfdFault == PLSR_TEST_SFD_FAULT_BEFORE_COMMIT) { PlsrHostSfdFault = PLSR_TEST_SFD_FAULT_NONE; return PlsrPersistenceEndSfdOperation( PLSR_PERSISTENCE_PROGRAM_FAILED); } #endif result = PlsrPersistenceProgramSfdWord(&targetWords[0], headerWords[0]); if (result != PLSR_PERSISTENCE_OK) { return PlsrPersistenceEndSfdOperation(result); } result = (PlsrPersistenceSfdRecordIsValid(target) != 0U) ? PLSR_PERSISTENCE_OK : PLSR_PERSISTENCE_VERIFY_FAILED; return PlsrPersistenceEndSfdOperation(result); } PLSR_PERSISTENCE_RESULT PlsrPersistenceEraseSfd(void) { PLSR_PERSISTENCE_RESULT result; result = PlsrPersistenceBeginSfdOperation(); if (result != PLSR_PERSISTENCE_OK) { return result; } result = PlsrPersistenceEraseSfdSlot(0U); if (result == PLSR_PERSISTENCE_OK) { result = PlsrPersistenceEraseSfdSlot(1U); } return PlsrPersistenceEndSfdOperation(result); } #ifdef PLSR_HOST_TEST void PlsrPersistenceTestResetStorage(void) { (void)memset(PlsrHostBackupSlots, 0, sizeof(PlsrHostBackupSlots)); (void)memset(PlsrHostSfdSlots, 0xFF, sizeof(PlsrHostSfdSlots)); PlsrHostSfdFault = PLSR_TEST_SFD_FAULT_NONE; } void PlsrPersistenceTestCorruptNewestHsd(void) { volatile PLSR_HSD_BACKUP_RECORD *slotA = PlsrPersistenceGetHsdSlot(0U); volatile PLSR_HSD_BACKUP_RECORD *slotB = PlsrPersistenceGetHsdSlot(1U); uint8_t validA = PlsrPersistenceHsdRecordIsValid(slotA); uint8_t validB = PlsrPersistenceHsdRecordIsValid(slotB); volatile PLSR_HSD_BACKUP_RECORD *newest; if ((validA == 0U) && (validB == 0U)) { return; } if ((validA != 0U) && (validB != 0U)) { newest = (PlsrPersistenceGenerationIsNewer(slotB->generation, slotA->generation) != 0U) ? slotB : slotA; } else { newest = (validA != 0U) ? slotA : slotB; } newest->crc32 ^= 1UL; } void PlsrPersistenceTestCorruptNewestSfd(void) { volatile PLSR_SFD_FLASH_RECORD *slotA = PlsrPersistenceGetSfdSlot(0U); volatile PLSR_SFD_FLASH_RECORD *slotB = PlsrPersistenceGetSfdSlot(1U); uint8_t validA = PlsrPersistenceSfdRecordIsValid(slotA); uint8_t validB = PlsrPersistenceSfdRecordIsValid(slotB); volatile PLSR_SFD_FLASH_RECORD *newest; if ((validA == 0U) && (validB == 0U)) { return; } if ((validA != 0U) && (validB != 0U)) { newest = (PlsrPersistenceGenerationIsNewer(slotB->generation, slotA->generation) != 0U) ? slotB : slotA; } else { newest = (validA != 0U) ? slotA : slotB; } newest->crc32 ^= 1UL; } void PlsrPersistenceTestSetSfdFault(PLSR_TEST_SFD_FAULT fault) { PlsrHostSfdFault = fault; } #endif