Skip to content

Commit b559d3d

Browse files
committed
[ot] hw/opentitan: ot_flash: Refactor to implement multi-word reads
This commit refactors `ot_flash_op_read` so that it can properly support multi-word reads. Two fields: `remaining`/`error` are introduced to the operation to count the number of processed/remaining words in the current transaction, and to track errors in multi-word transactions. These fields are used to allow us to retain the initial operation configuration and calculate the current address at any time. Under previous operation, if the read FIFO filled, the re-execution of the `ot_flash_op_read` function read from the initial `op.address`, thus reading repeated (garbage) data. This new logic fixes the issue and allows multi-word reads, whilst remaining generic for future operations. Calculation of the address has moved inside of the iteration, because this functionality is needed for the later addition of memory protection region functionality, and because it makes it easier to implement correct errors for individual operations in multi-word transactions. It also better decouples the transaction's memory accesses from our flash backend structure; it is possible for multi-word operations to occur over some boundary (e.g. read over a page boundary). Signed-off-by: Alex Jones <[email protected]>
1 parent 85a85e5 commit b559d3d

File tree

2 files changed

+100
-63
lines changed

2 files changed

+100
-63
lines changed

hw/opentitan/ot_flash.c

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -742,11 +742,13 @@ struct OtFlashState {
742742
struct {
743743
OtFlashOperation kind;
744744
unsigned count;
745+
unsigned remaining;
745746
unsigned address;
746747
unsigned info_sel;
747748
bool info_part;
748749
bool prog_sel;
749750
bool erase_sel;
751+
bool failed;
750752
} op;
751753
OtFifo32 rd_fifo;
752754
OtFifo32 prog_fifo;
@@ -818,7 +820,6 @@ static void ot_flash_update_prog_watermark(OtFlashState *s)
818820
}
819821

820822

821-
822823
static void ot_flash_op_signal(void *opaque)
823824
{
824825
OtFlashState *s = opaque;
@@ -908,78 +909,105 @@ static void ot_flash_op_complete(OtFlashState *s)
908909
ot_flash_update_irqs(s);
909910
}
910911

912+
static unsigned ot_flash_next_info_address(OtFlashState *s)
913+
{
914+
OtFlashStorage *storage = &s->flash;
915+
unsigned bank_size = storage->data_size;
916+
unsigned info_partition = s->op.info_sel;
917+
918+
/* offset the address by the number of processed ops to get next addr */
919+
unsigned op_offset = (s->op.count - s->op.remaining) * sizeof(uint32_t);
920+
unsigned op_address = s->op.address + op_offset;
921+
922+
if (info_partition >= storage->info_part_count) {
923+
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid info partition: %u\n",
924+
__func__, s->op.info_sel);
925+
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, op_address);
926+
s->op.failed = true;
927+
return op_address;
928+
}
929+
930+
/* extract the bank & bank-relative address from the address */
931+
unsigned bank = op_address / bank_size;
932+
if (bank >= storage->bank_count) {
933+
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid bank: %d\n", __func__,
934+
bank);
935+
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, op_address);
936+
s->op.failed = true;
937+
return op_address;
938+
}
939+
unsigned address_in_bank = op_address % bank_size;
940+
if (address_in_bank >= storage->info_parts[info_partition].size) {
941+
/*
942+
* Purposefuly do not check the whole transaction's address width here:
943+
* the RTL only errors when it attempts to read an invalid address.
944+
*/
945+
qemu_log_mask(LOG_GUEST_ERROR,
946+
"%s: invalid address in partition: %u %u\n", __func__,
947+
op_address, info_partition);
948+
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, op_address);
949+
s->op.failed = true;
950+
return op_address;
951+
}
952+
953+
/* Retrieve the raw backend byte address in the info partitions. */
954+
unsigned bank_offset = bank * storage->info_size;
955+
unsigned info_part_offset = storage->info_parts[info_partition].offset;
956+
unsigned address = address_in_bank + bank_offset + info_part_offset;
957+
trace_ot_flash_info_part(s->op.address, s->op.count, s->op.remaining, bank,
958+
info_partition, address);
959+
return address;
960+
}
961+
962+
static unsigned ot_flash_next_data_address(OtFlashState *s)
963+
{
964+
OtFlashStorage *storage = &s->flash;
965+
unsigned bank_size = storage->data_size;
966+
967+
/* offset the address by the number of proessed ops to get next addr */
968+
unsigned op_offset = (s->op.count - s->op.remaining) * sizeof(uint32_t);
969+
unsigned address = s->op.address + op_offset;
970+
unsigned bank = address / bank_size;
971+
if (bank >= storage->bank_count) {
972+
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid bank: %d\n", __func__,
973+
bank);
974+
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, address);
975+
s->op.failed = true;
976+
return address;
977+
}
978+
trace_ot_flash_data_part(s->op.address, s->op.count, s->op.remaining, bank,
979+
address);
980+
return address;
981+
}
982+
911983
static void ot_flash_op_read(OtFlashState *s)
912984
{
913985
if (ot_fifo32_is_full(&s->rd_fifo)) {
914986
xtrace_ot_flash_error("read while RD FIFO full");
915987
return;
916988
}
917-
unsigned max_size;
918-
unsigned offset;
919-
unsigned address;
989+
920990
OtFlashStorage *storage = &s->flash;
921-
uint32_t *src;
922-
923-
if (s->op.info_part) {
924-
if (s->op.info_sel >= storage->info_part_count) {
925-
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid info partition: %u\n",
926-
__func__, s->op.info_sel);
927-
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, s->op.address);
928-
ot_flash_op_complete(s);
929-
return;
930-
}
931-
max_size = storage->info_size;
932-
/* relative storage offset in the info storage */
933-
offset = storage->info_parts[s->op.info_sel].offset;
934-
unsigned bank_size = storage->data_size;
935-
/* extract the bank from the address */
936-
unsigned bank = s->op.address / bank_size;
937-
if (bank >= storage->bank_count) {
938-
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid bank: %d\n", __func__,
939-
bank);
940-
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, s->op.address);
941-
ot_flash_op_complete(s);
942-
return;
943-
}
944-
/* get the adress relative to the bank */
945-
address = s->op.address % bank_size;
946-
if (address + s->op.count * sizeof(uint32_t) >=
947-
storage->info_parts[s->op.info_sel].size) {
948-
qemu_log_mask(LOG_GUEST_ERROR,
949-
"%s: invalid address in partition: %u %u\n", __func__,
950-
address, s->op.info_sel);
951-
ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, s->op.address);
952-
ot_flash_op_complete(s);
953-
return;
991+
uint32_t *src = s->op.info_part ? storage->info : storage->data;
992+
993+
while (s->op.remaining) {
994+
uint32_t word = 0xFFFFFFFFu;
995+
if (!s->op.failed) {
996+
unsigned address = s->op.info_part ? ot_flash_next_info_address(s) :
997+
ot_flash_next_data_address(s);
998+
address /= sizeof(uint32_t); /* convert to word address */
999+
1000+
/* For multi-word read access permission errors, return all 1s. */
1001+
if (!s->op.failed) {
1002+
word = src[address];
1003+
}
9541004
}
955-
/* add the bank offset of the first byte of info part */
956-
address += bank * storage->info_size;
957-
/* add the offset of the partition in the current bank */
958-
address += offset;
959-
src = storage->info;
960-
trace_ot_flash_info_part(s->op.address, bank, s->op.info_sel, address);
961-
} else {
962-
max_size = storage->data_size;
963-
address = s->op.address;
964-
src = storage->data;
965-
}
966-
967-
/* sanity check */
968-
if (address >= max_size * storage->bank_count) {
969-
xtrace_ot_flash_error("read address out of bound");
970-
g_assert_not_reached();
971-
}
972-
973-
/* convert to word address */
974-
address /= sizeof(uint32_t);
9751005

976-
while (s->op.count) {
977-
uint32_t word = src[address++];
978-
s->op.count--;
9791006
if (!ot_flash_fifo_in_reset(s)) {
9801007
ot_fifo32_push(&s->rd_fifo, word);
9811008
s->regs[R_STATUS] &= ~R_STATUS_RD_EMPTY_MASK;
9821009
ot_flash_update_rd_watermark(s);
1010+
s->op.remaining--;
9831011
}
9841012
if (ot_fifo32_is_full(&s->rd_fifo)) {
9851013
s->regs[R_STATUS] |= R_STATUS_RD_FULL_MASK;
@@ -989,7 +1017,11 @@ static void ot_flash_op_read(OtFlashState *s)
9891017
}
9901018
}
9911019

992-
if (!s->op.count) {
1020+
/*
1021+
* If we finished the entire read operation (i.e. no early exit as FIFO
1022+
* is full), mark the operation as completed.
1023+
*/
1024+
if (!s->op.remaining) {
9931025
ot_flash_op_complete(s);
9941026
}
9951027
}
@@ -998,7 +1030,7 @@ static void ot_flash_op_execute(OtFlashState *s)
9981030
{
9991031
switch (s->op.kind) {
10001032
case OP_READ:
1001-
trace_ot_flash_op_start(OP_NAME(s->op.kind));
1033+
trace_ot_flash_op_execute(OP_NAME(s->op.kind));
10021034
ot_flash_op_read(s);
10031035
break;
10041036
default:
@@ -1342,6 +1374,9 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64,
13421374
ot_flash_op_complete(s);
13431375
return;
13441376
}
1377+
s->op.failed = false;
1378+
s->op.remaining = s->op.count;
1379+
trace_ot_flash_op_start(OP_NAME(s->op.kind));
13451380
}
13461381
ot_flash_op_execute(s);
13471382
break;

hw/opentitan/trace-events

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,14 @@ ot_entropy_src_update_generation(unsigned gennum) "%u"
177177

178178
ot_flash_error(const char *func, int line, const char *err) "%s:%d %s"
179179
ot_flash_info(const char *func, int line, const char *msg, uint32_t value) "%s:%d %s: 0x%08x"
180-
ot_flash_info_part(uint32_t op_addr, unsigned bank, unsigned infosel, uint32_t addr) "op_addr 0x%06x bank %u infosel %u addr 0x%06x"
180+
ot_flash_info_part(unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned infosel, unsigned addr) "op_addr 0x%06x count %u remaining %u bank %u infosel %u addr 0x%06x"
181+
ot_flash_data_part(unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned addr) "op_addr 0x%06x count %u remaining %u bank %u addr 0x%06x"
181182
ot_flash_io_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x"
182183
ot_flash_io_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x"
183184
ot_flash_irqs(uint32_t active, uint32_t mask, uint32_t eff) "act:0x%08x msk:0x%08x eff:0x%08x"
184185
ot_flash_mem_read_out(uint32_t addr, unsigned size, uint32_t val, uint32_t pc) "addr=0x%02x (%u), val=0x%08x, pc=0x%x"
185186
ot_flash_op_complete(const char *op, bool success) "%s: %u"
187+
ot_flash_op_execute(const char *op) "%s"
186188
ot_flash_op_start(const char *op) "%s"
187189
ot_flash_set_error(const char *op, uint32_t err_code, uint32_t err_addr) "%s: err=%08x at addr=0x%06x"
188190
ot_flash_update_rd_watermark(unsigned val, unsigned watermark) "%u >= %u"

0 commit comments

Comments
 (0)