flash/nor: implement protection blocks of different size than erase sector

Originally flash/nor infrastructure assumed protection blocks identical
to erase sectors. This assumption is not valid for many flash types.
Driver code fixed the problem either by increasing sector size to
size of protection block or by defining more protection block than
really existed in device. Both cases had drawbacks.

The change retains compatibility with the old driver.
Updated driver can set protection blocks table independent
of sector table.

Change-Id: I27f6d267528ad9ed9fe0a85f05436a8ec17603a4
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/3545
Tested-by: jenkins
Reviewed-by: Steven Stallion <stallion@squareup.com>
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
__archive__
Tomas Vanek 2016-07-14 20:33:27 +02:00 committed by Andreas Fritiofson
parent 77478eb0f5
commit 77a1c01ccb
4 changed files with 110 additions and 23 deletions

View File

@ -4746,8 +4746,10 @@ and display that status.
The @var{num} parameter is a value shown by @command{flash banks}. The @var{num} parameter is a value shown by @command{flash banks}.
@end deffn @end deffn
@deffn Command {flash info} num @deffn Command {flash info} num [sectors]
Print info about flash bank @var{num} Print info about flash bank @var{num}, a list of protection blocks
and their status. Use @option{sectors} to show a list of sectors instead.
The @var{num} parameter is a value shown by @command{flash banks}. The @var{num} parameter is a value shown by @command{flash banks}.
This command will first query the hardware, it does not print cached This command will first query the hardware, it does not print cached
and possibly stale information. and possibly stale information.

View File

@ -343,8 +343,9 @@ int default_flash_blank_check(struct flash_bank *bank)
* and address. Maps an address range to a set of sectors, and issues * and address. Maps an address range to a set of sectors, and issues
* the callback() on that set ... e.g. to erase or unprotect its members. * the callback() on that set ... e.g. to erase or unprotect its members.
* *
* (Note a current bad assumption: that protection operates on the same * Parameter iterate_protect_blocks switches iteration of protect block
* size sectors as erase operations use.) * instead of erase sectors. If there is no protect blocks array, sectors
* are used in iteration, so compatibility for old flash drivers is retained.
* *
* The "pad_reason" parameter is a kind of boolean: when it's NULL, the * The "pad_reason" parameter is a kind of boolean: when it's NULL, the
* range must fit those sectors exactly. This is clearly safe; it can't * range must fit those sectors exactly. This is clearly safe; it can't
@ -355,13 +356,16 @@ int default_flash_blank_check(struct flash_bank *bank)
*/ */
static int flash_iterate_address_range_inner(struct target *target, static int flash_iterate_address_range_inner(struct target *target,
char *pad_reason, uint32_t addr, uint32_t length, char *pad_reason, uint32_t addr, uint32_t length,
bool iterate_protect_blocks,
int (*callback)(struct flash_bank *bank, int first, int last)) int (*callback)(struct flash_bank *bank, int first, int last))
{ {
struct flash_bank *c; struct flash_bank *c;
struct flash_sector *block_array;
uint32_t last_addr = addr + length; /* first address AFTER end */ uint32_t last_addr = addr + length; /* first address AFTER end */
int first = -1; int first = -1;
int last = -1; int last = -1;
int i; int i;
int num_blocks;
int retval = get_flash_bank_by_addr(target, addr, true, &c); int retval = get_flash_bank_by_addr(target, addr, true, &c);
if (retval != ERROR_OK) if (retval != ERROR_OK)
@ -388,13 +392,21 @@ static int flash_iterate_address_range_inner(struct target *target,
return ERROR_FLASH_DST_BREAKS_ALIGNMENT; return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
} }
/** @todo: handle erasures that cross into adjacent banks */
addr -= c->base; addr -= c->base;
last_addr -= c->base; last_addr -= c->base;
for (i = 0; i < c->num_sectors; i++) { if (iterate_protect_blocks && c->prot_blocks && c->num_prot_blocks) {
struct flash_sector *f = c->sectors + i; block_array = c->prot_blocks;
num_blocks = c->num_prot_blocks;
} else {
block_array = c->sectors;
num_blocks = c->num_sectors;
iterate_protect_blocks = false;
}
for (i = 0; i < num_blocks; i++) {
struct flash_sector *f = &block_array[i];
uint32_t end = f->offset + f->size; uint32_t end = f->offset + f->size;
/* start only on a sector boundary */ /* start only on a sector boundary */
@ -472,6 +484,7 @@ static int flash_iterate_address_range_inner(struct target *target,
*/ */
static int flash_iterate_address_range(struct target *target, static int flash_iterate_address_range(struct target *target,
char *pad_reason, uint32_t addr, uint32_t length, char *pad_reason, uint32_t addr, uint32_t length,
bool iterate_protect_blocks,
int (*callback)(struct flash_bank *bank, int first, int last)) int (*callback)(struct flash_bank *bank, int first, int last))
{ {
struct flash_bank *c; struct flash_bank *c;
@ -491,6 +504,7 @@ static int flash_iterate_address_range(struct target *target,
} }
retval = flash_iterate_address_range_inner(target, retval = flash_iterate_address_range_inner(target,
pad_reason, addr, cur_length, pad_reason, addr, cur_length,
iterate_protect_blocks,
callback); callback);
if (retval != ERROR_OK) if (retval != ERROR_OK)
break; break;
@ -506,7 +520,7 @@ int flash_erase_address_range(struct target *target,
bool pad, uint32_t addr, uint32_t length) bool pad, uint32_t addr, uint32_t length)
{ {
return flash_iterate_address_range(target, pad ? "erase" : NULL, return flash_iterate_address_range(target, pad ? "erase" : NULL,
addr, length, &flash_driver_erase); addr, length, false, &flash_driver_erase);
} }
static int flash_driver_unprotect(struct flash_bank *bank, int first, int last) static int flash_driver_unprotect(struct flash_bank *bank, int first, int last)
@ -521,7 +535,7 @@ int flash_unlock_address_range(struct target *target, uint32_t addr, uint32_t le
* and doesn't restore it. * and doesn't restore it.
*/ */
return flash_iterate_address_range(target, "unprotect", return flash_iterate_address_range(target, "unprotect",
addr, length, &flash_driver_unprotect); addr, length, true, &flash_driver_unprotect);
} }
static int compare_section(const void *a, const void *b) static int compare_section(const void *a, const void *b)
@ -762,3 +776,22 @@ int flash_write(struct target *target, struct image *image,
{ {
return flash_write_unlock(target, image, written, erase, false); return flash_write_unlock(target, image, written, erase, false);
} }
struct flash_sector *alloc_block_array(uint32_t offset, uint32_t size, int num_blocks)
{
int i;
struct flash_sector *array = calloc(num_blocks, sizeof(struct flash_sector));
if (array == NULL)
return NULL;
for (i = 0; i < num_blocks; i++) {
array[i].offset = offset;
array[i].size = size;
array[i].is_erased = -1;
array[i].is_protected = -1;
offset += size;
}
return array;
}

View File

@ -46,6 +46,8 @@ struct flash_sector {
/** /**
* Indication of erasure status: 0 = not erased, 1 = erased, * Indication of erasure status: 0 = not erased, 1 = erased,
* other = unknown. Set by @c flash_driver_s::erase_check. * other = unknown. Set by @c flash_driver_s::erase_check.
*
* Flag is not used in protection block
*/ */
int is_erased; int is_erased;
/** /**
@ -56,6 +58,9 @@ struct flash_sector {
* This information must be considered stale immediately. * This information must be considered stale immediately.
* A million things could make it stale: power cycle, * A million things could make it stale: power cycle,
* reset of target, code running on target, etc. * reset of target, code running on target, etc.
*
* If a flash_bank uses an extra array of protection blocks,
* protection flag is not valid in sector array
*/ */
int is_protected; int is_protected;
}; };
@ -95,9 +100,19 @@ struct flash_bank {
* some non-zero value during "probe()" or "auto_probe()". * some non-zero value during "probe()" or "auto_probe()".
*/ */
int num_sectors; int num_sectors;
/** Array of sectors, allocated and initilized by the flash driver */ /** Array of sectors, allocated and initialized by the flash driver */
struct flash_sector *sectors; struct flash_sector *sectors;
/**
* The number of protection blocks in this bank. This value
* is set intially to 0 and sectors are used as protection blocks.
* Driver probe can set protection blocks array to work with
* protection granularity different than sector size.
*/
int num_prot_blocks;
/** Array of protection blocks, allocated and initilized by the flash driver */
struct flash_sector *prot_blocks;
struct flash_bank *next; /**< The next flash bank on this chip */ struct flash_bank *next; /**< The next flash bank on this chip */
}; };
@ -205,5 +220,13 @@ struct flash_bank *get_flash_bank_by_num_noprobe(int num);
*/ */
int get_flash_bank_by_addr(struct target *target, uint32_t addr, bool check, int get_flash_bank_by_addr(struct target *target, uint32_t addr, bool check,
struct flash_bank **result_bank); struct flash_bank **result_bank);
/**
* Allocate and fill an array of sectors or protection blocks.
* @param offset Offset of first block.
* @param size Size of each block.
* @param num_blocks Number of blocks in array.
* @returns A struct flash_sector pointer or NULL when allocation failed.
*/
struct flash_sector *alloc_block_array(uint32_t offset, uint32_t size, int num_blocks);
#endif /* OPENOCD_FLASH_NOR_CORE_H */ #endif /* OPENOCD_FLASH_NOR_CORE_H */

View File

@ -70,16 +70,27 @@ COMMAND_HANDLER(handle_flash_info_command)
struct flash_bank *p; struct flash_bank *p;
int j = 0; int j = 0;
int retval; int retval;
bool show_sectors = false;
bool prot_block_available;
if (CMD_ARGC != 1) if (CMD_ARGC < 1 || CMD_ARGC > 2)
return ERROR_COMMAND_SYNTAX_ERROR; return ERROR_COMMAND_SYNTAX_ERROR;
if (CMD_ARGC == 2) {
if (strcmp("sectors", CMD_ARGV[1]) == 0)
show_sectors = true;
else
return ERROR_COMMAND_SYNTAX_ERROR;
}
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p); retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
if (p != NULL) { if (p != NULL) {
char buf[1024]; char buf[1024];
int num_blocks;
struct flash_sector *block_array;
/* attempt auto probe */ /* attempt auto probe */
retval = p->driver->auto_probe(p); retval = p->driver->auto_probe(p);
@ -100,22 +111,32 @@ COMMAND_HANDLER(handle_flash_info_command)
p->size, p->size,
p->bus_width, p->bus_width,
p->chip_width); p->chip_width);
for (j = 0; j < p->num_sectors; j++) {
char *protect_state;
if (p->sectors[j].is_protected == 0) prot_block_available = p->num_prot_blocks && p->prot_blocks;
if (!show_sectors && prot_block_available) {
block_array = p->prot_blocks;
num_blocks = p->num_prot_blocks;
} else {
block_array = p->sectors;
num_blocks = p->num_sectors;
}
for (j = 0; j < num_blocks; j++) {
char *protect_state = "";
if (block_array[j].is_protected == 0)
protect_state = "not protected"; protect_state = "not protected";
else if (p->sectors[j].is_protected == 1) else if (block_array[j].is_protected == 1)
protect_state = "protected"; protect_state = "protected";
else else if (!show_sectors || !prot_block_available)
protect_state = "protection state unknown"; protect_state = "protection state unknown";
command_print(CMD_CTX, command_print(CMD_CTX,
"\t#%3i: 0x%8.8" PRIx32 " (0x%" PRIx32 " %" PRIi32 "kB) %s", "\t#%3i: 0x%8.8" PRIx32 " (0x%" PRIx32 " %" PRIi32 "kB) %s",
j, j,
p->sectors[j].offset, block_array[j].offset,
p->sectors[j].size, block_array[j].size,
p->sectors[j].size >> 10, block_array[j].size >> 10,
protect_state); protect_state);
} }
@ -333,21 +354,27 @@ COMMAND_HANDLER(handle_flash_protect_command)
struct flash_bank *p; struct flash_bank *p;
int retval; int retval;
int num_blocks;
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p); retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
if (p->num_prot_blocks)
num_blocks = p->num_prot_blocks;
else
num_blocks = p->num_sectors;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], first); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], first);
if (strcmp(CMD_ARGV[2], "last") == 0) if (strcmp(CMD_ARGV[2], "last") == 0)
last = p->num_sectors - 1; last = num_blocks - 1;
else else
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last); COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
bool set; bool set;
COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set); COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set);
retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors); retval = flash_check_sector_parameters(CMD_CTX, first, last, num_blocks);
if (retval != ERROR_OK) if (retval != ERROR_OK)
return retval; return retval;
@ -813,7 +840,7 @@ static const struct command_registration flash_exec_command_handlers[] = {
.name = "info", .name = "info",
.handler = handle_flash_info_command, .handler = handle_flash_info_command,
.mode = COMMAND_EXEC, .mode = COMMAND_EXEC,
.usage = "bank_id", .usage = "bank_id ['sectors']",
.help = "Print information about a flash bank.", .help = "Print information about a flash bank.",
}, },
{ {
@ -988,6 +1015,8 @@ COMMAND_HANDLER(handle_flash_bank_command)
c->default_padded_value = 0xff; c->default_padded_value = 0xff;
c->num_sectors = 0; c->num_sectors = 0;
c->sectors = NULL; c->sectors = NULL;
c->num_prot_blocks = 0;
c->prot_blocks = NULL;
c->next = NULL; c->next = NULL;
int retval; int retval;