/** * signature.c * * Reading / writing for the user signature region. * * Copyright (c) 2022 Bigscreen, Inc. */ #include #include #include #include "signature.h" #include "flash_mutex.h" uint32_t sig_buffer[USER_SIG_SIZE_WORDS]; // temporary storage for the signature flash region contents User_Signature_State sig_state = SIG_UNINITIALIZED; SemaphoreHandle_t sig_mutex = NULL; // Only allow one access to the raw memory at a time // Reads the 512 byte user signature region in flash memory and copies it to a local ram buffer static uint8_t read_user_signature(void) { uint32_t read_retval; // Make sure the semaphore exists if(sig_mutex == NULL) { sig_mutex = xSemaphoreCreateMutex(); } Flash_Mutex_Lock(); xSemaphoreTake(sig_mutex, portMAX_DELAY); // Disable interrupts for the read sequence. Do not want to have a context switch when Flash is unavailable. irqflags_t flags = cpu_irq_save(); read_retval = efc_perform_read_sequence(EFC, EFC_FCMD_STUS, EFC_FCMD_SPUS, sig_buffer, USER_SIG_SIZE_WORDS); cpu_irq_restore(flags); xSemaphoreGive(sig_mutex); Flash_Mutex_Unlock(); if(EFC_RC_OK == read_retval) { return pdPASS; } else { return pdFAIL; } } // Erases (resets to all 0xFF) the user signature region // No need to use FreeRTOS semaphores as this function disables // interrupts until it is completed. No context switching will occur. static uint8_t erase_user_signature(void) { uint32_t erase_retval; Flash_Mutex_Lock(); erase_retval = efc_perform_command(EFC, EFC_FCMD_EUS, 0); Flash_Mutex_Unlock(); return (EFC_RC_OK == erase_retval); } // copies the current values in the local ram buffer to flash storage // No need to use FreeRTOS semaphores as this function disables // interrupts until it is completed. No context switching will occur. static uint8_t write_user_signature_to_storage(void) { // Process is as follows: // (1) write the entire data storage section to the flash buffer // ...flash buffer is accessible at any flash page address // (2) perform the "write user signature" command uint32_t* first_flash_page; // can program to any valid page within Flash uint8_t flash_error = 0; Flash_Mutex_Lock(); uint32_t cur_int_status = __get_PRIMASK(); if(cur_int_status == 0 ){ __disable_irq(); } first_flash_page = (uint32_t*)(IFLASH_ADDR); // Copy bytes to the destination flash page buffer // Must be performed in 32 bit writes. 8 or 16 bit writes could result in weird stuff // Also, writes must be continuous (contiguous?) from lower to higher address. // So we can't use memcpy. Probably does random weird stuff in write sequence. // Making a (possibly stupid) assumption that data is at least one page (512 bytes) long for(uint16_t i = 0; i < (USER_SIG_SIZE_BYTES / sizeof(uint32_t)); i++) { first_flash_page[i] = sig_buffer[i]; } // Perform program function flash_error = efc_perform_command(EFC, EFC_FCMD_WUS, 0); if(cur_int_status == 0) { __enable_irq(); } Flash_Mutex_Unlock(); if (EFC_RC_OK == flash_error) { return pdPASS; }else{ return pdFAIL; } } // Calculates a CRC8 checksum value over any length of data uint8_t crc8(uint8_t initcrc, const uint8_t* input_data, uint32_t len) { uint32_t i; uint8_t j; const uint8_t* l_input; l_input = input_data; for(i = 0; i < len; i++) { initcrc ^= *l_input; l_input++; for(j = 0; j < 8; j++) { if(initcrc & 0x80u) { initcrc = (initcrc << 1) ^ CRC8POLY; } else { initcrc = (initcrc << 1); } } } return initcrc; } // Returns a pointer to the signature buffer. User code cannot modify this buffer, should instead be copied and modified // Reads from Flash storage before returning a pointer to the local buffer const User_Signature_T* get_signature(void) { if((sig_state == SIG_UNINITIALIZED) || (sig_state == SIG_OVERWRITTEN)) { if(pdPASS == read_user_signature()) { sig_state = SIG_VALID; return (const User_Signature_T*) sig_buffer; } else { return NULL; // User code should check for null before using the buffer. } } else { // no need to re-read flash, can simply return the signature return (const User_Signature_T*) sig_buffer; } } const uint32_t* get_signature_raw(void) { if((sig_state == SIG_UNINITIALIZED) || (sig_state == SIG_OVERWRITTEN)) { if(pdPASS == read_user_signature()) { sig_state = SIG_VALID; return (const uint32_t*) sig_buffer; } else { return NULL; // User code should check for null before using the buffer. } } else { // no need to re-read flash, can simply return the signature return (const uint32_t*) sig_buffer; } } uint8_t update_signature_raw(uint8_t* buf, uint32_t offset, uint32_t len) { uint8_t* sig_buffer_as_bytes = (uint8_t*) sig_buffer; if((buf != NULL) && (offset < USER_SIG_SIZE_BYTES) && ((offset + len) <= USER_SIG_SIZE_BYTES)) { memcpy(&(sig_buffer_as_bytes[offset]), buf, len); sig_state = SIG_OVERWRITTEN; return pdPASS; } return pdFAIL; } // Saves the user signature back to flash uint8_t save_signature(void) { uint8_t retval = pdFAIL; // Point at the buffer as a signature struct User_Signature_T* sig = (User_Signature_T*) sig_buffer; // Only allow saving flash signature region if we have a ram buffer and flash regions that are // different from each other, and the ram buffer has been recently overwritten if(sig_state == SIG_OVERWRITTEN) { // Sanity checks: magic number and crc if(check_signature(sig)) { // all good, we can do the flash write retval = erase_user_signature(); if(pdPASS == retval) { retval = write_user_signature_to_storage(); } } } return retval; } uint8_t save_signature_raw(void) { uint8_t retval = pdFAIL; // Only allow saving flash signature region if we have a ram buffer and flash regions that are // different from each other, and the ram buffer has been recently overwritten if(sig_state == SIG_OVERWRITTEN) { retval = erase_user_signature(); if(pdPASS == retval) { retval = write_user_signature_to_storage(); } } return retval; } bool check_signature(const User_Signature_T* sig) { // Confirms that the signature block is valid: // 1. Pointer is not null. // 2. Magic number is correct. // 3. CRC-8 is valid. if(NULL == sig) { return false; } if(SIG_MAGIC != (sig->magic_num)) { return false; } if(crc8(0xFFu, (const uint8_t*)sig, USER_SIG_STRUCT_SIZE - 1) != sig->crc8){ return false; } return true; } static uint16_t sigv2_find_tag_loc(uint8_t tag) { uint8_t* sig_bytes = (uint8_t*) sig_buffer; uint16_t sig_ptr = 0; uint8_t data_length; uint8_t entry_length; while(sig_ptr < USER_SIG_SIZE_BYTES){ if(SigTag_Invalid == sig_bytes[sig_ptr]) {return 0xFFFFu;} // Reached the end of programmed signature, bust out data_length = sig_bytes[sig_ptr+1]; entry_length = data_length + 3; // 3 more bytes: tag, length, and CRC if(sig_bytes[sig_ptr] == tag) { // Check the CRC on this entry if(crc8(0xFFu, &(sig_bytes[sig_ptr]), entry_length - 1) == sig_bytes[sig_ptr+entry_length-1]) { // it's correct! we can return our location return sig_ptr; } } sig_ptr += entry_length; } // Wasn't found. Return a false value (max 16 bit int) return 0xFFFFu; } bool sigv2_find_tag(uint8_t tag, uint8_t* retval) { // Returns true if tag was found, false if it was not. // If the tag was found, saves the data to retval. // Make sure the array that retval is pointing to // is big enough to hold the data. // Does not change anything in retval if the tag wasn't found. uint8_t* sig_bytes = (uint8_t*)sig_buffer; uint16_t tag_ptr = sigv2_find_tag_loc(tag); if(tag_ptr >= USER_SIG_SIZE_BYTES) { return false; } // tag_ptr+1 is length, and data starts at tag_ptr+2 memcpy(&(retval[0]), &(sig_bytes[tag_ptr+2]), sig_bytes[tag_ptr+1]); return true; } bool sigv2_tag_exists(uint8_t tag) { uint16_t tag_ptr = sigv2_find_tag_loc(tag); if(tag_ptr >= USER_SIG_SIZE_BYTES) { // not found result is 65535, so this block would run return false; } return true; } uint8_t sigv2_tag_length(uint8_t tag) { uint8_t* sig_bytes = (uint8_t*)sig_buffer; uint16_t tag_ptr = sigv2_find_tag_loc(tag); if(tag_ptr >= USER_SIG_SIZE_BYTES) { return 0; // zero length isn't a real entry, so this can be used as an "exists" check too } return sig_bytes[tag_ptr+1]; } uint16_t sigv2_build_signature(uint16_t sig_ptr, uint8_t tag, uint8_t length, uint8_t* data) { // Called repeatedly to build the signature buffer. // Once all the tags have been added, save the buffer into Flash using "save_signature_raw" // Usage - // On the first call, set "sig_ptr" to zero. This means the first location in the signature flash // will be used. This function's return value is the new pointer location (first byte just // after the tag that was just added). On the next and subsequent calls of this function with the // remaining tags, use the previous call return value as "sig_ptr". // Return value 0xFFFF (65535) is used for a failure // First check that the tag we're trying to save isn't invalid if(SigTag_Invalid == tag) { return 0xFFFFu; } // Also check that we can fit this tag in the buffer. uint8_t tag_length = length + 3; // tag, length, data bytes, and CRC if((sig_ptr + tag_length) > USER_SIG_SIZE_BYTES) { return 0xFFFFu; } // Since it can fit, we add it sig_state = SIG_OVERWRITTEN; uint8_t* sig_bytes = (uint8_t*) sig_buffer; sig_bytes[sig_ptr] = tag; sig_bytes[sig_ptr+1] = length; memcpy(&(sig_bytes[sig_ptr+2]), data, length); sig_bytes[sig_ptr+length+2] = crc8(0xFFu, &(sig_bytes[sig_ptr]), tag_length - 1); return sig_ptr + length + 3; } bool sigv2_add_new_tag(uint8_t tag, uint8_t length, uint8_t* data) { uint16_t sig_ptr; // First ensure that the current buffer is actually what's in Flash if(sig_state != SIG_VALID) { if(pdPASS != read_user_signature()) { return false; // failed since we couldn't read the signature region } } // Get the first blank spot and stick this tag on the end sig_ptr = sigv2_find_first_blank(); if(sig_ptr >= USER_SIG_SIZE_BYTES) { return false; // no space available } sig_ptr = sigv2_build_signature(sig_ptr, tag, length, data); save_signature_raw(); return (sig_ptr != 0xFFFFu); } uint16_t sigv2_find_first_blank(void) { // Returns the location of the first blank location. This is represented by a tag with 0xFF. // Note that we can't just search for the byte value 0xFF, because it could be a valid // length, data, or CRC. Only 0xFF in a tag location is considered empty space. // Also returns a tag location if the CRC check is bad. This could be the case if random // data is in the signature for whatever reason. uint8_t* sig_bytes = (uint8_t*) sig_buffer; uint16_t sig_ptr = 0; uint8_t data_length; while(sig_ptr < USER_SIG_SIZE_BYTES) { if(SigTag_Invalid == sig_bytes[sig_ptr]) { return sig_ptr; } data_length = sig_bytes[sig_ptr+1]; if(data_length + sig_ptr + 3u >= USER_SIG_SIZE_BYTES) { // Data was too long, so this entry doesn't fit in the signature // counts as invalid entry. Return it as a blank. return sig_ptr; } if(sig_bytes[sig_ptr + data_length + 2] != crc8(0xFFu, &(sig_bytes[sig_ptr]), data_length + 2)) { return sig_ptr; // invalid CRC, this is also considered a blank } sig_ptr += data_length + 3; // tag, length, crc bytes and the length of data bytes } // got here if the entire signature is full return 0xFFFFu; } void sigv2_upgrade_from_sigv1(void) { // Reads the signature, checks if it's a V1 signature, and if so // reloads it as a V2 signature and saves it. const User_Signature_T* sigv1 = get_signature(); if(check_signature(sigv1)) { // start copying into the new signature format // We need a scratch space for this, as we begin writing the new signature it will get clobbered. // This is a fairly large stack usage, should probably run this function in main() before FreeRTOS // scheduler is started. That way we have a much larger stack limit to work with. User_Signature_T sigv1_copy; memcpy(&sigv1_copy, sigv1, sizeof(User_Signature_T)); // Completely clear out the signature region memset(sig_buffer, 0xFFu, USER_SIG_SIZE_BYTES); uint16_t sig_ptr = 0; sig_ptr = sigv2_build_signature(sig_ptr, SigTag_Serial, 16, (uint8_t*)&(sigv1_copy.serial_number)); if(sig_ptr != 0xFFFFu) { sig_ptr = sigv2_build_signature(sig_ptr, SigTag_RGB_Color, 3, (uint8_t*)&(sigv1_copy.default_color)); } if(sig_ptr != 0xFFFFu) { sig_ptr = sigv2_build_signature(sig_ptr, SigTag_Fan_Speed, 1, (uint8_t*)&(sigv1_copy.default_fan_speed)); } if(sig_ptr != 0xFFFFu) { sig_ptr = sigv2_build_signature(sig_ptr, SigTag_Prox_Disable, 1, (uint8_t*)&(sigv1_copy.prox_disabled)); } if(sig_ptr != 0xFFFFu) { sig_ptr = sigv2_build_signature(sig_ptr, SigTag_Linkbox_v1, 1, (uint8_t*)&(sigv1_copy.linkbox_is_v1)); } if(sig_ptr != 0xFFFFu) { // Got all the variables added, now we can save save_signature_raw(); } } }