blob: 6d8b85ab62dc79e5f8b999c90b22e651a7dfb5df [file] [log] [blame]
/*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <inject_exp.h>
#include <rec.h>
/*
* Calculate the address of the vector entry when an exception is inserted
* into the Realm.
*
* @vbar The base address of the vector table in the Realm.
* @spsr The Saved Program Status Register at EL2.
*/
static unsigned long calc_vector_entry(unsigned long vbar, unsigned long spsr)
{
unsigned long offset;
if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL1h) {
offset = VBAR_CEL_SP_ELx_OFFSET;
} else if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL1t) {
offset = VBAR_CEL_SP_EL0_OFFSET;
} else if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL0t) {
if ((spsr & MASK(SPSR_EL2_nRW)) == SPSR_EL2_nRW_AARCH64) {
offset = VBAR_LEL_AA64_OFFSET;
} else {
offset = VBAR_LEL_AA32_OFFSET;
}
} else {
assert(false);
offset = 0UL;
}
return vbar + offset;
}
/*
* Calculate the value of the pstate when an exception
* is inserted into the Realm.
*/
static unsigned long calc_pstate(void)
{
/*
* The pstate is EL1, AArch64, SPSel = SP_ELx and:
* DAIF = '1111b'
* NZCV = '0000b'
* TODO: setup TCO, DIT, UAO, PAN, SSBS, BTYPE
*/
unsigned long pstate = SPSR_EL2_MODE_EL1h |
SPSR_EL2_nRW_AARCH64 |
SPSR_EL2_F_BIT |
SPSR_EL2_I_BIT |
SPSR_EL2_A_BIT |
SPSR_EL2_D_BIT;
return pstate;
}
/*
* Calculate the content of the Realm's esr_el1 register when
* the Synchronous Instruction or Data Abort is injected into
* the Realm (EL1).
*
* The value is constructed from the @esr_el2 & @spsr_el2 that
* are captured when the exception from the Realm was taken to EL2.
*
* The fault status code (ESR_EL1.I/DFSC) is set to @fsc
*/
static unsigned long calc_esr_idabort(unsigned long esr_el2,
unsigned long spsr_el2,
unsigned long fsc)
{
/*
* Copy esr_el2 into esr_el1 apart from the following fields:
* - The exception class (EC). Its value depends on whether the
* exception to EL2 was from either EL1 or EL0.
* - I/DFSC. It will be set to @fsc.
* - FnV. It will set to zero.
* - S1PTW. It will be set to zero.
*/
unsigned long esr_el1 = esr_el2 & ~(MASK(ESR_EL2_EC) |
MASK(ESR_EL2_ABORT_FSC) |
ESR_EL2_ABORT_FNV_BIT |
ESR_EL2_ABORT_S1PTW_BIT);
unsigned long ec = esr_el2 & MASK(ESR_EL2_EC);
assert((ec == ESR_EL2_EC_INST_ABORT) || (ec == ESR_EL2_EC_DATA_ABORT));
if ((spsr_el2 & MASK(SPSR_EL2_MODE)) != SPSR_EL2_MODE_EL0t) {
ec += 1UL << ESR_EL2_EC_SHIFT;
}
esr_el1 |= ec;
/*
* Set the I/DFSC.
*/
assert((fsc & ~MASK(ESR_EL2_ABORT_FSC)) == 0UL);
esr_el1 |= fsc;
/*
* Set the EA.
*/
esr_el1 |= ESR_EL2_ABORT_EA_BIT;
return esr_el1;
}
/*
* Inject the Synchronous Instruction or Data Abort into the current REC.
* The I/DFSC field in the ESR_EL1 is set to @fsc
*/
void inject_sync_idabort(unsigned long fsc)
{
unsigned long esr_el2 = read_esr_el2();
unsigned long far_el2 = read_far_el2();
unsigned long elr_el2 = read_elr_el2();
unsigned long spsr_el2 = read_spsr_el2();
unsigned long vbar_el2 = read_vbar_el12();
unsigned long esr_el1 = calc_esr_idabort(esr_el2, spsr_el2, fsc);
unsigned long pc = calc_vector_entry(vbar_el2, spsr_el2);
unsigned long pstate = calc_pstate();
write_far_el12(far_el2);
write_elr_el12(elr_el2);
write_spsr_el12(spsr_el2);
write_esr_el12(esr_el1);
write_elr_el2(pc);
write_spsr_el2(pstate);
}
/*
* Inject the Synchronous Instruction or Data Abort into @rec.
* The I/DFSC field in the ESR_EL1 is set to @fsc
*/
void inject_sync_idabort_rec(struct rec *rec, unsigned long fsc)
{
rec->sysregs.far_el1 = rec->last_run_info.far;
rec->sysregs.elr_el1 = rec->pc;
rec->sysregs.spsr_el1 = rec->pstate;
rec->sysregs.esr_el1 = calc_esr_idabort(rec->last_run_info.esr,
rec->pstate, fsc);
rec->pc = calc_vector_entry(rec->sysregs.vbar_el1, rec->pstate);
rec->pstate = calc_pstate();
}
/*
* Inject the Undefined Synchronous Exception into the current REC.
*/
void realm_inject_undef_abort(void)
{
unsigned long esr = MASK(ESR_EL2_IL) | ESR_EL2_EC_UNKNOWN;
unsigned long elr = read_elr_el2();
unsigned long spsr = read_spsr_el2();
unsigned long vbar = read_vbar_el12();
unsigned long pc = calc_vector_entry(vbar, spsr);
unsigned long pstate = calc_pstate();
write_elr_el12(elr);
write_spsr_el12(spsr);
write_esr_el12(esr);
write_elr_el2(pc);
write_spsr_el2(pstate);
}