mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-10 09:44:13 +00:00
hv: Store exception info regs on entry, and use it
This is the second part of allowing nested exceptions Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
parent
607464f8b0
commit
25bc815821
4 changed files with 75 additions and 72 deletions
8
src/hv.c
8
src/hv.c
|
@ -301,11 +301,11 @@ void hv_rearm(void)
|
|||
msr(CNTP_CTL_EL0, CNTx_CTL_ENABLE);
|
||||
}
|
||||
|
||||
void hv_check_rendezvous(u64 *regs)
|
||||
void hv_check_rendezvous(struct exc_info *ctx)
|
||||
{
|
||||
if (hv_want_cpu == smp_id()) {
|
||||
hv_want_cpu = -1;
|
||||
hv_exc_proxy(regs, START_HV, HV_USER_INTERRUPT, NULL);
|
||||
hv_exc_proxy(ctx, START_HV, HV_USER_INTERRUPT, NULL);
|
||||
} else if (hv_want_cpu != -1) {
|
||||
// Unlock the HV so the target CPU can get into the proxy
|
||||
spin_unlock(&bhl);
|
||||
|
@ -317,7 +317,7 @@ void hv_check_rendezvous(u64 *regs)
|
|||
}
|
||||
}
|
||||
|
||||
void hv_tick(u64 *regs)
|
||||
void hv_tick(struct exc_info *ctx)
|
||||
{
|
||||
if (hv_should_exit) {
|
||||
spin_unlock(&bhl);
|
||||
|
@ -326,7 +326,7 @@ void hv_tick(u64 *regs)
|
|||
hv_wdt_pet();
|
||||
iodev_handle_events(uartproxy_iodev);
|
||||
if (iodev_can_read(uartproxy_iodev)) {
|
||||
hv_exc_proxy(regs, START_HV, HV_USER_INTERRUPT, NULL);
|
||||
hv_exc_proxy(ctx, START_HV, HV_USER_INTERRUPT, NULL);
|
||||
}
|
||||
hv_vuart_poll();
|
||||
}
|
||||
|
|
9
src/hv.h
9
src/hv.h
|
@ -3,6 +3,7 @@
|
|||
#ifndef HV_H
|
||||
#define HV_H
|
||||
|
||||
#include "exception.h"
|
||||
#include "iodev.h"
|
||||
#include "types.h"
|
||||
#include "uartproxy.h"
|
||||
|
@ -51,7 +52,7 @@ int hv_map_sw(u64 from, u64 to, u64 size);
|
|||
int hv_map_hook(u64 from, hv_hook_t *hook, u64 size);
|
||||
u64 hv_translate(u64 addr, bool s1only, bool w);
|
||||
u64 hv_pt_walk(u64 addr);
|
||||
bool hv_handle_dabort(u64 *regs);
|
||||
bool hv_handle_dabort(struct exc_info *ctx);
|
||||
bool hv_pa_write(u64 addr, u64 *val, int width);
|
||||
bool hv_pa_read(u64 addr, u64 *val, int width);
|
||||
bool hv_pa_rw(u64 addr, u64 *val, bool write, int width);
|
||||
|
@ -64,7 +65,7 @@ void hv_vuart_poll(void);
|
|||
void hv_map_vuart(u64 base, int irq, iodev_id_t iodev);
|
||||
|
||||
/* Exceptions */
|
||||
void hv_exc_proxy(u64 *regs, uartproxy_boot_reason_t reason, uartproxy_exc_code_t type,
|
||||
void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, uartproxy_exc_code_t type,
|
||||
void *extra);
|
||||
|
||||
/* WDT */
|
||||
|
@ -94,7 +95,7 @@ void hv_rendezvous(void);
|
|||
void hv_switch_cpu(int cpu);
|
||||
void hv_arm_tick(void);
|
||||
void hv_rearm(void);
|
||||
void hv_check_rendezvous(u64 *regs);
|
||||
void hv_tick(u64 *regs);
|
||||
void hv_check_rendezvous(struct exc_info *ctx);
|
||||
void hv_tick(struct exc_info *ctx);
|
||||
|
||||
#endif
|
||||
|
|
110
src/hv_exc.c
110
src/hv_exc.c
|
@ -36,9 +36,10 @@ static u64 exc_entry_time;
|
|||
|
||||
extern u32 hv_cpus_in_guest;
|
||||
|
||||
void hv_exc_proxy(u64 *regs, uartproxy_boot_reason_t reason, uartproxy_exc_code_t type, void *extra)
|
||||
void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, uartproxy_exc_code_t type,
|
||||
void *extra)
|
||||
{
|
||||
int from_el = FIELD_GET(SPSR_M, hv_get_spsr()) >> 2;
|
||||
int from_el = FIELD_GET(SPSR_M, ctx->spsr) >> 2;
|
||||
|
||||
hv_wdt_breadcrumb('P');
|
||||
|
||||
|
@ -49,26 +50,15 @@ void hv_exc_proxy(u64 *regs, uartproxy_boot_reason_t reason, uartproxy_exc_code_
|
|||
u64 entry_time = mrs(CNTPCT_EL0);
|
||||
#endif
|
||||
|
||||
struct exc_info exc_info = {
|
||||
.cpu_id = smp_id(),
|
||||
.spsr = hv_get_spsr(),
|
||||
.elr = hv_get_elr(),
|
||||
.esr = hv_get_esr(),
|
||||
.far = hv_get_far(),
|
||||
.afsr1 = hv_get_afsr1(),
|
||||
.sp = {mrs(SP_EL0), mrs(SP_EL1), 0},
|
||||
.mpidr = mrs(MPIDR_EL1),
|
||||
.elr_phys = hv_translate(hv_get_elr(), false, false),
|
||||
.far_phys = hv_translate(hv_get_far(), false, false),
|
||||
.sp_phys = hv_translate(from_el == 0 ? mrs(SP_EL0) : mrs(SP_EL1), false, false),
|
||||
.extra = extra,
|
||||
};
|
||||
memcpy(exc_info.regs, regs, sizeof(exc_info.regs));
|
||||
ctx->elr_phys = hv_translate(ctx->elr, false, false);
|
||||
ctx->far_phys = hv_translate(ctx->far, false, false);
|
||||
ctx->sp_phys = hv_translate(from_el == 0 ? ctx->sp[0] : ctx->sp[1], false, false);
|
||||
ctx->extra = extra;
|
||||
|
||||
struct uartproxy_msg_start start = {
|
||||
.reason = reason,
|
||||
.code = type,
|
||||
.info = &exc_info,
|
||||
.info = ctx,
|
||||
};
|
||||
|
||||
hv_wdt_suspend();
|
||||
|
@ -77,11 +67,6 @@ void hv_exc_proxy(u64 *regs, uartproxy_boot_reason_t reason, uartproxy_exc_code_
|
|||
|
||||
switch (ret) {
|
||||
case EXC_RET_HANDLED:
|
||||
memcpy(regs, exc_info.regs, sizeof(exc_info.regs));
|
||||
hv_set_spsr(exc_info.spsr);
|
||||
hv_set_elr(exc_info.elr);
|
||||
msr(SP_EL0, exc_info.sp[0]);
|
||||
msr(SP_EL1, exc_info.sp[1]);
|
||||
hv_wdt_breadcrumb('p');
|
||||
#ifdef TIME_ACCOUNTING
|
||||
u64 lost = mrs(CNTPCT_EL0) - entry_time;
|
||||
|
@ -93,7 +78,7 @@ void hv_exc_proxy(u64 *regs, uartproxy_boot_reason_t reason, uartproxy_exc_code_
|
|||
hv_exit_guest();
|
||||
default:
|
||||
printf("Guest exception not handled, rebooting.\n");
|
||||
print_regs(regs, 0);
|
||||
print_regs(ctx->regs, 0);
|
||||
flush_and_reboot();
|
||||
}
|
||||
}
|
||||
|
@ -144,13 +129,15 @@ static void hv_update_fiq(void)
|
|||
_msr(sr_tkn(sr), regs[rt]); \
|
||||
return true;
|
||||
|
||||
static bool hv_handle_msr(u64 *regs, u64 iss)
|
||||
static bool hv_handle_msr(struct exc_info *ctx, u64 iss)
|
||||
{
|
||||
u64 reg = iss & (ESR_ISS_MSR_OP0 | ESR_ISS_MSR_OP2 | ESR_ISS_MSR_OP1 | ESR_ISS_MSR_CRn |
|
||||
ESR_ISS_MSR_CRm);
|
||||
u64 rt = FIELD_GET(ESR_ISS_MSR_Rt, iss);
|
||||
bool is_read = iss & ESR_ISS_MSR_DIR;
|
||||
|
||||
u64 *regs = ctx->regs;
|
||||
|
||||
regs[31] = 0;
|
||||
|
||||
switch (reg) {
|
||||
|
@ -244,9 +231,19 @@ static bool hv_handle_msr(u64 *regs, u64 iss)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void hv_exc_entry(u64 *regs)
|
||||
static void hv_exc_entry(struct exc_info *ctx)
|
||||
{
|
||||
UNUSED(regs);
|
||||
ctx->spsr = hv_get_spsr();
|
||||
ctx->elr = hv_get_elr();
|
||||
ctx->esr = hv_get_esr();
|
||||
ctx->far = hv_get_far();
|
||||
ctx->afsr1 = hv_get_afsr1();
|
||||
ctx->sp[0] = mrs(SP_EL0);
|
||||
ctx->sp[1] = mrs(SP_EL1);
|
||||
ctx->sp[2] = (u64)ctx;
|
||||
ctx->cpu_id = smp_id();
|
||||
ctx->mpidr = mrs(MPIDR_EL1);
|
||||
|
||||
__atomic_sub_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
|
||||
spin_lock(&bhl);
|
||||
hv_wdt_breadcrumb('X');
|
||||
|
@ -257,9 +254,8 @@ static void hv_exc_entry(u64 *regs)
|
|||
msr(SYS_IMP_APL_PMCR0, pmcr0 & ~PMCR0_CNT_MASK);
|
||||
}
|
||||
|
||||
static void hv_exc_exit(u64 *regs)
|
||||
static void hv_exc_exit(struct exc_info *ctx)
|
||||
{
|
||||
UNUSED(regs);
|
||||
hv_wdt_breadcrumb('x');
|
||||
hv_update_fiq();
|
||||
/* reenable PMU counters */
|
||||
|
@ -267,30 +263,34 @@ static void hv_exc_exit(u64 *regs)
|
|||
msr(CNTVOFF_EL2, stolen_time);
|
||||
spin_unlock(&bhl);
|
||||
__atomic_add_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
|
||||
|
||||
hv_set_spsr(ctx->spsr);
|
||||
hv_set_elr(ctx->elr);
|
||||
msr(SP_EL0, ctx->sp[0]);
|
||||
msr(SP_EL1, ctx->sp[1]);
|
||||
}
|
||||
|
||||
void hv_exc_sync(u64 *regs)
|
||||
void hv_exc_sync(struct exc_info *ctx)
|
||||
{
|
||||
hv_wdt_breadcrumb('S');
|
||||
hv_exc_entry(regs);
|
||||
hv_exc_entry(ctx);
|
||||
bool handled = false;
|
||||
u64 esr = hv_get_esr();
|
||||
u32 ec = FIELD_GET(ESR_EC, esr);
|
||||
u32 ec = FIELD_GET(ESR_EC, ctx->esr);
|
||||
|
||||
switch (ec) {
|
||||
case ESR_EC_DABORT_LOWER:
|
||||
hv_wdt_breadcrumb('D');
|
||||
handled = hv_handle_dabort(regs);
|
||||
handled = hv_handle_dabort(ctx);
|
||||
break;
|
||||
case ESR_EC_MSR:
|
||||
hv_wdt_breadcrumb('M');
|
||||
handled = hv_handle_msr(regs, FIELD_GET(ESR_ISS, esr));
|
||||
handled = hv_handle_msr(ctx, FIELD_GET(ESR_ISS, ctx->esr));
|
||||
break;
|
||||
case ESR_EC_IMPDEF:
|
||||
hv_wdt_breadcrumb('A');
|
||||
switch (FIELD_GET(ESR_ISS, esr)) {
|
||||
switch (FIELD_GET(ESR_ISS, ctx->esr)) {
|
||||
case ESR_ISS_IMPDEF_MSR:
|
||||
handled = hv_handle_msr(regs, hv_get_afsr1());
|
||||
handled = hv_handle_msr(ctx, ctx->afsr1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -298,38 +298,38 @@ void hv_exc_sync(u64 *regs)
|
|||
|
||||
if (handled) {
|
||||
hv_wdt_breadcrumb('+');
|
||||
hv_set_elr(hv_get_elr() + 4);
|
||||
ctx->elr += 4;
|
||||
} else {
|
||||
hv_wdt_breadcrumb('-');
|
||||
hv_exc_proxy(regs, START_EXCEPTION_LOWER, EXC_SYNC, NULL);
|
||||
hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SYNC, NULL);
|
||||
}
|
||||
|
||||
hv_exc_exit(regs);
|
||||
hv_exc_exit(ctx);
|
||||
hv_wdt_breadcrumb('s');
|
||||
}
|
||||
|
||||
void hv_exc_irq(u64 *regs)
|
||||
void hv_exc_irq(struct exc_info *ctx)
|
||||
{
|
||||
hv_wdt_breadcrumb('I');
|
||||
hv_exc_entry(regs);
|
||||
hv_exc_proxy(regs, START_EXCEPTION_LOWER, EXC_IRQ, NULL);
|
||||
hv_exc_exit(regs);
|
||||
hv_exc_entry(ctx);
|
||||
hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_IRQ, NULL);
|
||||
hv_exc_exit(ctx);
|
||||
hv_wdt_breadcrumb('i');
|
||||
}
|
||||
|
||||
void hv_exc_fiq(u64 *regs)
|
||||
void hv_exc_fiq(struct exc_info *ctx)
|
||||
{
|
||||
hv_wdt_breadcrumb('F');
|
||||
hv_exc_entry(regs);
|
||||
hv_exc_entry(ctx);
|
||||
if (mrs(CNTP_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
|
||||
msr(CNTP_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE);
|
||||
hv_tick(regs);
|
||||
hv_tick(ctx);
|
||||
hv_arm_tick();
|
||||
}
|
||||
|
||||
if (mrs(CNTV_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
|
||||
msr(CNTV_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE);
|
||||
hv_exc_proxy(regs, START_HV, HV_VTIMER, NULL);
|
||||
hv_exc_proxy(ctx, START_HV, HV_VTIMER, NULL);
|
||||
}
|
||||
|
||||
u64 reg = mrs(SYS_IMP_APL_PMCR0);
|
||||
|
@ -345,7 +345,7 @@ void hv_exc_fiq(u64 *regs)
|
|||
if ((reg & UPMCR0_IMODE_MASK) == UPMCR0_IMODE_FIQ && (mrs(SYS_IMP_APL_UPMSR) & UPMSR_IACT)) {
|
||||
printf("[FIQ] UPMC IRQ, masking");
|
||||
reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK);
|
||||
hv_exc_proxy(regs, START_EXCEPTION_LOWER, EXC_FIQ, NULL);
|
||||
hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_FIQ, NULL);
|
||||
}
|
||||
|
||||
if (mrs(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
|
||||
|
@ -356,18 +356,18 @@ void hv_exc_fiq(u64 *regs)
|
|||
msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING);
|
||||
sysop("isb");
|
||||
}
|
||||
hv_check_rendezvous(regs);
|
||||
hv_check_rendezvous(ctx);
|
||||
|
||||
// Handles guest timers
|
||||
hv_exc_exit(regs);
|
||||
hv_exc_exit(ctx);
|
||||
hv_wdt_breadcrumb('f');
|
||||
}
|
||||
|
||||
void hv_exc_serr(u64 *regs)
|
||||
void hv_exc_serr(struct exc_info *ctx)
|
||||
{
|
||||
hv_wdt_breadcrumb('E');
|
||||
hv_exc_entry(regs);
|
||||
hv_exc_proxy(regs, START_EXCEPTION_LOWER, EXC_SERROR, NULL);
|
||||
hv_exc_exit(regs);
|
||||
hv_exc_entry(ctx);
|
||||
hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SERROR, NULL);
|
||||
hv_exc_exit(ctx);
|
||||
hv_wdt_breadcrumb('e');
|
||||
}
|
||||
|
|
20
src/hv_vm.c
20
src/hv_vm.c
|
@ -443,11 +443,12 @@ union simd_reg {
|
|||
u8 b[16];
|
||||
};
|
||||
|
||||
static bool emulate_load(u64 *regs, u32 insn, u64 *val, u64 *width)
|
||||
static bool emulate_load(struct exc_info *ctx, u32 insn, u64 *val, u64 *width)
|
||||
{
|
||||
u64 Rt = insn & 0x1f;
|
||||
u64 Rn = (insn >> 5) & 0x1f;
|
||||
u64 imm9 = EXT((insn >> 12) & 0x1ff, 9);
|
||||
u64 *regs = ctx->regs;
|
||||
|
||||
union simd_reg simd[32];
|
||||
|
||||
|
@ -571,11 +572,12 @@ static bool emulate_load(u64 *regs, u32 insn, u64 *val, u64 *width)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool emulate_store(u64 *regs, u32 insn, u64 *val, u64 *width)
|
||||
static bool emulate_store(struct exc_info *ctx, u32 insn, u64 *val, u64 *width)
|
||||
{
|
||||
u64 Rt = insn & 0x1f;
|
||||
u64 Rn = (insn >> 5) & 0x1f;
|
||||
u64 imm9 = EXT((insn >> 12) & 0x1ff, 9);
|
||||
u64 *regs = ctx->regs;
|
||||
|
||||
*width = insn >> 30;
|
||||
|
||||
|
@ -701,7 +703,7 @@ bool hv_pa_rw(u64 addr, u64 *val, bool write, int width)
|
|||
return hv_pa_read(addr, val, width);
|
||||
}
|
||||
|
||||
bool hv_handle_dabort(u64 *regs)
|
||||
bool hv_handle_dabort(struct exc_info *ctx)
|
||||
{
|
||||
hv_wdt_breadcrumb('0');
|
||||
u64 esr = hv_get_esr();
|
||||
|
@ -740,7 +742,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
u64 target = pte & PTE_TARGET_MASK_L4;
|
||||
u64 paddr = target | (far & MASK(VADDR_L4_OFFSET_BITS));
|
||||
|
||||
u64 elr = hv_get_elr();
|
||||
u64 elr = ctx->elr;
|
||||
u64 elr_pa = hv_translate(elr, false, false);
|
||||
if (!elr_pa) {
|
||||
printf("HV: Failed to fetch instruction for data abort at 0x%lx\n", elr);
|
||||
|
@ -756,7 +758,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
if (esr & ESR_ISS_DABORT_WnR) {
|
||||
hv_wdt_breadcrumb('W');
|
||||
|
||||
if (!emulate_store(regs, insn, val, &width)) {
|
||||
if (!emulate_store(ctx, insn, val, &width)) {
|
||||
printf("HV: store not emulated: 0x%08x at 0x%lx\n", insn, ipa);
|
||||
return false;
|
||||
}
|
||||
|
@ -797,7 +799,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
.addr = ipa,
|
||||
.data = {val[0], val[1]},
|
||||
};
|
||||
hv_exc_proxy(regs, START_HV, HV_HOOK_VM, &hook);
|
||||
hv_exc_proxy(ctx, START_HV, HV_HOOK_VM, &hook);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -807,7 +809,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
} else {
|
||||
hv_wdt_breadcrumb('R');
|
||||
|
||||
if (!emulate_load(regs, insn, NULL, &width)) {
|
||||
if (!emulate_load(ctx, insn, NULL, &width)) {
|
||||
printf("HV: load not emulated: 0x%08x at 0x%lx\n", insn, ipa);
|
||||
return false;
|
||||
}
|
||||
|
@ -841,7 +843,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
.id = FIELD_GET(PTE_TARGET_MASK_L4, pte),
|
||||
.addr = ipa,
|
||||
};
|
||||
hv_exc_proxy(regs, START_HV, HV_HOOK_VM, &hook);
|
||||
hv_exc_proxy(ctx, START_HV, HV_HOOK_VM, &hook);
|
||||
memcpy(val, hook.data, sizeof(val));
|
||||
break;
|
||||
}
|
||||
|
@ -855,7 +857,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
emit_mmiotrace(elr, ipa, val, width, 0, pte & SPTE_TRACE_UNBUF);
|
||||
|
||||
hv_wdt_breadcrumb('8');
|
||||
if (!emulate_load(regs, insn, val, &width))
|
||||
if (!emulate_load(ctx, insn, val, &width))
|
||||
return false;
|
||||
|
||||
hv_wdt_breadcrumb('9');
|
||||
|
|
Loading…
Reference in a new issue