Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for NMIs to ot_earlgrey #111

Draft
wants to merge 11 commits into
base: ot-earlgrey-9.1.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions hw/intc/sifive_plic.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,12 +361,29 @@ static void parse_hart_config(SiFivePLICState *plic)

static void sifive_plic_irq_request(void *opaque, int irq, int level)
{
SiFivePLICState *s = opaque;
SiFivePLICState *plic = opaque;

assert(irq < s->num_sources);
assert(irq < plic->num_sources);

sifive_plic_set_pending(s, irq, level > 0);
sifive_plic_update(s);
/*
* In OpenTitan (or more generally when we have a system with support for
* NMIs), we only want to clear a pending IRQ at the PLIC when it has
* been claimed. Normally this is not a problem - the IRQ will cause
* software to acknowledge the pending IRQ at the peripheral, which will
* propagate to the PLIC, which has already been serviced. But with NMIs,
* we have the case where (a) a peripheral generates an NMI and IRQ, (b)
* the NMI handler disables the IRQ at the peripheral, as part of
* acknowleding the NMI, (c) this would cause the PLIC to de-assert the
* IRQ, but since we haven't claimed it yet we still want it to call our
* ISR handler as normal after the NMI handler exits. So, we should only
* de-assert a pending IRQ when it is claimed.
*/
bool is_claimed = (plic->claimed[irq >> 5] & (1u << (irq & 31)));
bool is_pending = (plic->pending[irq >> 5] & (1u << (irq & 31)));
if (level || (!is_pending || is_claimed)) {
sifive_plic_set_pending(plic, irq, level > 0);
}
sifive_plic_update(plic);
}

static void sifive_plic_realize(DeviceState *dev, Error **errp)
Expand Down
8 changes: 4 additions & 4 deletions hw/misc/pulp_rv_dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ struct PulpRVDMState {
MemoryRegion rom; /* ROM */

qemu_irq *ack_out;
IbexIRQ alert;
qemu_irq alert;

uint32_t dmflag_regs[PULP_RV_DM_DMFLAG_SIZE / sizeof(uint32_t)];

Expand Down Expand Up @@ -259,7 +259,7 @@ static void pulp_rv_dm_regs_write(void *opaque, hwaddr addr, uint64_t val64,
switch (R32_OFF(addr)) {
case R_ALERT_TEST:
val32 &= R_ALERT_TEST_FATAL_FAULT_MASK;
ibex_irq_set(&s->alert, (int)(bool)val32);
qemu_set_irq(s->alert, (int)(bool)val32);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
Expand Down Expand Up @@ -477,7 +477,7 @@ static void pulp_rv_dm_reset(DeviceState *dev)
{
PulpRVDMState *s = PULP_RV_DM(dev);

ibex_irq_set(&s->alert, false);
qemu_set_irq(s->alert, false);

memset(memory_region_get_ram_ptr(&s->prog), 0, PULP_RV_DM_PROG_SIZE);
memset(s->dmflag_regs, 0, sizeof(s->dmflag_regs));
Expand Down Expand Up @@ -524,7 +524,7 @@ static void pulp_rv_dm_init(Object *obj)

pulp_rv_dm_load_rom(s);

ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT);
qdev_init_gpio_out_named(DEVICE(obj), &s->alert, OT_DEVICE_ALERT, 1);
}

static void pulp_rv_dm_class_init(ObjectClass *klass, void *data)
Expand Down
9 changes: 5 additions & 4 deletions hw/opentitan/ot_aes.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ enum OtAESMode {
struct OtAESState {
SysBusDevice parent_obj;
MemoryRegion mmio;
IbexIRQ alerts[PARAM_NUM_ALERTS];
qemu_irq alerts[PARAM_NUM_ALERTS];
IbexIRQ clkmgr;
QEMUBH *process_bh;
QEMUTimer *retard_timer; /* only used with disabled fast-mode */
Expand Down Expand Up @@ -328,7 +328,7 @@ static void ot_aes_update_alert(OtAESState *s)
{
for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) {
bool level = (bool)(s->regs->status & (1u << ix));
ibex_irq_set(&s->alerts[ix], (int)level);
qemu_set_irq(s->alerts[ix], (int)level);
}
}

Expand Down Expand Up @@ -1269,7 +1269,7 @@ static void ot_aes_reset(DeviceState *dev)
ot_aes_load_reseed_rate(s);

for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) {
ibex_irq_set(&s->alerts[ix], 0);
qemu_set_irq(s->alerts[ix], 0);
}

trace_ot_aes_reseed("reset");
Expand All @@ -1294,7 +1294,8 @@ static void ot_aes_init(Object *obj)
}

for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) {
ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT);
qdev_init_gpio_out_named(DEVICE(obj), &s->alerts[ix], OT_DEVICE_ALERT,
1);
}

ibex_qdev_init_irq(obj, &s->clkmgr, OT_CLOCK_ACTIVE);
Expand Down
7 changes: 7 additions & 0 deletions hw/opentitan/ot_alert.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ struct OtAlertState {
MemoryRegion mmio;
IbexIRQ *irqs;
IbexIRQ *esc_txs;
IbexIRQ nmi_alert;
OtAlertScheduler *schedulers;

OtAlertRegs regs; /* not ordered by register index */
Expand Down Expand Up @@ -571,6 +572,7 @@ static void ot_alert_clear_alert(OtAlertState *s, unsigned nclass)
trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), ix, "release");
}
ibex_irq_set(esc_tx, 0);
ibex_irq_set(&s->nmi_alert, 0u);
}
/*
* "Software can clear CLASSn_ACCUM_CNT with a write to CLASSA_CLR_SHADOWED"
Expand Down Expand Up @@ -741,6 +743,10 @@ static void ot_alert_signal_tx(void *opaque, int n, int level)

trace_ot_alert_signal_tx(s->ot_id, alert, (bool)level, alert_en);

if (alert_en && level) {
ibex_irq_set(&s->nmi_alert, 1u);
}

if (!alert_en || !level) {
/* releasing the alert does not clear it */
return;
Expand Down Expand Up @@ -1034,6 +1040,7 @@ static void ot_alert_realize(DeviceState *dev, Error **errp)
s->esc_txs = g_new0(IbexIRQ, PARAM_N_ESC_SEV);
ibex_qdev_init_irqs(OBJECT(dev), s->esc_txs, OT_ALERT_ESCALATE,
PARAM_N_ESC_SEV);
ibex_qdev_init_irq(OBJECT(dev), &s->nmi_alert, OT_ALERT_NMI);

qdev_init_gpio_in_named(dev, &ot_alert_signal_tx, OT_DEVICE_ALERT,
s->n_alerts);
Expand Down
94 changes: 57 additions & 37 deletions hw/opentitan/ot_aon_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,23 @@ REG32(ALERT_TEST, 0x00u)
REG32(WKUP_CTRL, 0x04u)
FIELD(WKUP_CTRL, ENABLE, 0u, 1u)
FIELD(WKUP_CTRL, PRESCALER, 1u, 12u)
REG32(WKUP_THOLD, 0x08)
REG32(WKUP_COUNT, 0x0cu)
REG32(WDOG_REGWEN, 0x10u)
REG32(WKUP_THOLD_HI, 0x08u)
REG32(WKUP_THOLD_LO, 0x0cu)
REG32(WKUP_COUNT_HI, 0x10u)
REG32(WKUP_COUNT_LO, 0x14u)
REG32(WDOG_REGWEN, 0x18u)
FIELD(WDOG_REGWEN, REGWEN, 0u, 1u)
REG32(WDOG_CTRL, 0x14u)
REG32(WDOG_CTRL, 0x1cu)
FIELD(WDOG_CTRL, ENABLE, 0u, 1u)
FIELD(WDOG_CTRL, PAUSE_IN_SLEEP, 0u, 1u)
REG32(WDOG_BARK_THOLD, 0x18u)
REG32(WDOG_BITE_THOLD, 0x1cu)
REG32(WDOG_COUNT, 0x20u)
REG32(INTR_STATE, 0x24u)
FIELD(WDOG_CTRL, PAUSE_IN_SLEEP, 1u, 1u)
REG32(WDOG_BARK_THOLD, 0x20u)
REG32(WDOG_BITE_THOLD, 0x24u)
REG32(WDOG_COUNT, 0x28u)
REG32(INTR_STATE, 0x2cu)
SHARED_FIELD(INTR_WKUP_TIMER_EXPIRED, 0u, 1u)
SHARED_FIELD(INTR_WDOG_TIMER_BARK, 1u, 1u)
REG32(INTR_TEST, 0x28u)
REG32(WKUP_CAUSE, 0x2cu)
REG32(INTR_TEST, 0x30u)
REG32(WKUP_CAUSE, 0x34u)
FIELD(WKUP_CAUSE, CAUSE, 0u, 1u)
/* clang-format on */

Expand All @@ -80,8 +82,10 @@ static const char REG_NAMES[REGS_COUNT][20u] = {
/* clang-format off */
REG_NAME_ENTRY(ALERT_TEST),
REG_NAME_ENTRY(WKUP_CTRL),
REG_NAME_ENTRY(WKUP_THOLD),
REG_NAME_ENTRY(WKUP_COUNT),
REG_NAME_ENTRY(WKUP_THOLD_HI),
REG_NAME_ENTRY(WKUP_THOLD_LO),
REG_NAME_ENTRY(WKUP_COUNT_HI),
REG_NAME_ENTRY(WKUP_COUNT_LO),
REG_NAME_ENTRY(WDOG_REGWEN),
REG_NAME_ENTRY(WDOG_CTRL),
REG_NAME_ENTRY(WDOG_BARK_THOLD),
Expand All @@ -104,7 +108,7 @@ struct OtAonTimerState {
IbexIRQ nmi_bark;
IbexIRQ pwrmgr_wkup;
IbexIRQ pwrmgr_bite;
IbexIRQ alert;
qemu_irq alert;

QEMUTimer *wkup_timer;
QEMUTimer *wdog_timer;
Expand All @@ -119,36 +123,40 @@ struct OtAonTimerState {
uint32_t pclk;
};

static uint32_t
static uint64_t
ot_aon_timer_ns_to_ticks(OtAonTimerState *s, uint32_t prescaler, int64_t ns)
{
uint64_t ticks = muldiv64((uint64_t)ns, s->pclk, NANOSECONDS_PER_SECOND);
return (uint32_t)(ticks / (prescaler + 1u));
return ticks / (prescaler + 1u);
}

static int64_t
ot_aon_timer_ticks_to_ns(OtAonTimerState *s, uint32_t prescaler, uint32_t ticks)
ot_aon_timer_ticks_to_ns(OtAonTimerState *s, uint32_t prescaler, uint64_t ticks)
{
uint64_t ns = muldiv64((uint64_t)ticks * (prescaler + 1u),
NANOSECONDS_PER_SECOND, s->pclk);
uint64_t ns =
muldiv64(ticks * (prescaler + 1u), NANOSECONDS_PER_SECOND, s->pclk);
if (ns > INT64_MAX) {
return INT64_MAX;
}
return (int64_t)ns;
}

static uint32_t ot_aon_timer_get_wkup_count(OtAonTimerState *s, uint64_t now)
static uint64_t ot_aon_timer_get_wkup_count(OtAonTimerState *s, uint64_t now)
{
uint32_t prescaler = FIELD_EX32(s->regs[R_WKUP_CTRL], WKUP_CTRL, PRESCALER);
return s->regs[R_WKUP_COUNT] +
uint64_t wkup_count =
((uint64_t)s->regs[R_WKUP_COUNT_HI] << 32u) | s->regs[R_WKUP_COUNT_LO];
return wkup_count +
ot_aon_timer_ns_to_ticks(s, prescaler,
(int64_t)(now - s->wkup_origin_ns));
}

static uint32_t ot_aon_timer_get_wdog_count(OtAonTimerState *s, uint64_t now)
{
return s->regs[R_WDOG_COUNT] +
ot_aon_timer_ns_to_ticks(s, 0u, (int64_t)(now - s->wdog_origin_ns));
(uint32_t)
ot_aon_timer_ns_to_ticks(s, 0u,
(int64_t)(now - s->wdog_origin_ns));
}

static int64_t ot_aon_timer_compute_next_timeout(OtAonTimerState *s,
Expand Down Expand Up @@ -185,19 +193,18 @@ static inline bool ot_aon_timer_wdog_register_write_enabled(OtAonTimerState *s)
static void ot_aon_timer_update_alert(OtAonTimerState *s)
{
bool level = s->regs[R_ALERT_TEST] & R_ALERT_TEST_FATAL_FAULT_MASK;
ibex_irq_set(&s->alert, level);
qemu_set_irq(s->alert, level);
}

static void ot_aon_timer_update_irqs(OtAonTimerState *s)
{
bool wkup = (bool)(s->regs[R_INTR_STATE] & INTR_WKUP_TIMER_EXPIRED_MASK);
bool bark = (bool)(s->regs[R_INTR_STATE] & INTR_WDOG_TIMER_BARK_MASK);

trace_ot_aon_timer_irqs(s->ot_id, wkup, bark, s->wdog_bite);

ibex_irq_set(&s->irq_wkup, wkup);
ibex_irq_set(&s->irq_bark, bark);
ibex_irq_set(&s->nmi_bark, bark);
ibex_irq_set(&s->irq_bark, bark);
ibex_irq_set(&s->pwrmgr_wkup, wkup);
ibex_irq_set(&s->pwrmgr_bite, s->wdog_bite);
}
Expand All @@ -218,8 +225,9 @@ static void ot_aon_timer_rearm_wkup(OtAonTimerState *s, bool reset_origin)
return;
}

uint32_t count = ot_aon_timer_get_wkup_count(s, now);
uint32_t threshold = s->regs[R_WKUP_THOLD];
uint64_t count = ot_aon_timer_get_wkup_count(s, now);
uint64_t threshold =
((uint64_t)s->regs[R_WKUP_THOLD_HI] << 32u) | s->regs[R_WKUP_THOLD_LO];

if (count >= threshold) {
s->regs[R_INTR_STATE] |= INTR_WKUP_TIMER_EXPIRED_MASK;
Expand Down Expand Up @@ -309,7 +317,8 @@ static uint64_t ot_aon_timer_read(void *opaque, hwaddr addr, unsigned size)
hwaddr reg = R32_OFF(addr);
switch (reg) {
case R_WKUP_CTRL:
case R_WKUP_THOLD:
case R_WKUP_THOLD_HI:
case R_WKUP_THOLD_LO:
case R_WDOG_REGWEN:
case R_WDOG_CTRL:
case R_WDOG_BARK_THOLD:
Expand All @@ -318,11 +327,18 @@ static uint64_t ot_aon_timer_read(void *opaque, hwaddr addr, unsigned size)
case R_WKUP_CAUSE:
val32 = s->regs[reg];
break;
case R_WKUP_COUNT: {
case R_WKUP_COUNT_HI: {
uint64_t now = ot_aon_timer_is_wkup_enabled(s) ?
qemu_clock_get_ns(OT_VIRTUAL_CLOCK) :
s->wkup_origin_ns;
val32 = (uint32_t)(ot_aon_timer_get_wkup_count(s, now) >> 32u);
break;
}
case R_WKUP_COUNT_LO: {
uint64_t now = ot_aon_timer_is_wkup_enabled(s) ?
qemu_clock_get_ns(OT_VIRTUAL_CLOCK) :
s->wkup_origin_ns;
val32 = ot_aon_timer_get_wkup_count(s, now);
val32 = (uint32_t)ot_aon_timer_get_wkup_count(s, now);
break;
}
case R_WDOG_COUNT: {
Expand Down Expand Up @@ -384,19 +400,23 @@ static void ot_aon_timer_write(void *opaque, hwaddr addr, uint64_t value,
/* stop timer */
timer_del(s->wkup_timer);
/* save current count */
uint32_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK);
s->regs[R_WKUP_COUNT] = ot_aon_timer_get_wkup_count(s, now);
int64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK);
uint64_t count = ot_aon_timer_get_wkup_count(s, now);
s->regs[R_WKUP_COUNT_HI] = (uint32_t)(count >> 32u);
s->regs[R_WKUP_COUNT_LO] = (uint32_t)count;
s->wkup_origin_ns = now;
}
}
break;
}
case R_WKUP_THOLD:
s->regs[R_WKUP_THOLD] = val32;
case R_WKUP_THOLD_HI:
case R_WKUP_THOLD_LO:
s->regs[reg] = val32;
ot_aon_timer_rearm_wkup(s, false);
break;
case R_WKUP_COUNT:
s->regs[R_WKUP_COUNT] = val32;
case R_WKUP_COUNT_HI:
case R_WKUP_COUNT_LO:
s->regs[reg] = val32;
ot_aon_timer_rearm_wkup(s, true);
break;
case R_WDOG_REGWEN:
Expand Down Expand Up @@ -525,7 +545,7 @@ static void ot_aon_timer_init(Object *obj)
ibex_qdev_init_irq(obj, &s->nmi_bark, OT_AON_TIMER_BARK);
ibex_qdev_init_irq(obj, &s->pwrmgr_wkup, OT_AON_TIMER_WKUP);
ibex_qdev_init_irq(obj, &s->pwrmgr_bite, OT_AON_TIMER_BITE);
ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT);
qdev_init_gpio_out_named(DEVICE(obj), &s->alert, OT_DEVICE_ALERT, 1);

memory_region_init_io(&s->mmio, obj, &ot_aon_timer_ops, s,
TYPE_OT_AON_TIMER, REGS_SIZE);
Expand Down
11 changes: 6 additions & 5 deletions hw/opentitan/ot_clkmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ struct OtClkMgrState {
SysBusDevice parent_obj;
MemoryRegion mmio;
IbexIRQ hints[OT_CLKMGR_HINT_COUNT];
IbexIRQ alerts[PARAM_NUM_ALERTS];
qemu_irq alerts[PARAM_NUM_ALERTS];

uint32_t clock_states; /* bit set: active, reset: clock is idle */
uint32_t regs[REGS_COUNT]; /* shadowed slots are not used */
Expand All @@ -210,7 +210,7 @@ static void ot_clkmgr_update_alerts(OtClkMgrState *s)
{
bool recov = (bool)(s->regs[R_RECOV_ERR_CODE] &
R_RECOV_ERR_CODE_SHADOW_UPDATE_ERR_MASK);
ibex_irq_set(&s->alerts[ALERT_RECOVERABLE], recov);
qemu_set_irq(s->alerts[ALERT_RECOVERABLE], recov);
}

static void ot_clkmgr_clock_hint(void *opaque, int irq, int level)
Expand Down Expand Up @@ -320,7 +320,7 @@ static void ot_clkmgr_write(void *opaque, hwaddr addr, uint64_t val64,
case R_ALERT_TEST:
val32 &= ALERT_TEST_MASK;
for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) {
ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u));
qemu_set_irq(s->alerts[ix], (int)((val32 >> ix) & 0x1u));
}
break;
case R_EXTCLK_CTRL_REGWEN:
Expand Down Expand Up @@ -565,7 +565,7 @@ static void ot_clkmgr_reset(DeviceState *dev)
ot_shadow_reg_init(&s->sdw_regs.usb_meas_ctrl, 0x1ccfau);

for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) {
ibex_irq_set(&s->alerts[ix], 0);
qemu_set_irq(s->alerts[ix], 0);
}
}

Expand All @@ -578,7 +578,8 @@ static void ot_clkmgr_init(Object *obj)
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio);

for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) {
ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT);
qdev_init_gpio_out_named(DEVICE(obj), &s->alerts[ix], OT_DEVICE_ALERT,
1);
}

qdev_init_gpio_in_named(DEVICE(obj), &ot_clkmgr_clock_hint, OT_CLKMGR_HINT,
Expand Down
Loading
Loading