flash Kinetis: make FCF protection more user friendly

The Flash Configuration Field on Kinetis devices requires protection
because it is located in program flash space (at 0x400) and writing
an improper data to it may permanently lock the device. Even an erased
flash sector containing FCF engages security lock (not permanent one)
on the next reset or power cycle.

'kinetis fcf_source protection' mode was introduced in the change #3562.
Flash driver in this mode sets FCF immediately after sector erase to
prevent unintentional security lock. To do so the driver needs to know
FCF values before flash image data is actually processed. Flash
protection bits are available in bank structure, FOPT can be set by
'kinetis fopt' command and securing device by FSEC is not supported.

Nevertheless an inexperienced user flashed the device using an image
with FCF values different from those set in OpenOCD config and
concluded programming did not work as some verify errors showed.

This change tries to write maximum possible from image data
retaining FCF protection.

Check FCF in programmed data and report if some field differs from
values set by OpenOCD flash block protection and 'kinetis fopt' command.
Warn user about verify errors caused by FCF protection.

On devices with ECC flash (K26, K66 and KV5x) it is impossible to change
already programmed FCF - it would result in an ECC error. As FCF was
written just after erase in 'kinetis fcf_source protection' mode
the warning issued during flash write is the only possible action.

On non-ECC flash devices use cumulative flash programming to
set FCF values requested in programmed image data.
Use FSEC from programmed data only if it does not request a secure
mode. Device can be secured only in 'kinetis fcf_source write' mode.
Use FOPT from programmed data if its value was not configured
in OpenOCD config by 'kinetis fopt' command.

Change-Id: If65fbbd7700069f57e4ae32234dce371bff93674
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4228
Tested-by: jenkins
Reviewed-by: Robert Foss <robert.foss@memcpy.io>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
This commit is contained in:
Tomas Vanek 2017-09-06 22:34:14 +02:00 committed by Freddie Chopin
parent e3a5f1613b
commit e3f3b77729
1 changed files with 47 additions and 8 deletions

View File

@ -287,6 +287,7 @@ struct kinetis_chip {
FS_NO_CMD_BLOCKSTAT = 0x40,
FS_WIDTH_256BIT = 0x80,
FS_ECC = 0x100,
} flash_support;
enum {
@ -388,6 +389,7 @@ static const struct kinetis_type kinetis_types_old[] = {
static bool allow_fcf_writes;
static uint8_t fcf_fopt = 0xff;
static bool fcf_fopt_configured;
static bool create_banks;
@ -1881,9 +1883,13 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
{
int result;
bool set_fcf = false;
bool fcf_in_data_valid = false;
int sect = 0;
struct kinetis_flash_bank *k_bank = bank->driver_priv;
struct kinetis_chip *k_chip = k_bank->k_chip;
uint8_t fcf_buffer[FCF_SIZE];
uint8_t fcf_current[FCF_SIZE];
uint8_t fcf_in_data[FCF_SIZE];
result = kinetis_check_run_mode(k_chip);
if (result != ERROR_OK)
@ -1904,11 +1910,41 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
}
if (set_fcf) {
uint8_t fcf_buffer[FCF_SIZE];
uint8_t fcf_current[FCF_SIZE];
kinetis_fill_fcf(bank, fcf_buffer);
fcf_in_data_valid = offset <= FCF_ADDRESS
&& offset + count >= FCF_ADDRESS + FCF_SIZE;
if (fcf_in_data_valid) {
memcpy(fcf_in_data, buffer + FCF_ADDRESS - offset, FCF_SIZE);
if (memcmp(fcf_in_data + FCF_FPROT, fcf_buffer, 4)) {
fcf_in_data_valid = false;
LOG_INFO("Flash protection requested in programmed file differs from current setting.");
}
if (fcf_in_data[FCF_FDPROT] != fcf_buffer[FCF_FDPROT]) {
fcf_in_data_valid = false;
LOG_INFO("Data flash protection requested in programmed file differs from current setting.");
}
if ((fcf_in_data[FCF_FSEC] & 3) != 2) {
fcf_in_data_valid = false;
LOG_INFO("Device security requested in programmed file!");
} else if (k_chip->flash_support & FS_ECC
&& fcf_in_data[FCF_FSEC] != fcf_buffer[FCF_FSEC]) {
fcf_in_data_valid = false;
LOG_INFO("Strange unsecure mode 0x%02" PRIx8
"requested in programmed file!",
fcf_in_data[FCF_FSEC]);
}
if ((k_chip->flash_support & FS_ECC || fcf_fopt_configured)
&& fcf_in_data[FCF_FOPT] != fcf_fopt) {
fcf_in_data_valid = false;
LOG_INFO("FOPT requested in programmed file differs from current setting.");
}
if (!fcf_in_data_valid)
LOG_INFO("Expect verify errors at FCF (0x408-0x40f).");
}
}
if (set_fcf && !fcf_in_data_valid) {
if (offset < FCF_ADDRESS) {
/* write part preceding FCF */
result = kinetis_write_inner(bank, buffer, offset, FCF_ADDRESS - offset);
@ -1937,9 +1973,10 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
}
return result;
} else
} else {
/* no FCF fiddling, normal write */
return kinetis_write_inner(bank, buffer, offset, count);
}
}
@ -2146,7 +2183,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
k_chip->nvm_sector_size = 4<<10;
k_chip->max_flash_prog_size = 1<<10;
num_blocks = 4;
k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR;
k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_ECC;
cpu_mhz = 180;
break;
@ -2300,7 +2337,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
k_chip->max_flash_prog_size = 1<<10;
num_blocks = 1;
maxaddr_shift = 14;
k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_WIDTH_256BIT;
k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_WIDTH_256BIT | FS_ECC;
k_chip->pflash_base = 0x10000000;
k_chip->progr_accel_ram = 0x18000000;
cpu_mhz = 240;
@ -2959,10 +2996,12 @@ COMMAND_HANDLER(kinetis_fopt_handler)
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
if (CMD_ARGC == 1)
if (CMD_ARGC == 1) {
fcf_fopt = (uint8_t)strtoul(CMD_ARGV[0], NULL, 0);
else
fcf_fopt_configured = true;
} else {
command_print(CMD_CTX, "FCF_FOPT 0x%02" PRIx8, fcf_fopt);
}
return ERROR_OK;
}