Skip to content

Commit c6d95dd

Browse files
committed
[ot] hw/opentitan: ot_flash: Add program (write) functionality
Adds functionality that allows single/multi-word program (write) operations. This is similar to reading, but must also propagate changes back to our flash backend via the `blk_pwrite` API. The existing address calculation functionality from the flash read is re-used, as the flash controller transaction arbitration logic remains common across these operations. Upon encountering an error in a multi-word program transaction, the flash controller will continue to empty the program FIFO, but not perform any further writes. Writes before the error are valid. Signed-off-by: Alex Jones <[email protected]>
1 parent b559d3d commit c6d95dd

File tree

2 files changed

+106
-10
lines changed

2 files changed

+106
-10
lines changed

hw/opentitan/ot_flash.c

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,20 @@ static void ot_flash_update_alerts(OtFlashState *s)
775775
}
776776
}
777777

778+
static bool ot_flash_is_backend_writable(const OtFlashState *s)
779+
{
780+
return (s->blk != NULL) && blk_is_writable(s->blk);
781+
}
782+
783+
static bool ot_flash_write_backend(OtFlashState *s, const void *buffer,
784+
unsigned offset, size_t size)
785+
{
786+
/* NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) */
787+
return blk_pwrite(s->blk, (int64_t)(intptr_t)offset, (int64_t)size, buffer,
788+
(BdrvRequestFlags)0);
789+
/* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */
790+
}
791+
778792
static bool ot_flash_is_disabled(OtFlashState *s)
779793
{
780794
return s->regs[R_DIS] != OT_MULTIBITBOOL4_FALSE;
@@ -1026,13 +1040,99 @@ static void ot_flash_op_read(OtFlashState *s)
10261040
}
10271041
}
10281042

1043+
static void ot_flash_op_prog(OtFlashState *s)
1044+
{
1045+
if (ot_fifo32_is_empty(&s->prog_fifo)) {
1046+
xtrace_ot_flash_error("prog while prog FIFO empty");
1047+
return;
1048+
}
1049+
1050+
OtFlashStorage *storage = &s->flash;
1051+
uint32_t *dest = s->op.info_part ? storage->info : storage->data;
1052+
1053+
while (s->op.remaining) {
1054+
if (ot_flash_fifo_in_reset(s)) {
1055+
continue;
1056+
}
1057+
uint32_t word = ot_fifo32_pop(&s->prog_fifo);
1058+
s->regs[R_STATUS] &= ~R_STATUS_PROG_FULL_MASK;
1059+
ot_flash_update_prog_watermark(s);
1060+
bool fifo_empty = ot_fifo32_is_empty(&s->prog_fifo);
1061+
if (fifo_empty) {
1062+
s->regs[R_STATUS] |= R_STATUS_PROG_EMPTY_MASK;
1063+
s->regs[R_INTR_STATE] |= INTR_PROG_EMPTY_MASK;
1064+
ot_flash_update_irqs(s);
1065+
}
1066+
1067+
/* Must calculate next addr before decrementing the remaining count. */
1068+
unsigned address = 0u;
1069+
if (!s->op.failed) {
1070+
address = s->op.info_part ? ot_flash_next_info_address(s) :
1071+
ot_flash_next_data_address(s);
1072+
address /= sizeof(uint32_t); /* convert to word address */
1073+
}
1074+
s->op.remaining--;
1075+
1076+
/*
1077+
* On encountering a multi-word write error, we must continue to empty
1078+
* the prog FIFO regardless, hence we retrieve the word to program
1079+
* before checking for errors.
1080+
*/
1081+
trace_ot_flash_op_prog(s->op.address, s->op.count, s->op.remaining,
1082+
s->op.failed, fifo_empty);
1083+
if (s->op.failed) {
1084+
if (!fifo_empty) {
1085+
continue;
1086+
}
1087+
break;
1088+
}
1089+
1090+
/*
1091+
* Bits cannot be programmed back to 1 once programmed to 0; they must
1092+
* be erased instead.
1093+
*/
1094+
g_assert(address <
1095+
((s->op.info_part ? storage->info_size : storage->data_size) *
1096+
storage->bank_count));
1097+
dest[address] &= word;
1098+
trace_ot_flash_prog_word(s->op.info_part, address, word);
1099+
if (ot_flash_is_backend_writable(s)) {
1100+
uintptr_t dest_offset = (uintptr_t)dest - (uintptr_t)storage->data;
1101+
if (ot_flash_write_backend(s, &dest[address],
1102+
(unsigned)(dest_offset + address),
1103+
sizeof(uint32_t))) {
1104+
qemu_log_mask(LOG_GUEST_ERROR,
1105+
"%s: cannot update flash backend\n", __func__);
1106+
ot_flash_set_error(s, R_ERR_CODE_PROG_ERR_MASK,
1107+
address * sizeof(uint32_t));
1108+
}
1109+
}
1110+
1111+
if (fifo_empty) {
1112+
break;
1113+
}
1114+
}
1115+
1116+
/*
1117+
* If we finished the entire program operation (i.e. no early exit as FIFO
1118+
* is empty), mark the operation as completed.
1119+
*/
1120+
if (!s->op.remaining) {
1121+
ot_flash_op_complete(s);
1122+
}
1123+
}
1124+
10291125
static void ot_flash_op_execute(OtFlashState *s)
10301126
{
10311127
switch (s->op.kind) {
10321128
case OP_READ:
10331129
trace_ot_flash_op_execute(OP_NAME(s->op.kind));
10341130
ot_flash_op_read(s);
10351131
break;
1132+
case OP_PROG:
1133+
trace_ot_flash_op_execute(OP_NAME(s->op.kind));
1134+
ot_flash_op_prog(s);
1135+
break;
10361136
default:
10371137
xtrace_ot_flash_error("unsupported");
10381138
break;
@@ -1897,12 +1997,6 @@ static const char *ot_flash_hexdump(const uint8_t *buf, size_t size)
18971997

18981998
static void ot_flash_load(OtFlashState *s, Error **errp)
18991999
{
1900-
/*
1901-
* Notes:
1902-
* 1. only support read access to the flash backend
1903-
* 2. only data partition for now
1904-
*/
1905-
19062000
OtFlashStorage *flash = &s->flash;
19072001
memset(flash, 0, sizeof(OtFlashStorage));
19082002

@@ -1925,7 +2019,7 @@ static void ot_flash_load(OtFlashState *s, Error **errp)
19252019
blk_blockalign(s->blk, sizeof(OtFlashBackendHeader));
19262020

19272021
int rc;
1928-
// NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
2022+
/* NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) */
19292023
rc = blk_pread(s->blk, 0, sizeof(*header), header, 0);
19302024
if (rc < 0) {
19312025
error_setg(errp, "failed to read the flash header content: %d", rc);
@@ -1979,7 +2073,7 @@ static void ot_flash_load(OtFlashState *s, Error **errp)
19792073
unsigned offset = offsetof(OtFlashBackendHeader, hlength) +
19802074
sizeof(header->hlength) + header->hlength;
19812075

1982-
// NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
2076+
/* NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) */
19832077
rc = blk_pread(s->blk, (int64_t)offset, flash_size, flash->storage, 0);
19842078
if (rc < 0) {
19852079
error_setg(errp, "failed to read the initial flash content: %d",
@@ -1994,10 +2088,10 @@ static void ot_flash_load(OtFlashState *s, Error **errp)
19942088
size_t debug_trailer_size =
19952089
(size_t)(flash->bank_count) * ELFNAME_SIZE * BIN_APP_COUNT;
19962090
uint8_t *elfnames = blk_blockalign(s->blk, debug_trailer_size);
1997-
// NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange)
2091+
/* NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) */
19982092
rc = blk_pread(s->blk, (int64_t)offset + flash_size,
19992093
(int64_t)debug_trailer_size, elfnames, 0);
2000-
// NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange)
2094+
/* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */
20012095
if (!rc) {
20022096
const char *elfname = (const char *)elfnames;
20032097
for (unsigned ix = 0; ix < BIN_APP_COUNT; ix++) {

hw/opentitan/trace-events

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ ot_flash_set_error(const char *op, uint32_t err_code, uint32_t err_addr) "%s: er
190190
ot_flash_update_rd_watermark(unsigned val, unsigned watermark) "%u >= %u"
191191
ot_flash_update_prog_watermark(unsigned val, unsigned watermark) "%u <= %u"
192192
ot_flash_reset_fifo(const char *name) "%s"
193+
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"
194+
ot_flash_prog_word(bool info_part, unsigned word_addr, uint32_t word) "info_part %u word_addr %u word %u"
193195

194196
# ot_gpio.c
195197

0 commit comments

Comments
 (0)