mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-15 09:36:35 +00:00
thermosphere: hvisor_exception_frame.hpp
This commit is contained in:
parent
77fbbb4c68
commit
4adb675072
3 changed files with 145 additions and 159 deletions
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "utils.h"
|
|
||||||
#include "core_ctx.h"
|
|
||||||
|
|
||||||
// Adapted from https://developer.arm.com/docs/ddi0596/a/a64-shared-pseudocode-functions/shared-exceptions-pseudocode
|
|
||||||
typedef enum ExceptionClass {
|
|
||||||
Exception_Uncategorized = 0x0,
|
|
||||||
Exception_WFxTrap = 0x1,
|
|
||||||
Exception_CP15RTTrap = 0x3,
|
|
||||||
Exception_CP15RRTTrap = 0x4,
|
|
||||||
Exception_CP14RTTrap = 0x5,
|
|
||||||
Exception_CP14DTTrap = 0x6,
|
|
||||||
Exception_AdvSIMDFPAccessTrap = 0x7,
|
|
||||||
Exception_FPIDTrap = 0x8,
|
|
||||||
Exception_PACTrap = 0x9,
|
|
||||||
Exception_CP14RRTTrap = 0xC,
|
|
||||||
Exception_BranchTargetException = 0xD, // No official enum field name from Arm yet
|
|
||||||
Exception_IllegalState = 0xE,
|
|
||||||
Exception_SupervisorCallA32 = 0x11,
|
|
||||||
Exception_HypervisorCallA32 = 0x12,
|
|
||||||
Exception_MonitorCallA32 = 0x13,
|
|
||||||
Exception_SupervisorCallA64 = 0x15,
|
|
||||||
Exception_HypervisorCallA64 = 0x16,
|
|
||||||
Exception_MonitorCallA64 = 0x17,
|
|
||||||
Exception_SystemRegisterTrap = 0x18,
|
|
||||||
Exception_SVEAccessTrap = 0x19,
|
|
||||||
Exception_ERetTrap = 0x1A,
|
|
||||||
Exception_El3_ImplementationDefined = 0x1F,
|
|
||||||
Exception_InstructionAbortLowerEl = 0x20,
|
|
||||||
Exception_InstructionAbortSameEl = 0x21,
|
|
||||||
Exception_PCAlignment = 0x22,
|
|
||||||
Exception_DataAbortLowerEl = 0x24,
|
|
||||||
Exception_DataAbortSameEl = 0x25,
|
|
||||||
Exception_SPAlignment = 0x26,
|
|
||||||
Exception_FPTrappedExceptionA32 = 0x28,
|
|
||||||
Exception_FPTrappedExceptionA64 = 0x2C,
|
|
||||||
Exception_SError = 0x2F,
|
|
||||||
Exception_BreakpointLowerEl = 0x30,
|
|
||||||
Exception_BreakpointSameEl = 0x31,
|
|
||||||
Exception_SoftwareStepLowerEl = 0x32,
|
|
||||||
Exception_SoftwareStepSameEl = 0x33,
|
|
||||||
Exception_WatchpointLowerEl = 0x34,
|
|
||||||
Exception_WatchpointSameEl = 0x35,
|
|
||||||
Exception_SoftwareBreakpointA32 = 0x38,
|
|
||||||
Exception_VectorCatchA32 = 0x3A,
|
|
||||||
Exception_SoftwareBreakpointA64 = 0x3C,
|
|
||||||
} ExceptionClass;
|
|
||||||
|
|
||||||
typedef struct ExceptionSyndromeRegister {
|
|
||||||
u32 iss : 25; // Instruction Specific Syndrome
|
|
||||||
u32 il : 1; // Instruction Length (16 or 32-bit)
|
|
||||||
ExceptionClass ec : 6; // Exception Class
|
|
||||||
u32 res0 : 32;
|
|
||||||
} ExceptionSyndromeRegister;
|
|
||||||
|
|
||||||
typedef struct ExceptionStackFrame {
|
|
||||||
u64 x[31]; // x0 .. x30
|
|
||||||
u64 sp_el1;
|
|
||||||
union {
|
|
||||||
u64 sp_el2;
|
|
||||||
u64 sp_el0;
|
|
||||||
};
|
|
||||||
u64 elr_el2;
|
|
||||||
u64 spsr_el2;
|
|
||||||
ExceptionSyndromeRegister esr_el2;
|
|
||||||
u64 far_el2;
|
|
||||||
u64 cntpct_el0;
|
|
||||||
u64 cntp_ctl_el0;
|
|
||||||
u64 cntv_ctl_el0;
|
|
||||||
} ExceptionStackFrame;
|
|
||||||
|
|
||||||
//static_assert(offsetof(ExceptionStackFrame, far_el2) == 0x120, "Wrong definition for ExceptionStackFrame");
|
|
||||||
//static_assert(sizeof(ExceptionStackFrame) == 0x140, "Wrong size for ExceptionStackFrame");
|
|
||||||
|
|
||||||
static inline bool spsrIsA32(u64 spsr)
|
|
||||||
{
|
|
||||||
return (spsr & 0x10) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool spsrIsThumb(u64 spsr)
|
|
||||||
{
|
|
||||||
return spsrIsA32(spsr) && (spsr & 0x20) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 spsrGetT32ItFlags(u64 spsr)
|
|
||||||
{
|
|
||||||
return (((spsr >> 10) & 0x3F) << 2) | ((spsr >> 25) & 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void spsrSetT32ItFlags(u64 *spsr, u32 itFlags)
|
|
||||||
{
|
|
||||||
static const u32 itMask = (0x3F << 10) | (3 << 25);
|
|
||||||
*spsr &= ~itMask;
|
|
||||||
*spsr |= (itFlags & 3) << 25;
|
|
||||||
*spsr |= ((itFlags >> 2) & 0x3F) << 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u64 readFrameRegister(ExceptionStackFrame *frame, u32 id)
|
|
||||||
{
|
|
||||||
return frame->x[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u64 readFrameRegisterZ(ExceptionStackFrame *frame, u32 id)
|
|
||||||
{
|
|
||||||
return id == 31 ? 0 /* xzr */ : frame->x[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void writeFrameRegister(ExceptionStackFrame *frame, u32 id, u64 val)
|
|
||||||
{
|
|
||||||
frame->x[id] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void writeFrameRegisterZ(ExceptionStackFrame *frame, u32 id, u64 val)
|
|
||||||
{
|
|
||||||
if (id != 31) {
|
|
||||||
// If not xzr
|
|
||||||
frame->x[id] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u64 *exceptionGetSpPtr(ExceptionStackFrame *frame)
|
|
||||||
{
|
|
||||||
// Note: the return value is more or less meaningless if we took an exception from A32...
|
|
||||||
// We try our best to reflect which privilege level the exception was took from, nonetheless
|
|
||||||
|
|
||||||
bool spEl0;
|
|
||||||
u64 m = frame->spsr_el2 & 0xF;
|
|
||||||
if (spsrIsA32(frame->spsr_el2)) {
|
|
||||||
spEl0 = m == 0;
|
|
||||||
} else {
|
|
||||||
u64 el = m >> 2;
|
|
||||||
spEl0 = el == 2 || el == 0 || (m & 1) == 0; // note: frame->sp_el2 is aliased to frame->sp_el0
|
|
||||||
}
|
|
||||||
|
|
||||||
return spEl0 ? &frame->sp_el0 : &frame->sp_el1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool spsrEvaluateConditionCode(u64 spsr, u32 conditionCode);
|
|
||||||
void skipFaultingInstruction(ExceptionStackFrame *frame, u32 size);
|
|
||||||
void dumpStackFrame(const ExceptionStackFrame *frame, bool sameEl);
|
|
||||||
|
|
||||||
void exceptionEnterInterruptibleHypervisorCode(void);
|
|
144
thermosphere/src/hvisor_exception_frame.hpp
Normal file
144
thermosphere/src/hvisor_exception_frame.hpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cpu/hvisor_cpu_exception_sysregs.hpp"
|
||||||
|
|
||||||
|
namespace ams::hvisor {
|
||||||
|
|
||||||
|
struct alignas(16) ExceptionStackFrame {
|
||||||
|
u64 x[31]; // x0 .. x30
|
||||||
|
union {
|
||||||
|
u64 sp_el1;
|
||||||
|
u64 sp_el2;
|
||||||
|
};
|
||||||
|
u64 sp_el0;
|
||||||
|
u64 elr_el2;
|
||||||
|
u64 spsr_el2;
|
||||||
|
cpu::ExceptionSyndromeRegister esr_el2;
|
||||||
|
u64 far_el2;
|
||||||
|
u64 cntpct_el0;
|
||||||
|
u64 cntp_ctl_el0;
|
||||||
|
u64 cntv_ctl_el0;
|
||||||
|
|
||||||
|
constexpr bool IsA32() const { return (spsr_el2 & cpu::PSR_MODE32) != 0; }
|
||||||
|
constexpr bool IsThumb() const { return IsA32() && (spsr_el2 & cpu::PSR_AA32_THUMB) != 0; }
|
||||||
|
|
||||||
|
constexpr u32 GetT32ItFlags() const
|
||||||
|
{
|
||||||
|
u64 it10 = (spsr_el2 >> cpu::PSR_AA32_IT10_MASK) & cpu::PSR_AA32_IT10_MASK;
|
||||||
|
u64 it72 = (spsr_el2 >> cpu::PSR_AA32_IT72_MASK) & cpu::PSR_AA32_IT72_MASK;
|
||||||
|
return it72 << 2 | it10;
|
||||||
|
}
|
||||||
|
constexpr void SetT32ItFlags(u32 flags)
|
||||||
|
{
|
||||||
|
spsr_el2 &= ~(cpu::PSR_AA32_IT72_MASK << cpu::PSR_AA32_IT72_SHIFT);
|
||||||
|
spsr_el2 &= ~(cpu::PSR_AA32_IT10_MASK << cpu::PSR_AA32_IT10_SHIFT);
|
||||||
|
|
||||||
|
u64 it10 = flags & cpu::PSR_AA32_IT10_MASK;
|
||||||
|
u64 it72 = (flags >> 2) & cpu::PSR_AA32_IT72_MASK;
|
||||||
|
|
||||||
|
spsr_el2 |= it72 << cpu::PSR_AA32_IT72_SHIFT;
|
||||||
|
spsr_el2 |= it10 << cpu::PSR_AA32_IT10_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool EvaluateConditionCode(u32 conditionCode) const
|
||||||
|
{
|
||||||
|
u64 spsr = spsr_el2;
|
||||||
|
if (conditionCode == 14) {
|
||||||
|
// AL
|
||||||
|
return true;
|
||||||
|
} else if (conditionCode == 15) {
|
||||||
|
// Invalid encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NZCV
|
||||||
|
bool n = (spsr & BIT(31)) != 0;
|
||||||
|
bool z = (spsr & BIT(30)) != 0;
|
||||||
|
bool c = (spsr & BIT(29)) != 0;
|
||||||
|
bool v = (spsr & BIT(28)) != 0;
|
||||||
|
|
||||||
|
bool tableHalf[] = {
|
||||||
|
// EQ, CS, MI, VS, HI, GE, GT
|
||||||
|
z, c, n, v, c && !z, n == v, !z && n == v,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (conditionCode & 1) == 0 ? tableHalf[conditionCode / 2] : !tableHalf[conditionCode / 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void AdvanceItState()
|
||||||
|
{
|
||||||
|
u32 it = GetT32ItFlags();
|
||||||
|
|
||||||
|
// Just in case EL0 is executing A32 (& not sure if fully supported)
|
||||||
|
if (!IsThumb() || it == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last instruction of the block => wipe, otherwise advance
|
||||||
|
SetT32ItFlags((it & 7) == 0 ? 0 : (it & 0xE0) | ((it << 1) & 0x1F));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SkipInstruction(size_t size)
|
||||||
|
{
|
||||||
|
AdvanceItState();
|
||||||
|
elr_el2 += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = u64>
|
||||||
|
constexpr T ReadFrameRegister(u32 id) const
|
||||||
|
{
|
||||||
|
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>);
|
||||||
|
return id == 31 ? static_cast<T>(0u) /* xzr */ : static_cast<T>(x[id]);
|
||||||
|
}
|
||||||
|
constexpr void WriteFrameRegister(u32 id, u64 val)
|
||||||
|
{
|
||||||
|
if (id != 31) {
|
||||||
|
// If not xzr
|
||||||
|
x[id] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 &GetSpRef()
|
||||||
|
{
|
||||||
|
// Note: the return value is more or less meaningless if we took an exception from A32...
|
||||||
|
// We try our best to reflect which privilege level the exception was took from, nonetheless
|
||||||
|
|
||||||
|
bool spEl0 = false;
|
||||||
|
u64 m = spsr_el2 & 0xF;
|
||||||
|
if (IsA32()) {
|
||||||
|
spEl0 = m == 0;
|
||||||
|
} else {
|
||||||
|
u64 el = m >> 2;
|
||||||
|
spEl0 = el == 0 || (m & 1) == 0; // note: frame->sp_el2 is aliased to frame->sp_el1
|
||||||
|
}
|
||||||
|
|
||||||
|
return spEl0 ? sp_el0 : sp_el1;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(offsetof(ExceptionStackFrame, far_el2) == 0x120, "Wrong definition for ExceptionStackFrame");
|
||||||
|
static_assert(sizeof(ExceptionStackFrame) == 0x140, "Wrong size for ExceptionStackFrame");
|
||||||
|
|
||||||
|
static_assert(std::is_standard_layout_v<ExceptionStackFrame>);
|
||||||
|
static_assert(std::is_trivial_v<ExceptionStackFrame>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*void dumpStackFrame(const ExceptionStackFrame *frame, bool sameEl);
|
||||||
|
void exceptionEnterInterruptibleHypervisorCode(void);*/
|
|
@ -17,7 +17,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "spinlock.h"
|
//#include "spinlock.h"
|
||||||
|
|
||||||
#define MAX_TRANSPORT_INTERFACES 4
|
#define MAX_TRANSPORT_INTERFACES 4
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue