[loader] initial implementation of flash checking

main-solar-only
Richard Meadows 2016-08-05 16:10:39 +01:00
rodzic d7e28bed26
commit 91bfcb9d58
5 zmienionych plików z 202 dodań i 122 usunięć

Wyświetl plik

@ -25,11 +25,6 @@
#ifndef FLASH_H
#define FLASH_H
enum flash_state {
FLASH_GOOD, /* checksum matches */
FLASH_BAD_CSUM, /* mismatch */
};
enum flash_state check_flash_state(void);
uint32_t check_and_repair_memory(void);
#endif /* FLASH_H */

Wyświetl plik

@ -25,36 +25,15 @@
#ifndef MEMORY_H
#define MEMORY_H
/**
* Memory layout:
*
* 64-byte pages
* 256-byte rows (erase) - 4 pages
*/
#define TOTAL_PAGES 0x100
#define TOTAL_ROWS 0x40
#define PAGE_MASK 0x7FFC0
#define ROW_MASK 0x7FF00
#define PAGE_SIZE 0x00040
#define ROW_SIZE 0x00100
/**
* Pages assigned to backlog. Currently 256 records
*/
#define BACKLOG_START_PAGE 0x00
#define BACKLOG_END_PAGE 0xff
#define SECTOR_SIZE 0x00100
void mem_chip_erase(void);
void mem_read_memory(uint32_t address, uint8_t* buffer, uint32_t length);
void mem_write_word(uint32_t address, uint32_t word);
void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length);
void mem_erase_sector(uint32_t address);
void mem_read_memory(unsigned int* address, uint8_t* buffer, uint32_t length);
void mem_write_word(unsigned int* address, uint32_t word);
void mem_write_sector(unsigned int* address, uint8_t* buffer);
void mem_erase_sector(unsigned int* address);
uint8_t mem_power_on();
void mem_power_off();
#endif

Wyświetl plik

@ -23,16 +23,46 @@
*/
#include <stdlib.h>
#include <string.h>
#include "samd20.h"
#include "memory.h"
#include "flash.h"
#include "watchdog.h"
/**
* we put our checksum at the last address in flash
*/
volatile unsigned int* flash_checksum =
(unsigned int*)(FLASH_ADDR + FLASH_SIZE - 4);
#define APPLICATION_BASE (0x00004000) /* 16K */
#define APPLICATION_LENGTH (112*1024) /* 112K */
#define D1_START (APPLICATION_BASE)
#define D1_SECTORS (APPLICATION_LENGTH/256)
#define D2_START (APPLICATION_BASE+APPLICATION_LENGTH)
#define D2_SECTORS (D1_SECTORS)
/* Check these are multiples of 64 */
#if (D1_SECTORS & 0x3F)
#error D1_SECTORS _must_ be a mul 64, so checksums fill integer no. of sectors
#endif
#if (D2_SECTORS & 0x3F)
#error D2_SECTORS _must_ be a mul 64, so checksums fill integer no. of sectors
#endif
#define D1_CHECKSUMS_SECTORS (D1_SECTORS/64)
#define D2_CHECKSUMS_SECTORS (D2_SECTORS/64)
/* ROM copy of checksums */
const uint32_t d1_checksums_nvm[D1_SECTORS]
__attribute__ ((aligned (256)))
= { 0xFF };
const uint32_t d2_checksums_nvm[D2_SECTORS]
__attribute__ ((aligned (256)))
= { 0xFF };
/* RAM copy of checksums */
uint32_t d1_checksums_ram[D1_SECTORS];
uint32_t d2_checksums_ram[D2_SECTORS];
/* crc32 calculation */
uint32_t table[256] = { 0xFFFFFFFF };
// ---------------------------- crc32cx --------------------------------
@ -50,15 +80,15 @@ volatile unsigned int* flash_checksum =
who got it from Linux Source base,
www.gelato.unsw.edu.au/lxr/source/lib/crc32.c, lines 105-111. */
unsigned int crc32cx(unsigned int *ptr, size_t length)
uint32_t crc32cx(unsigned int *ptr, size_t length)
{
int j;
unsigned int byte, crc, mask;
unsigned int table[256];
uint32_t byte, crc, mask;
length &= ~0x3; /* must be mutiple of 4 */
/* Set up the table */
if (table[0] == 0xFFFFFFFF) {
for (byte = 0; byte <= 255; byte++) {
crc = byte;
for (j = 7; j >= 0; j--) { // Do eight times.
@ -67,6 +97,7 @@ unsigned int crc32cx(unsigned int *ptr, size_t length)
}
table[byte] = crc;
}
}
/* Through with table setup, now calculate the CRC. */
crc = 0xFFFFFFFF;
@ -83,34 +114,137 @@ unsigned int crc32cx(unsigned int *ptr, size_t length)
}
/**
* 32 bit checksum for the whole memory space
* 32 bit checksum for the given sector
*/
unsigned int checksum_memory(void)
uint32_t checksum_sector(unsigned int* sector)
{
/* do crc */
return crc32cx((unsigned int)FLASH_ADDR, /* start */
FLASH_SIZE - 4); /* length */
return crc32cx((unsigned int*)sector, /* start */
SECTOR_SIZE); /* length */
}
/* /\** */
/* * checks if memory checksum is good */
/* *\/ */
/* enum flash_state check_flash_state(void) */
/* { */
/* unsigned int calculated = checksum_memory(); */
/* if (*flash_checksum == 0xFFFFFFFF) { /\* not written *\/ */
/* /\* write it *\/ */
/* mem_write_word((uint32_t)flash_checksum, calculated); */
/* return FLASH_GOOD; */
/* } else { /\* written *\/ */
/* /\* check it *\/ */
/* if (calculated == *flash_checksum) { */
/* return FLASH_GOOD; */
/* } else { */
/* return FLASH_BAD_CSUM; */
/* } */
/* } */
/* } */
/**
* checks if memory checksum is good
* updates checksum records in nvm
*/
enum flash_state check_flash_state(void)
void update_checksums(const uint32_t* nvm, uint32_t* ram, int sectors)
{
unsigned int calculated = checksum_memory();
int i;
if (*flash_checksum == 0xFFFFFFFF) { /* not written */
/* write it */
mem_write_word((uint32_t)flash_checksum, calculated);
for (i = 0; i < sectors; i++,
nvm += SECTOR_SIZE, ram += SECTOR_SIZE) {
mem_erase_sector((unsigned int*)nvm);
mem_write_sector((unsigned int*)nvm, (uint8_t*)ram);
return FLASH_GOOD;
} else { /* written */
/* check it */
if (calculated == *flash_checksum) {
return FLASH_GOOD;
} else {
return FLASH_BAD_CSUM;
}
kick_the_watchdog();
}
}
/**
* Checks and repairs application memory space
*/
uint32_t check_and_repair_memory(void)
{
unsigned int* address1 = (unsigned int*)D1_START;
unsigned int* address2 = (unsigned int*)D2_START;
unsigned int check1, check2;
uint8_t update_d1_check = 0, update_d2_check = 0;
uint32_t errors_found = 0;
int i;
/* load checksums */
memcpy(d1_checksums_ram, d1_checksums_nvm, D1_SECTORS * sizeof(uint32_t));
memcpy(d2_checksums_ram, d2_checksums_nvm, D2_SECTORS * sizeof(uint32_t));
/* foreach sector */
for (i = 0; i < D1_SECTORS; i++,
address1 += SECTOR_SIZE, address2 += SECTOR_SIZE) {
/* calculate checksums */
check1 = checksum_sector(address1);
check2 = checksum_sector(address2);
if ((check1 == d1_checksums_ram[i]) &&
(check2 == d2_checksums_ram[i])) {
/* all good */
continue;
} else if ((check1 != d1_checksums_ram[i]) &&
(check2 == d2_checksums_ram[i])) {
/* restore d1 */
mem_erase_sector(address1);
mem_write_sector(address1, (uint8_t*)address2);
/* update d1 checksum */
d1_checksums_ram[i] = check2;
/* flag checksum for write */
update_d1_check = 1;
errors_found++;
} else if ((check1 == d1_checksums_ram[i]) &&
(check2 != d2_checksums_ram[i])) {
/* restore d2 */
mem_erase_sector(address2);
mem_write_sector(address2, (uint8_t*)address1);
/* update d2 checksum */
d2_checksums_ram[i] = check1;
/* flag checksum for write */
update_d2_check = 1;
errors_found++;
} else {
/* both bad, restore d2 and calculate both checksums */
/* restore d2 */
mem_erase_sector(address2);
mem_write_sector(address2, (uint8_t*)address1);
/* update both checksums */
d1_checksums_ram[i] = check1;
d2_checksums_ram[i] = check1;
/* flag checksums for write */
update_d1_check = 1;
update_d2_check = 1;
/* don't count these as errors, probably happen when
programming for first time */
}
kick_the_watchdog();
}
/* update checksums */
if (update_d1_check) { update_checksums(d1_checksums_nvm,
d1_checksums_ram,
D1_CHECKSUMS_SECTORS); }
if (update_d2_check) { update_checksums(d2_checksums_nvm,
d2_checksums_ram,
D2_CHECKSUMS_SECTORS); }
return errors_found;
}

Wyświetl plik

@ -33,6 +33,7 @@
#include "analogue.h"
#include "xosc.h"
#include "loader.h"
#include "flash.h"
/**
* MAIN
@ -56,11 +57,11 @@ int main(void)
}
/* Check battery */
} while (get_battery() < 4.0);
} while (get_battery() < 3.0);
/* Check and repair memory */
check_and_repair_memory();
/* Transfer control to application */
transfer_to_application();

Wyświetl plik

@ -33,15 +33,6 @@
*/
#define FIX_ERRATA_REV_C_FLASH_10804
#define MEM_SIZE 16384 /* 16 KB */
/**
* Allocate a 16KB section of flash memory, aligned to an NVM row
*/
const uint8_t nvm_section[MEM_SIZE]
__attribute__ ((aligned (256)))
__attribute__ ((section (".eeprom")))
= { 0xFF };
/**
* Poll the status register until the busy bit is cleared
*/
@ -57,47 +48,17 @@ void _mem_wait_for_done(void)
* =============================================================================
*/
/**
* Simple Commands
*/
void mem_chip_erase(void)
{
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */
uint32_t temp = NVMCTRL->CTRLB.reg;
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
#endif
/* erase each row */
for (int n = 0; n < TOTAL_ROWS; n++) {
/* write address */
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + (n*ROW_SIZE)) >> 1;
/* unlock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
/* erase */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
_mem_wait_for_done();
/* lock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
}
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* restore CTRLB */
NVMCTRL->CTRLB.reg = temp;
#endif
}
/**
* Read memory
*/
void mem_read_memory(uint32_t address, uint8_t* buffer, uint32_t length)
void mem_read_memory(unsigned int* address, uint8_t* buffer, uint32_t length)
{
memcpy(buffer, nvm_section + address, length);
memcpy(buffer, address, length);
}
/**
* Write single word
*/
void mem_write_word(uint32_t address, uint32_t word)
void mem_write_word(unsigned int* address, uint32_t word)
{
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */
@ -123,21 +84,28 @@ void mem_write_word(uint32_t address, uint32_t word)
#endif
}
/**
* Write 64-byte page. Address should be page aligned
* Write 256-byte sector. Address must be sector aligned
*/
void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
void mem_write_sector(unsigned int* address, uint8_t* buffer)
{
int i;
uint8_t tempbuf[0x40];
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */
uint32_t temp = NVMCTRL->CTRLB.reg;
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
#endif
if ((address < MEM_SIZE) && (length <= PAGE_SIZE)) {
/* align address with sector */
address = (unsigned int*)((uint32_t)address & ~0xFF);
for (i = 0; i < 4; i++) { /* write by page */
/* write address */
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + address) >> 1;
/* write data. length must be multiple of two */
memcpy((void*)(nvm_section + address), buffer, length & ~0x1);
NVMCTRL->ADDR.reg = (uint32_t)(address) >> 1;
/* write page. length must be multiple of two */
memcpy((void*)tempbuf, buffer, 0x40);
memcpy((void*)address, tempbuf, 0x40);
/* unlock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
/* write page */
@ -145,8 +113,13 @@ void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
_mem_wait_for_done();
/* lock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
/* next page */
address += 0x40;
buffer += 0x40;
}
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* restore CTRLB */
NVMCTRL->CTRLB.reg = temp;
@ -155,7 +128,7 @@ void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
/**
* Erase 256-byte sector.
*/
void mem_erase_sector(uint32_t address)
void mem_erase_sector(unsigned int* address)
{
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */
@ -163,18 +136,16 @@ void mem_erase_sector(uint32_t address)
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
#endif
if (address < MEM_SIZE) {
/* write address */
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + address) >> 1;
/* unlock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
_mem_wait_for_done();
/* erase row */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
_mem_wait_for_done();
/* lock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
}
/* write address */
NVMCTRL->ADDR.reg = (uint32_t)(address) >> 1;
/* unlock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
_mem_wait_for_done();
/* erase row */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
_mem_wait_for_done();
/* lock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
#ifdef FIX_ERRATA_REV_C_FLASH_10804
/* restore CTRLB */