Skip to content

Commit 47c3795

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

File tree

2 files changed

+171
-2
lines changed

2 files changed

+171
-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: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626

2727
#include "qemu/osdep.h"
28+
#include <string.h>
2829
#include "qemu/error-report.h"
2930
#include "qemu/typedefs.h"
3031
#include "qapi/error.h"
@@ -197,7 +198,7 @@ enum OtEGBoardDevice {
197198

198199
#define OT_EG_IBEX_WRAPPER_NUM_REGIONS 2u
199200

200-
static const uint8_t ot_eg_pmp_cfgs[] = {
201+
static uint8_t ot_eg_pmp_cfgs[] = {
201202
/* clang-format off */
202203
IBEX_PMP_CFG(0, IBEX_PMP_MODE_OFF, 0, 0, 0),
203204
IBEX_PMP_CFG(0, IBEX_PMP_MODE_OFF, 0, 0, 0),
@@ -218,7 +219,7 @@ static const uint8_t ot_eg_pmp_cfgs[] = {
218219
/* clang-format on */
219220
};
220221

221-
static const uint32_t ot_eg_pmp_addrs[] = {
222+
static uint32_t ot_eg_pmp_addrs[] = {
222223
/* clang-format off */
223224
IBEX_PMP_ADDR(0x00000000),
224225
IBEX_PMP_ADDR(0x00000000),
@@ -1492,6 +1493,8 @@ struct OtEGMachineState {
14921493
bool no_epmp_cfg;
14931494
bool ignore_elf_entry;
14941495
bool verilator;
1496+
/* ePMP region specification string */
1497+
char *epmp_regions;
14951498
};
14961499

14971500
struct OtEGMachineClass {
@@ -1553,6 +1556,126 @@ static void ot_eg_soc_flash_ctrl_configure(
15531556
}
15541557
}
15551558

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

1692+
ot_eg_soc_configure_pmp(ms, &error_fatal);
1693+
15691694
pmp_cfg = qlist_new();
15701695
for (unsigned ix = 0; ix < ARRAY_SIZE(ot_eg_pmp_cfgs); ix++) {
15711696
qlist_append_int(pmp_cfg, ot_eg_pmp_cfgs[ix]);
@@ -2006,6 +2131,29 @@ static void ot_eg_machine_set_verilator(Object *obj, bool value, Error **errp)
20062131
s->verilator = value;
20072132
}
20082133

2134+
static char *ot_eg_machine_get_epmp_regions(Object *obj, Error **errp)
2135+
{
2136+
OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj);
2137+
(void)errp;
2138+
2139+
return s->epmp_regions;
2140+
}
2141+
2142+
static void
2143+
ot_eg_machine_set_epmp_regions(Object *obj, const char *value, Error **errp)
2144+
{
2145+
OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj);
2146+
(void)errp;
2147+
2148+
if (s->epmp_regions) {
2149+
free(s->epmp_regions);
2150+
}
2151+
2152+
size_t len = strlen(value) + 1;
2153+
s->epmp_regions = g_malloc(len);
2154+
strlcpy(s->epmp_regions, value, len);
2155+
}
2156+
20092157
static ResettableState *ot_eg_machine_get_reset_state(Object *obj)
20102158
{
20112159
OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj);
@@ -2047,6 +2195,11 @@ static void ot_eg_machine_instance_init(Object *obj)
20472195
object_property_add_bool(obj, "verilator", &ot_eg_machine_get_verilator,
20482196
&ot_eg_machine_set_verilator);
20492197
object_property_set_description(obj, "verilator", "Use Verilator clocks");
2198+
object_property_add_str(obj, "epmp-regions",
2199+
&ot_eg_machine_get_epmp_regions,
2200+
&ot_eg_machine_set_epmp_regions);
2201+
object_property_set_description(
2202+
obj, "epmp-regions", "Set default ePMP memory region configuration");
20502203
}
20512204

20522205
static void ot_eg_machine_init(MachineState *state)

0 commit comments

Comments
 (0)