2019-07-24 23:29:17 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sysreg_traps.h"
|
|
|
|
#include "sysreg.h"
|
2019-08-01 00:58:16 +00:00
|
|
|
#include "arm.h"
|
2019-08-01 00:58:16 +00:00
|
|
|
#include "debug_log.h"
|
2019-08-08 19:38:13 +00:00
|
|
|
#include "software_breakpoints.h"
|
2019-07-24 23:29:17 +00:00
|
|
|
|
2019-07-28 16:50:16 +00:00
|
|
|
static void doSystemRegisterRwImpl(u64 *val, u32 iss)
|
|
|
|
{
|
|
|
|
u32 op0 = (iss >> 20) & 3;
|
|
|
|
u32 op2 = (iss >> 17) & 7;
|
|
|
|
u32 op1 = (iss >> 14) & 7;
|
|
|
|
u32 CRn = (iss >> 10) & 15;
|
|
|
|
//u32 Rt = (iss >> 5) & 31;
|
|
|
|
u32 CRm = (iss >> 1) & 15;
|
|
|
|
u32 dir = iss & 1;
|
|
|
|
|
|
|
|
u32 codebuf[] = {
|
|
|
|
0, // TBD
|
|
|
|
0xD65F03C0, // ret
|
|
|
|
};
|
|
|
|
|
|
|
|
codebuf[0] = dir ? MAKE_MRS_FROM_FIELDS(op0, op1, CRn, CRm, op2, 0) : MAKE_MSR_FROM_FIELDS(op0, op1, CRn, CRm, op2, 0);
|
|
|
|
|
2019-08-01 00:58:16 +00:00
|
|
|
flush_dcache_range(codebuf, (u8 *)codebuf + sizeof(codebuf));
|
|
|
|
invalidate_icache_all();
|
2019-07-28 16:50:16 +00:00
|
|
|
|
|
|
|
*val = ((u64 (*)(u64))codebuf)(*val);
|
|
|
|
}
|
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
void doSystemRegisterRead(ExceptionStackFrame *frame, u32 iss, u32 reg)
|
2019-07-28 16:50:16 +00:00
|
|
|
{
|
|
|
|
u64 val = 0;
|
|
|
|
|
|
|
|
iss &= ~((0x1F << 5) | 1);
|
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
// Hooks go here:
|
|
|
|
switch (iss) {
|
|
|
|
default:
|
|
|
|
break;
|
2019-07-28 16:50:16 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
doSystemRegisterRwImpl(&val, iss | 1);
|
2019-12-24 17:35:47 +00:00
|
|
|
writeFrameRegisterZ(frame, reg, val);
|
2019-08-02 20:22:39 +00:00
|
|
|
|
2019-07-28 16:50:16 +00:00
|
|
|
skipFaultingInstruction(frame, 4);
|
|
|
|
}
|
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
void doSystemRegisterWrite(ExceptionStackFrame *frame, u32 iss, u32 reg)
|
2019-07-28 16:50:16 +00:00
|
|
|
{
|
|
|
|
u64 val = 0;
|
|
|
|
iss &= ~((0x1F << 5) | 1);
|
|
|
|
|
2019-12-24 17:35:47 +00:00
|
|
|
val = readFrameRegisterZ(frame, reg);
|
2019-08-01 00:58:16 +00:00
|
|
|
|
2019-08-08 19:38:13 +00:00
|
|
|
bool reevalSoftwareBreakpoints = false;
|
|
|
|
|
2019-08-01 00:58:16 +00:00
|
|
|
// Hooks go here:
|
|
|
|
switch (iss) {
|
2019-08-08 19:38:13 +00:00
|
|
|
case ENCODE_SYSREG_ISS(TTBR0_EL1):
|
|
|
|
case ENCODE_SYSREG_ISS(TTBR1_EL1):
|
|
|
|
case ENCODE_SYSREG_ISS(TCR_EL1):
|
|
|
|
case ENCODE_SYSREG_ISS(SCTLR_EL1):
|
|
|
|
reevalSoftwareBreakpoints = true;
|
|
|
|
break;
|
2019-08-01 00:58:16 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-08 19:38:13 +00:00
|
|
|
if (reevalSoftwareBreakpoints) {
|
|
|
|
revertAllSoftwareBreakpoints();
|
|
|
|
}
|
|
|
|
|
2019-07-28 16:50:16 +00:00
|
|
|
doSystemRegisterRwImpl(&val, iss);
|
2019-08-08 19:38:13 +00:00
|
|
|
|
|
|
|
if (reevalSoftwareBreakpoints) {
|
|
|
|
__dsb_sy();
|
|
|
|
__isb();
|
|
|
|
applyAllSoftwareBreakpoints();
|
|
|
|
}
|
|
|
|
|
2019-07-28 16:50:16 +00:00
|
|
|
skipFaultingInstruction(frame, 4);
|
|
|
|
}
|
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
void handleMsrMrsTrap(ExceptionStackFrame *frame, ExceptionSyndromeRegister esr)
|
2019-07-24 23:29:17 +00:00
|
|
|
{
|
2019-08-02 20:22:39 +00:00
|
|
|
u32 iss = esr.iss;
|
|
|
|
u32 reg = (iss >> 5) & 31;
|
|
|
|
bool isRead = (iss & 1) != 0;
|
2019-07-24 23:29:17 +00:00
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
if (isRead) {
|
|
|
|
doSystemRegisterRead(frame, iss, reg);
|
|
|
|
} else {
|
|
|
|
doSystemRegisterWrite(frame, iss, reg);
|
2019-07-24 23:29:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-25 15:50:15 +00:00
|
|
|
static bool evaluateMcrMrcCondition(u64 spsr, u32 condition, bool condValid)
|
|
|
|
{
|
|
|
|
if (!condValid) {
|
|
|
|
// Only T32 instructions can do that
|
|
|
|
u32 it = spsrGetT32ItFlags(spsr);
|
|
|
|
return it == 0 || spsrEvaluateConditionCode(spsr, it >> 4);
|
|
|
|
} else {
|
|
|
|
return spsrEvaluateConditionCode(spsr, condition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
void handleSysregAccessA32Stub(ExceptionStackFrame *frame, ExceptionSyndromeRegister esr)
|
2019-07-25 15:50:15 +00:00
|
|
|
{
|
2019-08-02 20:22:39 +00:00
|
|
|
// A32 stub: Skip instruction, read 0 if necessary (there are debug regs at EL0)
|
2019-07-25 15:50:15 +00:00
|
|
|
|
2019-08-02 20:22:39 +00:00
|
|
|
if (esr.iss & 1 && evaluateMcrMrcCondition(frame->spsr_el2, (esr.iss >> 20) & 0xF, (esr.iss & BIT(24)) != 0)) {
|
2019-12-24 17:35:47 +00:00
|
|
|
writeFrameRegisterZ(frame, (esr.iss >> 5) & 0x1F, 0);
|
2019-07-25 15:50:15 +00:00
|
|
|
}
|
2019-07-25 23:22:23 +00:00
|
|
|
skipFaultingInstruction(frame, esr.il == 0 ? 2 : 4);
|
|
|
|
}
|