Skip to content

Commit bb869e5

Browse files
committed
[ot] hw/riscv: ot_earlgrey: allow regions to be configured
Signed-off-by: James Wainwright <[email protected]>
1 parent a0f33d3 commit bb869e5

File tree

2 files changed

+152
-2
lines changed

2 files changed

+152
-2
lines changed

docs/opentitan/earlgrey.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,22 @@ See [`tools.md`](tools.md)
146146
`-M ot-earlgrey,no_epmp_cfg=true` to disable the initial ePMP configuration, which can be very
147147
useful to execute arbitrary code on the Ibex core without requiring an OT ROM image to boot up.
148148

149+
* `epmp_regions=<specification>` can be appended to the machine switch, _i.e._
150+
`-M ot-earlgrey,epmp_regions=1:00000000:OFF:FL#2:0000bffc:NAPOT:FLRX` to configure the default
151+
ePMP regions at startup. The specification string describes one or more regions separated with
152+
a '#' character. Each region specification has the following syntax:
153+
`<region index>:<address>:<mode>:<flags>` where:
154+
155+
- `<region index>` is the zero-based index of the region to configure.
156+
- `<address>` is the hexadecimal address field for the region.
157+
- `<mode>` is one of the following ePMP region modes: `OFF`, `TOR`, `NA4`, or `NAPOT`.
158+
- `<flags>` is a set of the following uppercase characters denoting region flags:
159+
160+
- `L`: locked
161+
- `R`: readable
162+
- `W`: writable
163+
- `X`: executable
164+
149165
* `ignore_elf_entry=true` can be appended to the machine option switch, _i.e._
150166
`-M ot-earlgrey,ignore_elf_entry=true` to prevent the ELF entry point of a loaded application to
151167
update the vCPU reset vector at startup. When this option is used, with `-kernel` option for

hw/riscv/ot_earlgrey.c

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ enum OtEGBoardDevice {
197197

198198
#define OT_EG_IBEX_WRAPPER_NUM_REGIONS 2u
199199

200-
static const uint8_t ot_eg_pmp_cfgs[] = {
200+
static uint8_t ot_eg_pmp_cfgs[] = {
201201
/* clang-format off */
202202
IBEX_PMP_CFG(0, IBEX_PMP_MODE_OFF, 0, 0, 0),
203203
IBEX_PMP_CFG(0, IBEX_PMP_MODE_OFF, 0, 0, 0),
@@ -218,7 +218,7 @@ static const uint8_t ot_eg_pmp_cfgs[] = {
218218
/* clang-format on */
219219
};
220220

221-
static const uint32_t ot_eg_pmp_addrs[] = {
221+
static uint32_t ot_eg_pmp_addrs[] = {
222222
/* clang-format off */
223223
IBEX_PMP_ADDR(0x00000000),
224224
IBEX_PMP_ADDR(0x00000000),
@@ -1492,6 +1492,8 @@ struct OtEGMachineState {
14921492
bool no_epmp_cfg;
14931493
bool ignore_elf_entry;
14941494
bool verilator;
1495+
/* ePMP region specification string */
1496+
char *epmp_regions;
14951497
};
14961498

14971499
struct OtEGMachineClass {
@@ -1553,6 +1555,110 @@ static void ot_eg_soc_flash_ctrl_configure(
15531555
}
15541556
}
15551557

1558+
/*
1559+
* Parse and apply the PMP configuration specification provided as a property.
1560+
*
1561+
* The specification string contains one or more region configurations separated
1562+
* by `#` characters. Each configuration has the following syntax:
1563+
*
1564+
* <region index>:<address>:<mode>:<flags>`
1565+
*
1566+
* - `<region index>` is the zero-based index of the region to configure.
1567+
* - `<address>` is the hexadecimal address field for the region.
1568+
* - `<mode>` is one of the following ePMP region modes: `OFF`, `TOR`, `NA4`, or `NAPOT`.
1569+
* - `<flags>` is a set of the following uppercase characters denoting region flags.
1570+
*
1571+
* The supported flags are:
1572+
*
1573+
* - `L`: locked
1574+
* - `R`: readable
1575+
* - `W`: writable
1576+
* - `X`: executable
1577+
*/
1578+
static void ot_eg_soc_configure_pmp(OtEGMachineState *ms, Error **errp) {
1579+
const char *config = ms->epmp_regions;
1580+
1581+
/* escape early if config is empty */
1582+
if (config == NULL || *config == '\0') {
1583+
return;
1584+
}
1585+
1586+
while (true) {
1587+
unsigned pmp_idx;
1588+
target_ulong addr;
1589+
char mode_str[6] = {0};
1590+
char flags[5] = {0};
1591+
unsigned len;
1592+
1593+
/* extract one region configuration from the string */
1594+
int parsed = sscanf(config, "%d:%8x:%5[^:]:%4[LRWX]%n", &pmp_idx, &addr, mode_str, flags, &len);
1595+
1596+
/* only accept when all parts of the configuration were present */
1597+
if (parsed != 4) {
1598+
error_setg(errp, "bad epmp format: expected 4 parts, got %d", parsed);
1599+
abort();
1600+
}
1601+
1602+
/* parse the mode */
1603+
unsigned mode;
1604+
if (strncmp(mode_str, "OFF", 3) == 0) {
1605+
mode = IBEX_PMP_MODE_OFF;
1606+
} else if(strncmp(mode_str, "TOR", 3) == 0) {
1607+
mode = IBEX_PMP_MODE_TOR;
1608+
} else if(strncmp(mode_str, "NA4", 3) == 0) {
1609+
mode = IBEX_PMP_MODE_NA4;
1610+
} else if(strncmp(mode_str, "NAPOT", 5) == 0) {
1611+
mode = IBEX_PMP_MODE_NAPOT;
1612+
} else {
1613+
error_setg(errp, "bad mode %s, expected `OFF`, `TOR`, `NA4`, or `NAPOT`", mode_str);
1614+
abort();
1615+
}
1616+
1617+
/* parse the flags */
1618+
bool l = false, r = false, w = false, x = false;
1619+
for (unsigned i = 0; flags[i]; i++) {
1620+
switch (flags[i]) {
1621+
case 'L':
1622+
l = true;
1623+
break;
1624+
case 'R':
1625+
r = true;
1626+
break;
1627+
case 'W':
1628+
w = true;
1629+
break;
1630+
case 'X':
1631+
x = true;
1632+
break;
1633+
default:
1634+
error_setg(errp, "bad flag %c, expected `L`, `R`, `W`, or `X`", flags[i]);
1635+
abort();
1636+
}
1637+
}
1638+
1639+
/* prepare the `PMPCFG` and `PMPADDR` codes */
1640+
uint8_t pmpcfg = IBEX_PMP_CFG(l, mode, x, w, r);
1641+
target_ulong pmpaddr = IBEX_PMP_ADDR(addr);
1642+
1643+
(void)pmp_idx;
1644+
(void)pmpcfg;
1645+
(void)pmpaddr;
1646+
ot_eg_pmp_cfgs[pmp_idx] = pmpcfg;
1647+
ot_eg_pmp_addrs[pmp_idx] = pmpaddr;
1648+
1649+
/* determine whether there are more configurations to parse */
1650+
if (config[len] == '#') {
1651+
config = config + len + 1;
1652+
continue;
1653+
} else if (config[len] == '\0') {
1654+
break;
1655+
} else {
1656+
error_setg(errp, "bad region format, expected `,` or end of string, got %c", config[len]);
1657+
abort();
1658+
}
1659+
}
1660+
}
1661+
15561662
static void ot_eg_soc_hart_configure(DeviceState *dev, const IbexDeviceDef *def,
15571663
DeviceState *parent)
15581664
{
@@ -1566,6 +1672,8 @@ static void ot_eg_soc_hart_configure(DeviceState *dev, const IbexDeviceDef *def,
15661672
return;
15671673
}
15681674

1675+
ot_eg_soc_configure_pmp(ms, &error_fatal);
1676+
15691677
pmp_cfg = qlist_new();
15701678
for (unsigned ix = 0; ix < ARRAY_SIZE(ot_eg_pmp_cfgs); ix++) {
15711679
qlist_append_int(pmp_cfg, ot_eg_pmp_cfgs[ix]);
@@ -2006,6 +2114,28 @@ static void ot_eg_machine_set_verilator(Object *obj, bool value, Error **errp)
20062114
s->verilator = value;
20072115
}
20082116

2117+
static char *ot_eg_machine_get_epmp_regions(Object *obj, Error **errp)
2118+
{
2119+
OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj);
2120+
(void)errp;
2121+
2122+
return s->epmp_regions;
2123+
}
2124+
2125+
static void ot_eg_machine_set_epmp_regions(Object *obj, const char *value, Error **errp)
2126+
{
2127+
OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj);
2128+
(void)errp;
2129+
2130+
if (s->epmp_regions) {
2131+
free(s->epmp_regions);
2132+
}
2133+
2134+
size_t len = strlen(value) + 1;
2135+
s->epmp_regions = g_malloc(len);
2136+
strcpy(s->epmp_regions, value);
2137+
}
2138+
20092139
static ResettableState *ot_eg_machine_get_reset_state(Object *obj)
20102140
{
20112141
OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj);
@@ -2047,6 +2177,10 @@ static void ot_eg_machine_instance_init(Object *obj)
20472177
object_property_add_bool(obj, "verilator", &ot_eg_machine_get_verilator,
20482178
&ot_eg_machine_set_verilator);
20492179
object_property_set_description(obj, "verilator", "Use Verilator clocks");
2180+
object_property_add_str(obj, "epmp-regions", &ot_eg_machine_get_epmp_regions,
2181+
&ot_eg_machine_set_epmp_regions);
2182+
object_property_set_description(obj, "epmp-regions",
2183+
"Set default ePMP memory region configuration");
20502184
}
20512185

20522186
static void ot_eg_machine_init(MachineState *state)

0 commit comments

Comments
 (0)