Skip to content

Commit e7964f6

Browse files
committed
[ot] hw/opentitan: ot_flash: Add page/bank erase functionality
Adds flash erase operation functionality, which works at either page or bank granularities. Erase ops hence do not have lengths/counts, and the area to be erased is determine based on the address and partition selections. Successful erases are then propagated to the flash backend. Page erases use similar functionality to read/programs to calculate the page address and erase the page. Bank erases are more involved: they bypass regular memory protection mechanisms. This commit implements these bank protection mechanisms, as well as the logic for erasing just the data partition of the bank, or both data and info partitions. Signed-off-by: Alex Jones <[email protected]>
1 parent c6d95dd commit e7964f6

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

hw/opentitan/ot_flash.c

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -789,12 +789,12 @@ static bool ot_flash_write_backend(OtFlashState *s, const void *buffer,
789789
/* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */
790790
}
791791

792-
static bool ot_flash_is_disabled(OtFlashState *s)
792+
static bool ot_flash_is_disabled(const OtFlashState *s)
793793
{
794794
return s->regs[R_DIS] != OT_MULTIBITBOOL4_FALSE;
795795
}
796796

797-
static bool ot_flash_regs_is_wr_enabled(OtFlashState *s, unsigned regwen)
797+
static bool ot_flash_regs_is_wr_enabled(const OtFlashState *s, unsigned regwen)
798798
{
799799
return (bool)(s->regs[regwen] & REGWEN_EN_MASK);
800800
}
@@ -873,7 +873,7 @@ static void ot_flash_initialize(OtFlashState *s)
873873
qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + OP_INIT_DURATION_NS);
874874
}
875875

876-
static bool ot_flash_fifo_in_reset(OtFlashState *s)
876+
static bool ot_flash_fifo_in_reset(const OtFlashState *s)
877877
{
878878
return (bool)s->regs[R_FIFO_RST];
879879
}
@@ -923,6 +923,23 @@ static void ot_flash_op_complete(OtFlashState *s)
923923
ot_flash_update_irqs(s);
924924
}
925925

926+
static bool ot_flash_can_erase_bank(const OtFlashState *s, unsigned bank)
927+
{
928+
switch (bank) {
929+
case 0u:
930+
return (bool)FIELD_EX32(s->regs[R_MP_BANK_CFG_SHADOWED],
931+
MP_BANK_CFG_SHADOWED, ERASE_EN_0);
932+
case 1u:
933+
return (bool)FIELD_EX32(s->regs[R_MP_BANK_CFG_SHADOWED],
934+
MP_BANK_CFG_SHADOWED, ERASE_EN_1);
935+
default:
936+
qemu_log_mask(LOG_GUEST_ERROR,
937+
"%s: unknown bank %u for bank erase operation", __func__,
938+
bank);
939+
return false;
940+
}
941+
}
942+
926943
static unsigned ot_flash_next_info_address(OtFlashState *s)
927944
{
928945
OtFlashStorage *storage = &s->flash;
@@ -1122,6 +1139,127 @@ static void ot_flash_op_prog(OtFlashState *s)
11221139
}
11231140
}
11241141

1142+
static void ot_flash_op_erase_page(OtFlashState *s, unsigned address)
1143+
{
1144+
OtFlashStorage *storage = &s->flash;
1145+
uint32_t *dest = s->op.info_part ? storage->info : storage->data;
1146+
unsigned page_size = BYTES_PER_PAGE;
1147+
unsigned page_address = address - (address % page_size);
1148+
page_address /= sizeof(uint32_t); /* convert to word address */
1149+
1150+
g_assert((page_address + page_size) <
1151+
((s->op.info_part ? storage->info_size : storage->data_size) *
1152+
storage->bank_count));
1153+
memset(&dest[page_address], 0xFFu, page_size);
1154+
trace_ot_flash_erase(s->op.info_part, page_address, page_size);
1155+
if (ot_flash_is_backend_writable(s)) {
1156+
uintptr_t dest_offset = (uintptr_t)dest - (uintptr_t)storage->data;
1157+
if (ot_flash_write_backend(s, &dest[page_address],
1158+
(unsigned)(dest_offset + page_address),
1159+
page_size)) {
1160+
qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot update flash backend\n",
1161+
__func__);
1162+
ot_flash_set_error(s, R_ERR_CODE_PROG_ERR_MASK,
1163+
address * sizeof(uint32_t));
1164+
ot_flash_op_complete(s);
1165+
return;
1166+
}
1167+
}
1168+
1169+
ot_flash_op_complete(s);
1170+
}
1171+
1172+
static void ot_flash_op_erase_bank(OtFlashState *s, unsigned address)
1173+
{
1174+
OtFlashStorage *storage = &s->flash;
1175+
unsigned bank_size =
1176+
s->op.info_part ? storage->info_size : storage->data_size;
1177+
unsigned bank = address / bank_size;
1178+
1179+
if (!ot_flash_can_erase_bank(s, bank)) {
1180+
qemu_log_mask(
1181+
LOG_GUEST_ERROR,
1182+
"%s: cannot erase bank %u when bank-wide erase not enabled\n",
1183+
__func__, bank);
1184+
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, address);
1185+
ot_flash_op_complete(s);
1186+
return;
1187+
}
1188+
1189+
/*
1190+
* For bank erase only, if the data partition is selected, just the
1191+
* data partition is erased. If the info partition is selected, BOTH
1192+
* the data and info partitions are erased.
1193+
*/
1194+
unsigned bank_address = address - (address % bank_size);
1195+
bank_address /= sizeof(uint32_t); /* convert to word address */
1196+
unsigned data_address, info_address = 0u;
1197+
if (!s->op.info_part) {
1198+
data_address = bank_address;
1199+
g_assert((data_address + bank_size) <=
1200+
(storage->data_size * storage->bank_count));
1201+
memset(&storage->data[data_address], 0xFFu, bank_size);
1202+
trace_ot_flash_erase(s->op.info_part, data_address, bank_size);
1203+
} else {
1204+
info_address = bank_address;
1205+
g_assert((info_address + bank_size) <=
1206+
(storage->info_size * storage->bank_count));
1207+
memset(&storage->info[info_address], 0xFFu, bank_size);
1208+
trace_ot_flash_erase(true, info_address, bank_size);
1209+
1210+
bank_size = storage->data_size;
1211+
data_address = bank_size * bank / sizeof(uint32_t);
1212+
g_assert((data_address + bank_size) <=
1213+
(storage->data_size * storage->bank_count));
1214+
memset(&storage->data[data_address], 0xFFu, bank_size);
1215+
trace_ot_flash_erase(false, data_address, bank_size);
1216+
}
1217+
1218+
if (ot_flash_is_backend_writable(s)) {
1219+
int data_write_err =
1220+
ot_flash_write_backend(s, &storage->data[data_address],
1221+
(unsigned)(data_address),
1222+
storage->data_size);
1223+
int info_write_err = 0u;
1224+
if (s->op.info_part) {
1225+
uintptr_t offset =
1226+
(uintptr_t)storage->info - (uintptr_t)storage->data;
1227+
info_write_err =
1228+
ot_flash_write_backend(s, &storage->info[info_address],
1229+
(unsigned)(offset + info_address),
1230+
storage->info_size);
1231+
}
1232+
1233+
if (data_write_err || info_write_err) {
1234+
qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot update flash backend\n",
1235+
__func__);
1236+
ot_flash_set_error(s, R_ERR_CODE_PROG_ERR_MASK,
1237+
address * sizeof(uint32_t));
1238+
ot_flash_op_complete(s);
1239+
return;
1240+
}
1241+
}
1242+
1243+
ot_flash_op_complete(s);
1244+
}
1245+
1246+
static void ot_flash_op_erase(OtFlashState *s)
1247+
{
1248+
unsigned address = s->op.info_part ? ot_flash_next_info_address(s) :
1249+
ot_flash_next_data_address(s);
1250+
if (s->op.failed) {
1251+
ot_flash_op_complete(s);
1252+
return;
1253+
}
1254+
1255+
/* Try to erase all bits in the page/bank back to 1. */
1256+
if (s->op.erase_sel == ERASE_SEL_PAGE) {
1257+
ot_flash_op_erase_page(s, address);
1258+
} else { /* ERASE_SEL_BANK */
1259+
ot_flash_op_erase_bank(s, address);
1260+
}
1261+
}
1262+
11251263
static void ot_flash_op_execute(OtFlashState *s)
11261264
{
11271265
switch (s->op.kind) {
@@ -1133,6 +1271,10 @@ static void ot_flash_op_execute(OtFlashState *s)
11331271
trace_ot_flash_op_execute(OP_NAME(s->op.kind));
11341272
ot_flash_op_prog(s);
11351273
break;
1274+
case OP_ERASE:
1275+
trace_ot_flash_op_execute(OP_NAME(s->op.kind));
1276+
ot_flash_op_erase(s);
1277+
break;
11361278
default:
11371279
xtrace_ot_flash_error("unsupported");
11381280
break;

hw/opentitan/trace-events

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ ot_flash_update_prog_watermark(unsigned val, unsigned watermark) "%u <= %u"
192192
ot_flash_reset_fifo(const char *name) "%s"
193193
ot_flash_op_prog(unsigned op_addr, unsigned count, unsigned remaining, bool failed, bool fifo_empty) "op_addr 0x%06x count %u remaining %u failed %u fifo_empty %u"
194194
ot_flash_prog_word(bool info_part, unsigned word_addr, uint32_t word) "info_part %u word_addr %u word %u"
195+
ot_flash_erase(bool info_part, unsigned word_addr, unsigned num_bytes) "info_part %u word_addr %u num_bytes %u"
195196

196197
# ot_gpio.c
197198

0 commit comments

Comments
 (0)