2021-05-04 10:36:23 +00:00
|
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
|
|
|
|
#include "hv.h"
|
|
|
|
#include "assert.h"
|
|
|
|
#include "cpu_regs.h"
|
|
|
|
#include "exception.h"
|
|
|
|
#include "string.h"
|
|
|
|
#include "uartproxy.h"
|
|
|
|
|
2021-05-04 15:24:52 +00:00
|
|
|
void hv_exit_guest(void) __attribute__((noreturn));
|
|
|
|
|
2021-05-04 10:36:23 +00:00
|
|
|
static void hv_exc_proxy(u64 *regs, uartproxy_exc_code_t type)
|
|
|
|
{
|
2021-05-04 15:27:21 +00:00
|
|
|
int from_el = FIELD_GET(SPSR_M, mrs(SPSR_EL2)) >> 2;
|
|
|
|
|
2021-05-04 10:36:23 +00:00
|
|
|
struct uartproxy_exc_info exc_info = {
|
|
|
|
.spsr = mrs(SPSR_EL2),
|
|
|
|
.elr = mrs(ELR_EL2),
|
|
|
|
.esr = mrs(ESR_EL2),
|
|
|
|
.far = mrs(FAR_EL2),
|
|
|
|
.sp = {mrs(SP_EL0), mrs(SP_EL1), 0},
|
|
|
|
.mpidr = mrs(MPIDR_EL1),
|
2021-05-04 18:27:19 +00:00
|
|
|
.elr_phys = hv_translate(mrs(ELR_EL2), false, false),
|
|
|
|
.far_phys = hv_translate(mrs(FAR_EL2), false, false),
|
|
|
|
.sp_phys = hv_translate(from_el == 0 ? mrs(SP_EL0) : mrs(SP_EL1), false, false),
|
2021-05-04 10:36:23 +00:00
|
|
|
};
|
|
|
|
memcpy(exc_info.regs, regs, sizeof(exc_info.regs));
|
|
|
|
|
|
|
|
struct uartproxy_msg_start start = {
|
|
|
|
.reason = START_EXCEPTION_LOWER,
|
|
|
|
.code = type,
|
|
|
|
.info = &exc_info,
|
|
|
|
};
|
|
|
|
|
|
|
|
int ret = uartproxy_run(&start);
|
|
|
|
|
2021-05-04 15:24:52 +00:00
|
|
|
switch (ret) {
|
2021-05-08 18:15:25 +00:00
|
|
|
case EXC_RET_STEP:
|
2021-05-04 15:24:52 +00:00
|
|
|
case EXC_RET_HANDLED:
|
|
|
|
memcpy(regs, exc_info.regs, sizeof(exc_info.regs));
|
|
|
|
msr(SPSR_EL2, exc_info.spsr);
|
|
|
|
msr(ELR_EL2, exc_info.elr);
|
|
|
|
msr(SP_EL0, exc_info.sp[0]);
|
|
|
|
msr(SP_EL1, exc_info.sp[1]);
|
2021-05-08 18:15:25 +00:00
|
|
|
if (ret == EXC_RET_STEP) {
|
|
|
|
msr(CNTV_TVAL_EL0, 256);
|
|
|
|
msr(CNTV_CTL_EL0, 1);
|
|
|
|
}
|
2021-05-04 15:24:52 +00:00
|
|
|
return;
|
|
|
|
case EXC_EXIT_GUEST:
|
|
|
|
hv_exit_guest();
|
|
|
|
default:
|
|
|
|
printf("Guest exception not handled, rebooting.\n");
|
|
|
|
print_regs(regs, 0);
|
2021-05-04 18:21:48 +00:00
|
|
|
flush_and_reboot();
|
2021-05-04 10:36:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void hv_exc_sync(u64 *regs)
|
|
|
|
{
|
|
|
|
u64 esr = mrs(ESR_EL2);
|
|
|
|
u32 ec = FIELD_GET(ESR_EC, esr);
|
|
|
|
|
|
|
|
switch (ec) {
|
|
|
|
case ESR_EC_DABORT_LOWER:
|
2021-05-04 18:29:11 +00:00
|
|
|
if (hv_handle_dabort(regs))
|
2021-05-04 10:36:23 +00:00
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hv_exc_proxy(regs, EXC_SYNC);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hv_exc_irq(u64 *regs)
|
|
|
|
{
|
|
|
|
hv_exc_proxy(regs, EXC_IRQ);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hv_exc_fiq(u64 *regs)
|
|
|
|
{
|
|
|
|
hv_exc_proxy(regs, EXC_FIQ);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hv_exc_serr(u64 *regs)
|
|
|
|
{
|
|
|
|
hv_exc_proxy(regs, EXC_SERROR);
|
|
|
|
}
|