/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ #include #include #include #include #include #ifndef SAFETY_MEMORY_STRIPOUT_DUMP #include #endif /* SAFETY_MEMORY_STRIPOUT_DUMP */ static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out) { int ret = 0; if (!out) return -1002; if (entry_data == SAFETY_MEMORY_NOP_ENTRY_WORD) { out->flag_num = 0U; out->type = SAFETY_MEMORY_ERR_ENTRY_NOP; out->counter = 0U; } else if ((entry_data & 0xFFU) == SAFETY_MEMORY_ERROR_ENTRY_MARKER) { out->flag_num = (uint8_t)((entry_data >> 8U) & 0xFFU); out->type = SAFETY_MEMORY_ERR_ENTRY_FLAG; out->counter = (uint16_t)((entry_data >> 16U) & 0xFFFF); } else { /* Invalid entry */ ret = -1; } return ret; } static uint32_t error_memory_entry_to_word(const struct error_memory_entry *entry) { uint32_t word = 0; switch (entry->type) { case SAFETY_MEMORY_ERR_ENTRY_NOP: word = SAFETY_MEMORY_NOP_ENTRY_WORD; break; case SAFETY_MEMORY_ERR_ENTRY_FLAG: word = (uint32_t)SAFETY_MEMORY_ERROR_ENTRY_MARKER | ((uint32_t)entry->flag_num << 8U) | ((uint32_t)entry->counter << 16U); break; } return word; } static enum safety_memory_state safety_memory_get_header(struct safety_memory_header *header) { int res; enum safety_memory_state ret; if (!header) return SAFETY_MEMORY_INIT_CORRUPTED; res = backup_ram_get_data(0UL, (uint32_t *)header, wordsize_of(struct safety_memory_header)); if (res) return SAFETY_MEMORY_INIT_CORRUPTED; /* Check magic */ if (header->magic != SAFETY_MEMORY_MAGIC) { /* Magic invalid */ ret = SAFETY_MEMORY_INIT_FRESH; goto return_val; } /* Check the header crc */ crc_unit_reset(); crc_unit_input_array((uint32_t *)header, wordsize_of(struct safety_memory_header)); if (crc_unit_get_crc() != 0UL) { ret = SAFETY_MEMORY_INIT_CORRUPTED; goto return_val; } res = 0; if (header->boot_status_offset < wordsize_of(struct safety_memory_header)) res++; if (header->config_overrides_offset < header->boot_status_offset + wordsize_of(struct safety_memory_boot_status)) res++; if (header->config_overrides_len > SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT) res++; if (header->firmware_update_filename < header->config_overrides_offset + header->config_overrides_len) res++; if (header->err_memory_offset < header->firmware_update_filename + (SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE / 4)) res++; if (header->err_memory_end >= backup_ram_get_size_in_words() || header->err_memory_end < header->err_memory_offset) res++; if (res) { /* Error detected: Write new header */ ret = SAFETY_MEMORY_INIT_CORRUPTED; } else { ret = SAFETY_MEMORY_INIT_VALID_MEMORY; } return_val: return ret; } static void safety_memory_write_and_patch_header(struct safety_memory_header *header) { /* Patch the CRC */ crc_unit_reset(); crc_unit_input_array((uint32_t *)header, wordsize_of(struct safety_memory_header) - 1U); header->crc = crc_unit_get_crc(); /* Write to memory */ backup_ram_write_data(0UL, (uint32_t *)header, wordsize_of(*header)); } static void safety_memory_write_new_header(void) { struct safety_memory_header header; header.boot_status_offset = wordsize_of(struct safety_memory_header); header.config_overrides_len = SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT; header.config_overrides_offset = header.boot_status_offset + wordsize_of(struct safety_memory_boot_status); header.firmware_update_filename = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT; header.err_memory_offset = header.firmware_update_filename + (SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE / 4); header.err_memory_end = header.err_memory_offset; header.magic = SAFETY_MEMORY_MAGIC; backup_ram_wipe(); safety_memory_write_and_patch_header(&header); } static int safety_memory_check_crc(void) { struct safety_memory_header header; enum safety_memory_state state = safety_memory_get_header(&header); uint32_t crc_offset; uint32_t data; uint32_t addr; int res; if (state != SAFETY_MEMORY_INIT_VALID_MEMORY) return -1; crc_offset = header.err_memory_end; crc_unit_reset(); for (addr = 0; addr < crc_offset; addr++) { res = backup_ram_get_data(addr, &data, 1UL); if (res) return -2000; crc_unit_input(data); } res = backup_ram_get_data(crc_offset, &data, 1UL); if (res) return -2001; if (crc_unit_get_crc() != data) return -3000; else return 0; } static int safety_memory_gen_crc(void) { struct safety_memory_header header; uint32_t word_addr; uint32_t data; int res; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -1; crc_unit_reset(); for (word_addr = 0; word_addr < header.err_memory_end; word_addr++) { res = backup_ram_get_data(word_addr, &data, 1); if (res) return -2; crc_unit_input(data); } /* Write CRC */ data = crc_unit_get_crc(); res = backup_ram_write_data(header.err_memory_end, &data, 1UL); if (res) return -3; return 0; } int safety_memory_reinit(enum safety_memory_state *found_state) { struct safety_memory_header header; int res; int ret = -1; if (!found_state) return -1001; *found_state = safety_memory_get_header(&header); switch (*found_state) { case SAFETY_MEMORY_INIT_VALID_MEMORY: /* Valid memory detected. Check CRC and error entries */ res = safety_memory_check(); if (res) *found_state = SAFETY_MEMORY_INIT_CORRUPTED; break; case SAFETY_MEMORY_INIT_FRESH: break; case SAFETY_MEMORY_INIT_CORRUPTED: break; default: *found_state = SAFETY_MEMORY_INIT_CORRUPTED; break; } /* Check if memory header has to be written */ if (*found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { safety_memory_write_new_header(); /* If yes, generate new CRC checksum */ res = safety_memory_gen_crc(); if (res) ret = -100; else ret = 0; } else { ret = 0; } return ret; } int safety_memory_init(enum safety_memory_state *found_state) { crc_unit_init(); backup_ram_init(true); return safety_memory_reinit(found_state); } int safety_memory_get_boot_status(struct safety_memory_boot_status *status) { struct safety_memory_header header; int res; if (!status) return -1001; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (safety_memory_check_crc()) return -2001; res = backup_ram_get_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status)); if (res) return -3000; return 0; } int safety_memory_set_boot_status(const struct safety_memory_boot_status *status) { struct safety_memory_header header; int res; if (!status) return -1001; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (safety_memory_check_crc()) return -2001; res = backup_ram_write_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status)); res |= safety_memory_gen_crc(); if (res) return -3000; return 0; } static int safety_memory_check_error_entries(void) { struct safety_memory_header header; uint32_t addr; uint32_t data; int ret = 0; int res; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) { res = backup_ram_get_data(addr, &data, 1UL); if (res) return -100; /* Valid flag entry */ if ((data & 0xFF) == SAFETY_MEMORY_ERROR_ENTRY_MARKER) continue; if (data == SAFETY_MEMORY_NOP_ENTRY_WORD) continue; ret--; } return ret; } int safety_memory_get_error_entry_count(uint32_t *count) { struct safety_memory_header header; if (!count) return -1001; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; *count = header.err_memory_end - header.err_memory_offset; return 0; } int safety_memory_check(void) { int res; res = safety_memory_check_crc(); if (!res) res |= safety_memory_check_error_entries(); return -!!res; } int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry) { struct safety_memory_header header; uint32_t err_mem_count; int ret = -1; int res; uint32_t data; if (!entry) return -1001; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; err_mem_count = header.err_memory_end - header.err_memory_offset; if (idx < err_mem_count && err_mem_count > 0) { res = backup_ram_get_data(header.err_memory_offset + idx, &data, 1UL); if (res) goto return_value; res = word_to_error_memory_entry(data, entry); if (res) goto return_value; ret = 0; } else { /* out of range */ ret = -1001; } return_value: return ret; } int safety_memory_insert_error_entry(struct error_memory_entry *entry) { int res; int ret = -0xFFFF; uint32_t addr; uint32_t data; bool found; uint32_t input_data; struct error_memory_entry current_entry; struct safety_memory_header header; input_data = error_memory_entry_to_word(entry); if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (entry->type == SAFETY_MEMORY_ERR_ENTRY_NOP) { /* Append to end */ if ((header.err_memory_end + 1U) < backup_ram_get_size_in_words()) { /* Still fits in memory */ backup_ram_write_data(header.err_memory_end, &input_data, 1UL); header.err_memory_end++; safety_memory_write_and_patch_header(&header); safety_memory_gen_crc(); ret = 0; } } else if (entry->type == SAFETY_MEMORY_ERR_ENTRY_FLAG) { found = false; for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) { res = backup_ram_get_data(addr, &data, 1UL); if (res) { ret = -1; goto return_value; } res = word_to_error_memory_entry(data, ¤t_entry); if (res) { ret = -2; goto return_value; } if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_FLAG && current_entry.flag_num == entry->flag_num) { found = true; break; } if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_NOP) { found = true; break; } } if (!found) { /* No suitable place found in memory. Append */ if ((addr + 1) < backup_ram_get_size_in_words()) { backup_ram_write_data(addr, &input_data, 1UL); header.err_memory_end++; safety_memory_write_and_patch_header(&header); } else { ret = -3; goto return_value; } } else { if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_NOP) { backup_ram_write_data(addr, &input_data, 1UL); } else { current_entry.counter += entry->counter; if (current_entry.counter < entry->counter) current_entry.counter = 0xFFFF; data = error_memory_entry_to_word(¤t_entry); backup_ram_write_data(addr, &data, 1UL); } } safety_memory_gen_crc(); ret = 0; } else { ret = -1001; } return_value: return ret; } static uint32_t convert_config_override_to_word(const struct config_override *conf_override) { uint32_t data = 0; if (conf_override->type == SAFETY_MEMORY_CONFIG_OVERRIDE_WEIGHT) { data |= 0xAA0000A2UL; data |= ((uint32_t)conf_override->entry.weight_override.flag) << 16; data |= ((uint32_t)conf_override->entry.weight_override.weight) << 8; } else if (conf_override->type == SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTENCE) { data |= 0xBB00008EUL; data |= ((uint32_t)conf_override->entry.persistence_override.flag) << 16; data |= ((uint32_t)(conf_override->entry.persistence_override.persistence ? 1UL : 0UL)) << 8; } return data; } int safety_memory_insert_config_override(struct config_override *config_override) { struct safety_memory_header header; uint32_t idx; uint32_t data; int res; int ret = -3; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (header.config_overrides_len == 0) return -1; for (idx = 0; idx < header.config_overrides_len; idx++) { res = backup_ram_get_data(header.config_overrides_offset + idx, &data, 1UL); if (res) return -2; if (data == 0UL) { data = convert_config_override_to_word(config_override); res = backup_ram_write_data(header.config_overrides_offset + idx, &data, 1UL); if (res) return -4; res = safety_memory_gen_crc(); if (res) return -5; ret = 0; break; } } return ret; } int safety_memory_get_config_override_count(uint32_t *count) { struct safety_memory_header header; uint32_t iter; uint32_t valid_count; uint32_t data; int res; if (!count) return -1001; *count = 0UL; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (header.config_overrides_len == 0) return 0; valid_count = 0; for (iter = 0; iter < header.config_overrides_len; iter++) { res = backup_ram_get_data(header.config_overrides_offset + iter, &data, 1UL); if (res) return -2; if (data != 0) valid_count++; else break; } *count = valid_count; return 0; } int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override) { struct safety_memory_header header; uint32_t data; int res; if (!config_override) return -1002; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (idx >= header.config_overrides_len) return -1001; res = backup_ram_get_data(header.config_overrides_offset + idx, &data, 1UL); if (res) return -1; switch (data & 0xFF) { case 0xA2: /* Weight override */ config_override->type = SAFETY_MEMORY_CONFIG_OVERRIDE_WEIGHT; config_override->entry.weight_override.flag = (data & 0xFF0000UL) >> 16; config_override->entry.weight_override.weight = (data & 0xFF00UL) >> 8; break; case 0x8E: /* persistence override */ config_override->type = SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTENCE; config_override->entry.persistence_override.flag = (data & 0xFF0000UL) >> 16; config_override->entry.persistence_override.persistence = ((data & 0xFF00UL) >> 8) ? true : false; break; default: return -2; } return 0; } #ifndef SAFETY_MEMORY_STRIPOUT_DUMP int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size) { uint32_t safety_mem_size; size_t output_size; const char *backup_mem_ptr; int res; if (!buffer) return -1000; safety_mem_size = backup_ram_get_size_in_words() * 4U; output_size = base64_calculate_encoded_size(safety_mem_size); if (output_size + 1 > buffsize) return -1001; backup_mem_ptr = (const char *)backup_ram_get_base_ptr(); res = base64_encode(backup_mem_ptr, buffer, safety_mem_size, buffsize, &output_size); if (res) return -1; buffer[output_size] = '\0'; if (used_size) *used_size = output_size + 1u; return 0; } #endif /* SAFETY_MEMORY_STRIPOUT_DUMP */ int safety_memory_get_update_filename(char *filename, size_t *outlen) { struct safety_memory_header header; unsigned int i; size_t len = 0u; volatile char *ptr; /* If filename and outlen are both NULL, we don't do anything */ if (!filename && !outlen) return -1; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; /* Get the filename */ ptr = (volatile char *)backup_ram_get_base_ptr(); ptr += (unsigned int)(header.firmware_update_filename * 4); for (i = 0; i < SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE; i++) { if (filename) filename[i] = *ptr; if (*ptr) len++; else break; ptr++; } if (outlen) *outlen = len; return 0; } int safety_memory_set_update_filename(const char *filename) { int ret = 0; size_t len; unsigned int i; struct safety_memory_header header; volatile char *ram_ptr; if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) return -2000; if (!filename) return -1001; len = strnlen(filename, SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE - 1); ram_ptr = backup_ram_get_base_ptr(); ram_ptr += header.firmware_update_filename * 4; for (i = 0u; i < len; i++) ram_ptr[i] = filename[i]; ram_ptr[i] = 0; ret = safety_memory_gen_crc(); return ret; }