mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-target-arm-20251010' of https://gitlab.com/pm215/qemu into staging
target-arm queue: * Implement FEAT_GCS * Implement FEAT_MEC # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmjpBGAZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3nyaEACV1f4oBSn/rzEgX0PYmYzj # jW3tGbEk1i1QFApjkOSbjqNRBKEYLj1LsaeNOVqixRswATe1mMx9ZNWHqJnSd/tw # 7XLr7dN+YsVvYViILL4VLrHipYcLrgyC1Vlg+UK5RsuVPV2O4PZw6T0LoV32CSF6 # r/LbEGKH4VKHOVMRIR7SJlajmkFbHQvTTj3jjXCgQCUQaKfMzkEGK/UGOt2D3H54 # oSrGLif9nRg0o6Ce9NzfC2xb4XSvdwyT3RE84vkuSSlRcmjt9zQEE+kds4yHhAAi # D6w1m+Aq8zh4sKJbqVRp9M7ymb5465xv6p/4Av2r3Gxy3v4d0ADgQahel+AYh8Sp # urzqZWAR66RLrWSEj51K5nbW8yUM6OYNC/VXrtcMBXgBRMeCYVLgZF3hCrqVyDtv # fP61xJBHPd2+nlcJNFEE5yqazFkcpUsoE/gm2lDPPsdPF5DFKky4VkVqJIGreain # 25zGj44q9vDY7slMJMW38rbB3f1pxbxlcljG93N8+2ZzPLKz+7ezvgXFpY2lij0r # qNn7eFEG80roh+lykTe7BroQSQ+pIAxOXM/ouwr+59fsXtnCKrdFG+96WdS+yhsC # 4ss24hvHUvVMGnEGiYbUL/tIwFJku1wBq+a745DiwJwqyVGbavOGApVbrv/9xuWN # s2MWF0xy8CnhPBJwyK4iOg== # =yKgg # -----END PGP SIGNATURE----- # gpg: Signature made Fri 10 Oct 2025 06:04:32 AM PDT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [unknown] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [unknown] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [unknown] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [unknown] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20251010' of https://gitlab.com/pm215/qemu: (76 commits) target/arm: Enable FEAT_MEC in -cpu max target/arm: Implement FEAT_MEC registers target/arm: Add a cpreg flag to indicate no trap in NV tests/tcg/aarch64: Add gcsss tests/tcg/aarch64: Add gcspushm tests/tcg/aarch64: Add gcsstr linux-user/aarch64: Enable GCS in HWCAP linux-user/aarch64: Generate GCS signal records linux-user/aarch64: Inject SIGSEGV for GCS faults target/arm: Enable GCSPR_EL0 for read in user-mode linux-user/aarch64: Implement map_shadow_stack syscall linux-user/aarch64: Release gcs stack on thread exit linux-user/aarch64: Allocate new gcs stack on clone linux-user/aarch64: Implement prctls for GCS target/arm: Enable FEAT_GCS with -cpu max target/arm: Implement EXLOCK check during exception return target/arm: Copy EXLOCKEn to EXLOCK on exception to the same EL target/arm: Load gcs record for RET with PAuth target/arm: Load gcs record for RET target/arm: Add gcs record for BLR with PAuth ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -90,9 +90,6 @@
|
||||
*/
|
||||
QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data));
|
||||
|
||||
/* We currently can't handle more than 16 bits in the MMUIDX bitmask.
|
||||
*/
|
||||
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
|
||||
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
||||
|
||||
static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
|
||||
|
||||
@@ -28,6 +28,7 @@ the following architecture extensions:
|
||||
- FEAT_BF16 (AArch64 BFloat16 instructions)
|
||||
- FEAT_BTI (Branch Target Identification)
|
||||
- FEAT_CCIDX (Extended cache index)
|
||||
- FEAT_CHK (Check Feature Status)
|
||||
- FEAT_CMOW (Control for cache maintenance permission)
|
||||
- FEAT_CRC32 (CRC32 instructions)
|
||||
- FEAT_Crypto (Cryptographic Extension)
|
||||
@@ -72,6 +73,7 @@ the following architecture extensions:
|
||||
- FEAT_FRINTTS (Floating-point to integer instructions)
|
||||
- FEAT_FlagM (Flag manipulation instructions v2)
|
||||
- FEAT_FlagM2 (Enhancements to flag manipulation instructions)
|
||||
- FEAT_GCS (Guarded Control Stack Extension)
|
||||
- FEAT_GTG (Guest translation granule size)
|
||||
- FEAT_HAFDBS (Hardware management of the access flag and dirty bit state)
|
||||
- FEAT_HBC (Hinted conditional branches)
|
||||
@@ -92,6 +94,9 @@ the following architecture extensions:
|
||||
- FEAT_LSE2 (Large System Extensions v2)
|
||||
- FEAT_LSE128 (128-bit Atomics)
|
||||
- FEAT_LVA (Large Virtual Address space)
|
||||
- FEAT_MEC (Memory Encryption Contexts)
|
||||
|
||||
* This is a register-only implementation without encryption.
|
||||
- FEAT_MixedEnd (Mixed-endian support)
|
||||
- FEAT_MixedEndEL0 (Mixed-endian support at EL0)
|
||||
- FEAT_MOPS (Standardization of memory operations)
|
||||
@@ -123,6 +128,8 @@ the following architecture extensions:
|
||||
- FEAT_RME_GPC2 (RME Granule Protection Check 2 Extension)
|
||||
- FEAT_RNG (Random number generator)
|
||||
- FEAT_RPRES (Increased precision of FRECPE and FRSQRTE)
|
||||
- FEAT_S1PIE (Stage 1 permission indirections)
|
||||
- FEAT_S2PIE (Stage 2 permission indirections)
|
||||
- FEAT_S2FWB (Stage 2 forced Write-Back)
|
||||
- FEAT_SB (Speculation Barrier)
|
||||
- FEAT_SCTLR2 (Extension to SCTLR_ELx)
|
||||
|
||||
@@ -25,9 +25,10 @@ typedef uint32_t MemOpIdx;
|
||||
static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
assert(idx <= 15);
|
||||
assert(idx <= 31);
|
||||
assert(clz32(op) >= 5);
|
||||
#endif
|
||||
return (op << 4) | idx;
|
||||
return (op << 5) | idx;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +39,7 @@ static inline MemOpIdx make_memop_idx(MemOp op, unsigned idx)
|
||||
*/
|
||||
static inline MemOp get_memop(MemOpIdx oi)
|
||||
{
|
||||
return oi >> 4;
|
||||
return oi >> 5;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +50,7 @@ static inline MemOp get_memop(MemOpIdx oi)
|
||||
*/
|
||||
static inline unsigned get_mmuidx(MemOpIdx oi)
|
||||
{
|
||||
return oi & 15;
|
||||
return oi & 31;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -198,10 +198,11 @@ struct CPUClass {
|
||||
};
|
||||
|
||||
/*
|
||||
* Fix the number of mmu modes to 16.
|
||||
* Fix the number of mmu modes across all targets.
|
||||
* Current maximum is target/arm/.
|
||||
*/
|
||||
#define NB_MMU_MODES 16
|
||||
typedef uint16_t MMUIdxMap;
|
||||
#define NB_MMU_MODES 22
|
||||
typedef uint32_t MMUIdxMap;
|
||||
|
||||
/* Use a fully associative victim tlb of 8 entries. */
|
||||
#define CPU_VTLB_SIZE 8
|
||||
|
||||
@@ -89,6 +89,11 @@ static void signal_for_exception(CPUARMState *env, vaddr addr)
|
||||
si_code = TARGET_ILL_ILLOPN;
|
||||
break;
|
||||
|
||||
case EC_GCS:
|
||||
si_signo = TARGET_SIGSEGV;
|
||||
si_code = TARGET_SEGV_CPERR;
|
||||
break;
|
||||
|
||||
case EC_MOP:
|
||||
/*
|
||||
* FIXME: The kernel fixes up wrong-option exceptions.
|
||||
|
||||
@@ -169,6 +169,7 @@ abi_ulong get_elf_hwcap(CPUState *cs)
|
||||
GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP);
|
||||
GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC);
|
||||
GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC);
|
||||
GET_FEATURE_ID(aa64_gcs, ARM_HWCAP_A64_GCS);
|
||||
|
||||
return hwcaps;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* AArch64 gcs functions for linux-user
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef AARCH64_GCS_INTERNAL_H
|
||||
#define AARCH64_GCS_INTERNAL_H
|
||||
|
||||
#ifndef PR_SHADOW_STACK_ENABLE
|
||||
# define PR_SHADOW_STACK_ENABLE (1U << 0)
|
||||
# define PR_SHADOW_STACK_WRITE (1U << 1)
|
||||
# define PR_SHADOW_STACK_PUSH (1U << 2)
|
||||
#endif
|
||||
|
||||
static inline uint64_t gcs_get_el0_mode(CPUArchState *env)
|
||||
{
|
||||
uint64_t cr = env->cp15.gcscr_el[0];
|
||||
abi_ulong flags = 0;
|
||||
|
||||
flags |= cr & GCSCR_PCRSEL ? PR_SHADOW_STACK_ENABLE : 0;
|
||||
flags |= cr & GCSCR_STREN ? PR_SHADOW_STACK_WRITE : 0;
|
||||
flags |= cr & GCSCR_PUSHMEN ? PR_SHADOW_STACK_PUSH : 0;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline void gcs_set_el0_mode(CPUArchState *env, uint64_t flags)
|
||||
{
|
||||
uint64_t cr = GCSCRE0_NTR;
|
||||
|
||||
cr |= flags & PR_SHADOW_STACK_ENABLE ? GCSCR_RVCHKEN | GCSCR_PCRSEL : 0;
|
||||
cr |= flags & PR_SHADOW_STACK_WRITE ? GCSCR_STREN : 0;
|
||||
cr |= flags & PR_SHADOW_STACK_PUSH ? GCSCR_PUSHMEN : 0;
|
||||
|
||||
env->cp15.gcscr_el[0] = cr;
|
||||
}
|
||||
|
||||
#endif
|
||||
+132
-6
@@ -22,6 +22,7 @@
|
||||
#include "signal-common.h"
|
||||
#include "linux-user/trace.h"
|
||||
#include "target/arm/cpu-features.h"
|
||||
#include "gcs-internal.h"
|
||||
|
||||
struct target_sigcontext {
|
||||
uint64_t fault_address;
|
||||
@@ -152,6 +153,16 @@ struct target_zt_context {
|
||||
QEMU_BUILD_BUG_ON(TARGET_ZT_SIG_REG_BYTES != \
|
||||
sizeof_field(CPUARMState, za_state.zt0));
|
||||
|
||||
#define TARGET_GCS_MAGIC 0x47435300
|
||||
#define GCS_SIGNAL_CAP(X) ((X) & TARGET_PAGE_MASK)
|
||||
|
||||
struct target_gcs_context {
|
||||
struct target_aarch64_ctx head;
|
||||
uint64_t gcspr;
|
||||
uint64_t features_enabled;
|
||||
uint64_t reserved;
|
||||
};
|
||||
|
||||
struct target_rt_sigframe {
|
||||
struct target_siginfo info;
|
||||
struct target_ucontext uc;
|
||||
@@ -322,6 +333,35 @@ static void target_setup_zt_record(struct target_zt_context *zt,
|
||||
}
|
||||
}
|
||||
|
||||
static bool target_setup_gcs_record(struct target_gcs_context *ctx,
|
||||
CPUARMState *env, uint64_t return_addr)
|
||||
{
|
||||
uint64_t mode = gcs_get_el0_mode(env);
|
||||
uint64_t gcspr = env->cp15.gcspr_el[0];
|
||||
|
||||
if (mode & PR_SHADOW_STACK_ENABLE) {
|
||||
/* Push a cap for the signal frame. */
|
||||
gcspr -= 8;
|
||||
if (put_user_u64(GCS_SIGNAL_CAP(gcspr), gcspr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Push a gcs entry for the trampoline. */
|
||||
if (put_user_u64(return_addr, gcspr - 8)) {
|
||||
return false;
|
||||
}
|
||||
env->cp15.gcspr_el[0] = gcspr - 8;
|
||||
}
|
||||
|
||||
__put_user(TARGET_GCS_MAGIC, &ctx->head.magic);
|
||||
__put_user(sizeof(*ctx), &ctx->head.size);
|
||||
__put_user(gcspr, &ctx->gcspr);
|
||||
__put_user(mode, &ctx->features_enabled);
|
||||
__put_user(0, &ctx->reserved);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void target_restore_general_frame(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
{
|
||||
@@ -502,6 +542,64 @@ static bool target_restore_zt_record(CPUARMState *env,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool target_restore_gcs_record(CPUARMState *env,
|
||||
struct target_gcs_context *ctx,
|
||||
bool *rebuild_hflags)
|
||||
{
|
||||
TaskState *ts = get_task_state(env_cpu(env));
|
||||
uint64_t cur_mode = gcs_get_el0_mode(env);
|
||||
uint64_t new_mode, gcspr;
|
||||
|
||||
__get_user(new_mode, &ctx->features_enabled);
|
||||
__get_user(gcspr, &ctx->gcspr);
|
||||
|
||||
/*
|
||||
* The kernel pushes the value through the hw register:
|
||||
* write_sysreg_s(gcspr, SYS_GCSPR_EL0) in restore_gcs_context,
|
||||
* then read_sysreg_s(SYS_GCSPR_EL0) in gcs_restore_signal.
|
||||
* Since the bottom 3 bits are RES0, this can (CONSTRAINED UNPREDICTABLE)
|
||||
* force align the value. Mirror the choice from gcspr_write().
|
||||
*/
|
||||
gcspr &= ~7;
|
||||
|
||||
if (new_mode & ~(PR_SHADOW_STACK_ENABLE |
|
||||
PR_SHADOW_STACK_WRITE |
|
||||
PR_SHADOW_STACK_PUSH)) {
|
||||
return false;
|
||||
}
|
||||
if ((new_mode ^ cur_mode) & ts->gcs_el0_locked) {
|
||||
return false;
|
||||
}
|
||||
if (new_mode & ~cur_mode & PR_SHADOW_STACK_ENABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_mode & PR_SHADOW_STACK_ENABLE) {
|
||||
uint64_t cap;
|
||||
|
||||
/* Pop and clear the signal cap. */
|
||||
if (get_user_u64(cap, gcspr)) {
|
||||
return false;
|
||||
}
|
||||
if (cap != GCS_SIGNAL_CAP(gcspr)) {
|
||||
return false;
|
||||
}
|
||||
if (put_user_u64(0, gcspr)) {
|
||||
return false;
|
||||
}
|
||||
gcspr += 8;
|
||||
} else {
|
||||
new_mode = 0;
|
||||
}
|
||||
|
||||
env->cp15.gcspr_el[0] = gcspr;
|
||||
if (new_mode != cur_mode) {
|
||||
*rebuild_hflags = true;
|
||||
gcs_set_el0_mode(env, new_mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int target_restore_sigframe(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
{
|
||||
@@ -511,8 +609,10 @@ static int target_restore_sigframe(CPUARMState *env,
|
||||
struct target_za_context *za = NULL;
|
||||
struct target_tpidr2_context *tpidr2 = NULL;
|
||||
struct target_zt_context *zt = NULL;
|
||||
struct target_gcs_context *gcs = NULL;
|
||||
uint64_t extra_datap = 0;
|
||||
bool used_extra = false;
|
||||
bool rebuild_hflags = false;
|
||||
int sve_size = 0;
|
||||
int za_size = 0;
|
||||
int zt_size = 0;
|
||||
@@ -582,6 +682,15 @@ static int target_restore_sigframe(CPUARMState *env,
|
||||
zt_size = size;
|
||||
break;
|
||||
|
||||
case TARGET_GCS_MAGIC:
|
||||
if (gcs
|
||||
|| size != sizeof(struct target_gcs_context)
|
||||
|| !cpu_isar_feature(aa64_gcs, env_archcpu(env))) {
|
||||
goto err;
|
||||
}
|
||||
gcs = (struct target_gcs_context *)ctx;
|
||||
break;
|
||||
|
||||
case TARGET_EXTRA_MAGIC:
|
||||
if (extra || size != sizeof(struct target_extra_context)) {
|
||||
goto err;
|
||||
@@ -612,6 +721,10 @@ static int target_restore_sigframe(CPUARMState *env,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (gcs && !target_restore_gcs_record(env, gcs, &rebuild_hflags)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* SVE data, if present, overwrites FPSIMD data. */
|
||||
if (sve && !target_restore_sve_record(env, sve, sve_size, &svcr)) {
|
||||
goto err;
|
||||
@@ -631,6 +744,9 @@ static int target_restore_sigframe(CPUARMState *env,
|
||||
}
|
||||
if (env->svcr != svcr) {
|
||||
env->svcr = svcr;
|
||||
rebuild_hflags = true;
|
||||
}
|
||||
if (rebuild_hflags) {
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
unlock_user(extra, extra_datap, 0);
|
||||
@@ -701,7 +817,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
uc.tuc_mcontext.__reserved),
|
||||
};
|
||||
int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0;
|
||||
int zt_ofs = 0, esr_ofs = 0;
|
||||
int zt_ofs = 0, esr_ofs = 0, gcs_ofs = 0;
|
||||
int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0;
|
||||
struct target_rt_sigframe *frame;
|
||||
struct target_rt_frame_record *fr;
|
||||
@@ -720,6 +836,11 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
&layout);
|
||||
}
|
||||
|
||||
if (env->cp15.gcspr_el[0]) {
|
||||
gcs_ofs = alloc_sigframe_space(sizeof(struct target_gcs_context),
|
||||
&layout);
|
||||
}
|
||||
|
||||
/* SVE state needs saving only if it exists. */
|
||||
if (cpu_isar_feature(aa64_sve, env_archcpu(env)) ||
|
||||
cpu_isar_feature(aa64_sme, env_archcpu(env))) {
|
||||
@@ -779,6 +900,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
goto give_sigsegv;
|
||||
}
|
||||
|
||||
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
||||
return_addr = ka->sa_restorer;
|
||||
} else {
|
||||
return_addr = default_rt_sigreturn;
|
||||
}
|
||||
|
||||
target_setup_general_frame(frame, env, set);
|
||||
target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
|
||||
if (esr_ofs) {
|
||||
@@ -786,6 +913,10 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
/* Leave ESR_EL1 clear while it's not relevant. */
|
||||
env->cp15.esr_el[1] = 0;
|
||||
}
|
||||
if (gcs_ofs &&
|
||||
!target_setup_gcs_record((void *)frame + gcs_ofs, env, return_addr)) {
|
||||
goto give_sigsegv;
|
||||
}
|
||||
target_setup_end_record((void *)frame + layout.std_end_ofs);
|
||||
if (layout.extra_ofs) {
|
||||
target_setup_extra_record((void *)frame + layout.extra_ofs,
|
||||
@@ -811,11 +942,6 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
__put_user(env->xregs[29], &fr->fp);
|
||||
__put_user(env->xregs[30], &fr->lr);
|
||||
|
||||
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
||||
return_addr = ka->sa_restorer;
|
||||
} else {
|
||||
return_addr = default_rt_sigreturn;
|
||||
}
|
||||
env->xregs[0] = usig;
|
||||
env->xregs[29] = frame_addr + fr_ofs;
|
||||
env->xregs[30] = return_addr;
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
#ifndef AARCH64_TARGET_PRCTL_H
|
||||
#define AARCH64_TARGET_PRCTL_H
|
||||
|
||||
#include "qemu/units.h"
|
||||
#include "target/arm/cpu-features.h"
|
||||
#include "mte_user_helper.h"
|
||||
#include "gcs-internal.h"
|
||||
|
||||
static abi_long do_prctl_sve_get_vl(CPUArchState *env)
|
||||
{
|
||||
@@ -206,4 +208,98 @@ static abi_long do_prctl_get_tagged_addr_ctrl(CPUArchState *env)
|
||||
}
|
||||
#define do_prctl_get_tagged_addr_ctrl do_prctl_get_tagged_addr_ctrl
|
||||
|
||||
static abi_long do_prctl_get_shadow_stack_status(CPUArchState *env,
|
||||
abi_long arg2)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
|
||||
if (!cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
return put_user_ual(gcs_get_el0_mode(env), arg2);
|
||||
}
|
||||
#define do_prctl_get_shadow_stack_status do_prctl_get_shadow_stack_status
|
||||
|
||||
static abi_long gcs_alloc(abi_ulong hint, abi_ulong size)
|
||||
{
|
||||
/*
|
||||
* Without softmmu, we cannot protect GCS memory properly.
|
||||
* Make do with normal read/write permissions. This at least allows
|
||||
* emulation of correct programs which don't access the gcs stack
|
||||
* with normal instructions.
|
||||
*/
|
||||
return target_mmap(hint, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS |
|
||||
(hint ? MAP_FIXED_NOREPLACE : 0), -1, 0);
|
||||
}
|
||||
|
||||
static abi_ulong gcs_new_stack(TaskState *ts)
|
||||
{
|
||||
/* Use guest_stack_size as a proxy for RLIMIT_STACK. */
|
||||
abi_ulong size = MIN(MAX(guest_stack_size / 2, TARGET_PAGE_SIZE), 2 * GiB);
|
||||
abi_ulong base = gcs_alloc(0, size);
|
||||
|
||||
if (base == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ts->gcs_base = base;
|
||||
ts->gcs_size = size;
|
||||
return base + size - 8;
|
||||
}
|
||||
|
||||
static abi_long do_prctl_set_shadow_stack_status(CPUArchState *env,
|
||||
abi_long new_mode)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
TaskState *ts = get_task_state(env_cpu(env));
|
||||
abi_long cur_mode;
|
||||
|
||||
if (!cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
if (new_mode & ~(PR_SHADOW_STACK_ENABLE |
|
||||
PR_SHADOW_STACK_WRITE |
|
||||
PR_SHADOW_STACK_PUSH)) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
cur_mode = gcs_get_el0_mode(env);
|
||||
if ((new_mode ^ cur_mode) & ts->gcs_el0_locked) {
|
||||
return -TARGET_EBUSY;
|
||||
}
|
||||
|
||||
if (new_mode & ~cur_mode & PR_SHADOW_STACK_ENABLE) {
|
||||
abi_long gcspr;
|
||||
|
||||
if (ts->gcs_base || env->cp15.gcspr_el[0]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
gcspr = gcs_new_stack(ts);
|
||||
if (gcspr == -1) {
|
||||
return -TARGET_ENOMEM;
|
||||
}
|
||||
env->cp15.gcspr_el[0] = gcspr;
|
||||
}
|
||||
|
||||
gcs_set_el0_mode(env, new_mode);
|
||||
arm_rebuild_hflags(env);
|
||||
return 0;
|
||||
}
|
||||
#define do_prctl_set_shadow_stack_status do_prctl_set_shadow_stack_status
|
||||
|
||||
static abi_long do_prctl_lock_shadow_stack_status(CPUArchState *env,
|
||||
abi_long arg2)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
TaskState *ts = get_task_state(env_cpu(env));
|
||||
|
||||
if (!cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ts->gcs_el0_locked |= arg2;
|
||||
return 0;
|
||||
}
|
||||
#define do_prctl_lock_shadow_stack_status do_prctl_lock_shadow_stack_status
|
||||
|
||||
#endif /* AARCH64_TARGET_PRCTL_H */
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#define TARGET_SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */
|
||||
#define TARGET_SEGV_MTESERR 9 /* Synchronous ARM MTE exception */
|
||||
#define TARGET_SEGV_CPERR 10 /* Control protection fault */
|
||||
|
||||
#define TARGET_ARCH_HAS_SETUP_FRAME
|
||||
#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1
|
||||
|
||||
@@ -121,6 +121,11 @@ struct TaskState {
|
||||
abi_ulong child_tidptr;
|
||||
#ifdef TARGET_M68K
|
||||
abi_ulong tp_value;
|
||||
#endif
|
||||
#if defined(TARGET_AARCH64)
|
||||
vaddr gcs_base;
|
||||
abi_ulong gcs_size;
|
||||
abi_ulong gcs_el0_locked;
|
||||
#endif
|
||||
int used; /* non zero if used */
|
||||
struct image_info *info;
|
||||
|
||||
@@ -6353,6 +6353,17 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
|
||||
# define PR_SME_VL_LEN_MASK 0xffff
|
||||
# define PR_SME_VL_INHERIT (1 << 17)
|
||||
#endif
|
||||
#ifndef PR_GET_SHADOW_STACK_STATUS
|
||||
# define PR_GET_SHADOW_STACK_STATUS 74
|
||||
# define PR_SET_SHADOW_STACK_STATUS 75
|
||||
# define PR_LOCK_SHADOW_STACK_STATUS 76
|
||||
#endif
|
||||
#ifndef SHADOW_STACK_SET_TOKEN
|
||||
# define SHADOW_STACK_SET_TOKEN (1u << 0)
|
||||
#endif
|
||||
#ifndef SHADOW_STACK_SET_MARKER
|
||||
# define SHADOW_STACK_SET_MARKER (1u << 1)
|
||||
#endif
|
||||
|
||||
#include "target_prctl.h"
|
||||
|
||||
@@ -6399,6 +6410,15 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2)
|
||||
#ifndef do_prctl_sme_set_vl
|
||||
#define do_prctl_sme_set_vl do_prctl_inval1
|
||||
#endif
|
||||
#ifndef do_prctl_get_shadow_stack_status
|
||||
#define do_prctl_get_shadow_stack_status do_prctl_inval1
|
||||
#endif
|
||||
#ifndef do_prctl_set_shadow_stack_status
|
||||
#define do_prctl_set_shadow_stack_status do_prctl_inval1
|
||||
#endif
|
||||
#ifndef do_prctl_lock_shadow_stack_status
|
||||
#define do_prctl_lock_shadow_stack_status do_prctl_inval1
|
||||
#endif
|
||||
|
||||
static abi_long do_prctl_syscall_user_dispatch(CPUArchState *env,
|
||||
abi_ulong arg2, abi_ulong arg3,
|
||||
@@ -6499,6 +6519,21 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
return do_prctl_get_tagged_addr_ctrl(env);
|
||||
case PR_GET_SHADOW_STACK_STATUS:
|
||||
if (arg3 || arg4 || arg5) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
return do_prctl_get_shadow_stack_status(env, arg2);
|
||||
case PR_SET_SHADOW_STACK_STATUS:
|
||||
if (arg3 || arg4 || arg5) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
return do_prctl_set_shadow_stack_status(env, arg2);
|
||||
case PR_LOCK_SHADOW_STACK_STATUS:
|
||||
if (arg3 || arg4 || arg5) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
return do_prctl_lock_shadow_stack_status(env, arg2);
|
||||
|
||||
case PR_GET_UNALIGN:
|
||||
return do_prctl_get_unalign(env, arg2);
|
||||
@@ -6576,6 +6611,54 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
static abi_long do_map_shadow_stack(CPUArchState *env, abi_ulong addr,
|
||||
abi_ulong size, abi_int flags)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
abi_ulong alloc_size;
|
||||
|
||||
if (!cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
return -TARGET_EOPNOTSUPP;
|
||||
}
|
||||
if (flags & ~(SHADOW_STACK_SET_TOKEN | SHADOW_STACK_SET_MARKER)) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
if (addr & ~TARGET_PAGE_MASK) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
if (size == 8 || !QEMU_IS_ALIGNED(size, 8)) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
alloc_size = TARGET_PAGE_ALIGN(size);
|
||||
if (alloc_size < size) {
|
||||
return -TARGET_EOVERFLOW;
|
||||
}
|
||||
|
||||
mmap_lock();
|
||||
addr = gcs_alloc(addr, alloc_size);
|
||||
if (addr != -1) {
|
||||
if (flags & SHADOW_STACK_SET_TOKEN) {
|
||||
abi_ptr cap_ptr = addr + size - 8;
|
||||
uint64_t cap_val;
|
||||
|
||||
if (flags & SHADOW_STACK_SET_MARKER) {
|
||||
/* Leave an extra empty frame at top-of-stack. */
|
||||
cap_ptr -= 8;
|
||||
}
|
||||
cap_val = (cap_ptr & TARGET_PAGE_MASK) | 1;
|
||||
if (put_user_u64(cap_val, cap_ptr)) {
|
||||
/* Allocation succeeded above. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
mmap_unlock();
|
||||
return get_errno(addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define NEW_STACK_SIZE 0x40000
|
||||
|
||||
|
||||
@@ -6657,6 +6740,21 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
|
||||
ts = g_new0(TaskState, 1);
|
||||
init_task_state(ts);
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
/*
|
||||
* If GCS is enabled in the parent thread, it is also enabled
|
||||
* in the child thread, but with a newly allocated stack.
|
||||
*/
|
||||
abi_long new_gcspr = 0;
|
||||
if (env->cp15.gcscr_el[0] & GCSCR_PCRSEL) {
|
||||
new_gcspr = gcs_new_stack(ts);
|
||||
if (new_gcspr == -1) {
|
||||
g_free(ts);
|
||||
return -TARGET_ENOMEM;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Grab a mutex so that thread setup appears atomic. */
|
||||
pthread_mutex_lock(&clone_lock);
|
||||
|
||||
@@ -6678,6 +6776,11 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
|
||||
ts->info = parent_ts->info;
|
||||
ts->signal_mask = parent_ts->signal_mask;
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
ts->gcs_el0_locked = parent_ts->gcs_el0_locked;
|
||||
new_env->cp15.gcspr_el[0] = new_gcspr;
|
||||
#endif
|
||||
|
||||
if (flags & CLONE_CHILD_CLEARTID) {
|
||||
ts->child_tidptr = child_tidptr;
|
||||
}
|
||||
@@ -9380,6 +9483,12 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
|
||||
FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
if (ts->gcs_base) {
|
||||
target_munmap(ts->gcs_base, ts->gcs_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
object_unparent(OBJECT(cpu));
|
||||
object_unref(OBJECT(cpu));
|
||||
/*
|
||||
@@ -14010,6 +14119,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
|
||||
return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5);
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
case TARGET_NR_map_shadow_stack:
|
||||
return do_map_shadow_stack(cpu_env, arg1, arg2, arg3);
|
||||
#endif
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
|
||||
return -TARGET_ENOSYS;
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* QEMU ARM CP Register GCS regiters and instructions
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/icount.h"
|
||||
#include "hw/irq.h"
|
||||
#include "cpu.h"
|
||||
#include "cpu-features.h"
|
||||
#include "cpregs.h"
|
||||
#include "internals.h"
|
||||
|
||||
|
||||
static CPAccessResult access_gcs(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
if (arm_current_el(env) < 3
|
||||
&& arm_feature(env, ARM_FEATURE_EL3)
|
||||
&& !(env->cp15.scr_el3 & SCR_GCSEN)) {
|
||||
return CP_ACCESS_TRAP_EL3;
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
static CPAccessResult access_gcs_el0(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
if (arm_current_el(env) == 0 && !(env->cp15.gcscr_el[0] & GCSCRE0_NTR)) {
|
||||
return CP_ACCESS_TRAP_EL1;
|
||||
}
|
||||
return access_gcs(env, ri, isread);
|
||||
}
|
||||
|
||||
static void gcspr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
/*
|
||||
* Bits [2:0] are RES0, so we might as well clear them now,
|
||||
* rather than upon each usage a-la GetCurrentGCSPointer.
|
||||
*/
|
||||
raw_write(env, ri, value & ~7);
|
||||
}
|
||||
|
||||
static CPAccessResult access_gcspushm(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
int el = arm_current_el(env);
|
||||
if (!(env->cp15.gcscr_el[el] & GCSCR_PUSHMEN)) {
|
||||
return CP_ACCESS_TRAP_BIT | (el ? el : 1);
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
static CPAccessResult access_gcspushx(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
/* Trap if lock taken, and enabled. */
|
||||
if (!(env->pstate & PSTATE_EXLOCK)) {
|
||||
int el = arm_current_el(env);
|
||||
if (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN) {
|
||||
return CP_ACCESS_EXLOCK;
|
||||
}
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
static CPAccessResult access_gcspopcx(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
/* Trap if lock not taken, and enabled. */
|
||||
if (env->pstate & PSTATE_EXLOCK) {
|
||||
int el = arm_current_el(env);
|
||||
if (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN) {
|
||||
return CP_ACCESS_EXLOCK;
|
||||
}
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo gcs_reginfo[] = {
|
||||
{ .name = "GCSCRE0_EL1", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 2,
|
||||
.access = PL1_RW, .accessfn = access_gcs, .fgt = FGT_NGCS_EL0,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[0]) },
|
||||
{ .name = "GCSCR_EL1", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 0,
|
||||
.access = PL1_RW, .accessfn = access_gcs, .fgt = FGT_NGCS_EL1,
|
||||
.nv2_redirect_offset = 0x8d0 | NV2_REDIR_NV1,
|
||||
.vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 5, 0),
|
||||
.vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 5, 0),
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[1]) },
|
||||
{ .name = "GCSCR_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 5, .opc2 = 0,
|
||||
.access = PL2_RW, .accessfn = access_gcs,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[2]) },
|
||||
{ .name = "GCSCR_EL3", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 0,
|
||||
.access = PL3_RW,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[3]) },
|
||||
|
||||
{ .name = "GCSPR_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 2, .crm = 5, .opc2 = 1,
|
||||
.access = PL0_R | PL1_W, .accessfn = access_gcs_el0,
|
||||
.fgt = FGT_NGCS_EL0, .writefn = gcspr_write,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[0]) },
|
||||
{ .name = "GCSPR_EL1", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 1,
|
||||
.access = PL1_RW, .accessfn = access_gcs,
|
||||
.fgt = FGT_NGCS_EL1, .writefn = gcspr_write,
|
||||
.nv2_redirect_offset = 0x8c0 | NV2_REDIR_NV1,
|
||||
.vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 5, 1),
|
||||
.vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 5, 1),
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[1]) },
|
||||
{ .name = "GCSPR_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 5, .opc2 = 1,
|
||||
.access = PL2_RW, .accessfn = access_gcs, .writefn = gcspr_write,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) },
|
||||
{ .name = "GCSPR_EL3", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 1,
|
||||
.access = PL3_RW, .writefn = gcspr_write,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) },
|
||||
|
||||
{ .name = "GCSPUSHM", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 0,
|
||||
.access = PL0_W, .accessfn = access_gcspushm,
|
||||
.fgt = FGT_NGCSPUSHM_EL1, .type = ARM_CP_GCSPUSHM },
|
||||
{ .name = "GCSPOPM", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 1,
|
||||
.access = PL0_R, .type = ARM_CP_GCSPOPM },
|
||||
{ .name = "GCSSS1", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 2,
|
||||
.access = PL0_W, .type = ARM_CP_GCSSS1 },
|
||||
{ .name = "GCSSS2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 3,
|
||||
.access = PL0_R, .type = ARM_CP_GCSSS2 },
|
||||
{ .name = "GCSPUSHX", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4,
|
||||
.access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP,
|
||||
.type = ARM_CP_GCSPUSHX },
|
||||
{ .name = "GCSPOPCX", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 5,
|
||||
.access = PL1_W, .accessfn = access_gcspopcx, .fgt = FGT_NGCSEPP,
|
||||
.type = ARM_CP_GCSPOPCX },
|
||||
{ .name = "GCSPOPX", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 6,
|
||||
.access = PL1_W, .type = ARM_CP_GCSPOPX },
|
||||
};
|
||||
|
||||
void define_gcs_cpregs(ARMCPU *cpu)
|
||||
{
|
||||
if (cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
define_arm_cp_regs(cpu, gcs_reginfo);
|
||||
}
|
||||
}
|
||||
+37
-5
@@ -47,6 +47,14 @@ enum {
|
||||
ARM_CP_DC_ZVA = 0x0005,
|
||||
ARM_CP_DC_GVA = 0x0006,
|
||||
ARM_CP_DC_GZVA = 0x0007,
|
||||
/* Special: gcs instructions */
|
||||
ARM_CP_GCSPUSHM = 0x0008,
|
||||
ARM_CP_GCSPOPM = 0x0009,
|
||||
ARM_CP_GCSPUSHX = 0x000a,
|
||||
ARM_CP_GCSPOPX = 0x000b,
|
||||
ARM_CP_GCSPOPCX = 0x000c,
|
||||
ARM_CP_GCSSS1 = 0x000d,
|
||||
ARM_CP_GCSSS2 = 0x000e,
|
||||
|
||||
/* Flag: reads produce resetvalue; writes ignored. */
|
||||
ARM_CP_CONST = 1 << 4,
|
||||
@@ -136,6 +144,11 @@ enum {
|
||||
* identically to the normal one, other than FGT trapping handling.)
|
||||
*/
|
||||
ARM_CP_ADD_TLBI_NXS = 1 << 21,
|
||||
/*
|
||||
* Flag: even though this sysreg has opc1 == 4 or 5, it
|
||||
* should not trap to EL2 when HCR_EL2.NV is set.
|
||||
*/
|
||||
ARM_CP_NV_NO_TRAP = 1 << 22,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -351,6 +364,14 @@ typedef enum CPAccessResult {
|
||||
* specified target EL.
|
||||
*/
|
||||
CP_ACCESS_UNDEFINED = (2 << 2),
|
||||
|
||||
/*
|
||||
* Access fails with EXLOCK, a GCS exception syndrome.
|
||||
* These traps are always to the current execution EL,
|
||||
* which is the same as the usual target EL because
|
||||
* they cannot occur from EL0.
|
||||
*/
|
||||
CP_ACCESS_EXLOCK = (3 << 2),
|
||||
} CPAccessResult;
|
||||
|
||||
/* Indexes into fgt_read[] */
|
||||
@@ -779,8 +800,12 @@ typedef enum FGTBit {
|
||||
DO_BIT(HFGRTR, VBAR_EL1),
|
||||
DO_BIT(HFGRTR, ICC_IGRPENN_EL1),
|
||||
DO_BIT(HFGRTR, ERRIDR_EL1),
|
||||
DO_REV_BIT(HFGRTR, NGCS_EL0),
|
||||
DO_REV_BIT(HFGRTR, NGCS_EL1),
|
||||
DO_REV_BIT(HFGRTR, NSMPRI_EL1),
|
||||
DO_REV_BIT(HFGRTR, NTPIDR2_EL0),
|
||||
DO_REV_BIT(HFGRTR, NPIRE0_EL1),
|
||||
DO_REV_BIT(HFGRTR, NPIR_EL1),
|
||||
|
||||
/* Trap bits in HDFGRTR_EL2 / HDFGWTR_EL2, starting from bit 0. */
|
||||
DO_BIT(HDFGRTR, DBGBCRN_EL1),
|
||||
@@ -859,6 +884,8 @@ typedef enum FGTBit {
|
||||
DO_BIT(HFGITR, DVPRCTX),
|
||||
DO_BIT(HFGITR, CPPRCTX),
|
||||
DO_BIT(HFGITR, DCCVAC),
|
||||
DO_REV_BIT(HFGITR, NGCSPUSHM_EL1),
|
||||
DO_REV_BIT(HFGITR, NGCSEPP),
|
||||
DO_BIT(HFGITR, ATS1E1A),
|
||||
} FGTBit;
|
||||
|
||||
@@ -1156,12 +1183,17 @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri)
|
||||
* fragile to future new sysregs, but this seems the least likely
|
||||
* to break.
|
||||
*
|
||||
* In particular, note that the released sysreg XML defines that
|
||||
* the FEAT_MEC sysregs and instructions do not follow this FEAT_NV
|
||||
* trapping rule, so we will need to add an ARM_CP_* flag to indicate
|
||||
* "register does not trap on NV" to handle those if/when we implement
|
||||
* FEAT_MEC.
|
||||
* In particular, note that the FEAT_MEC sysregs and instructions
|
||||
* are exceptions to this trapping rule, so they are marked as
|
||||
* ARM_CP_NV_NO_TRAP to indicate that they should not be trapped
|
||||
* to EL2. (They are an exception because the FEAT_MEC sysregs UNDEF
|
||||
* unless in Realm, and Realm is not expected to be virtualized.)
|
||||
*/
|
||||
|
||||
if (ri->type & ARM_CP_NV_NO_TRAP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ri->opc1 == 4 || ri->opc1 == 5;
|
||||
}
|
||||
|
||||
|
||||
@@ -1149,6 +1149,11 @@ static inline bool isar_feature_aa64_nmi(const ARMISARegisters *id)
|
||||
return FIELD_EX64_IDREG(id, ID_AA64PFR1, NMI) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_gcs(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64_IDREG(id, ID_AA64PFR1, GCS) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_SEX64_IDREG(id, ID_AA64MMFR0, TGRAN4) >= 1;
|
||||
@@ -1349,6 +1354,21 @@ static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id)
|
||||
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_s1pie(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, S1PIE) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_s2pie(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, S2PIE) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_mec(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, MEC) != 0;
|
||||
}
|
||||
|
||||
static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id)
|
||||
{
|
||||
return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 &&
|
||||
|
||||
+17
-3
@@ -311,6 +311,10 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type)
|
||||
env->cp15.mdscr_el1 |= 1 << 12;
|
||||
/* Enable FEAT_MOPS */
|
||||
env->cp15.sctlr_el[1] |= SCTLR_MSCEN;
|
||||
/* For Linux, GCSPR_EL0 is always readable. */
|
||||
if (cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
env->cp15.gcscr_el[0] = GCSCRE0_NTR;
|
||||
}
|
||||
#else
|
||||
/* Reset into the highest available EL */
|
||||
if (arm_feature(env, ARM_FEATURE_EL3)) {
|
||||
@@ -635,12 +639,22 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el)
|
||||
if (cpu_isar_feature(aa64_fgt, cpu)) {
|
||||
env->cp15.scr_el3 |= SCR_FGTEN;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_gcs, cpu)) {
|
||||
env->cp15.scr_el3 |= SCR_GCSEN;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_tcr2, cpu)) {
|
||||
env->cp15.scr_el3 |= SCR_TCR2EN;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
|
||||
env->cp15.scr_el3 |= SCR_SCTLR2EN;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_s1pie, cpu) ||
|
||||
cpu_isar_feature(aa64_s2pie, cpu)) {
|
||||
env->cp15.scr_el3 |= SCR_PIEN;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_mec, cpu)) {
|
||||
env->cp15.scr_el3 |= SCR_MECEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_el == 2) {
|
||||
@@ -819,7 +833,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
uint32_t psr = pstate_read(env);
|
||||
uint64_t psr = pstate_read(env);
|
||||
int i, j;
|
||||
int el = arm_current_el(env);
|
||||
uint64_t hcr = arm_hcr_el2_eff(env);
|
||||
@@ -841,7 +855,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
} else {
|
||||
ns_status = "";
|
||||
}
|
||||
qemu_fprintf(f, "PSTATE=%08x %c%c%c%c %sEL%d%c",
|
||||
qemu_fprintf(f, "PSTATE=%016" PRIx64 " %c%c%c%c %sEL%d%c",
|
||||
psr,
|
||||
psr & PSTATE_N ? 'N' : '-',
|
||||
psr & PSTATE_Z ? 'Z' : '-',
|
||||
@@ -858,7 +872,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
(FIELD_EX64(env->svcr, SVCR, SM) ? 'S' : '-'));
|
||||
}
|
||||
if (cpu_isar_feature(aa64_bti, cpu)) {
|
||||
qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10);
|
||||
qemu_fprintf(f, " BTYPE=%d", (int)(psr & PSTATE_BTYPE) >> 10);
|
||||
}
|
||||
qemu_fprintf(f, "%s%s%s",
|
||||
(hcr & HCR_NV) ? " NV" : "",
|
||||
|
||||
+38
-215
@@ -33,6 +33,7 @@
|
||||
#include "target/arm/multiprocessing.h"
|
||||
#include "target/arm/gtimer.h"
|
||||
#include "target/arm/cpu-sysregs.h"
|
||||
#include "target/arm/mmuidx.h"
|
||||
|
||||
#define EXCP_UDEF 1 /* undefined instruction */
|
||||
#define EXCP_SWI 2 /* software interrupt */
|
||||
@@ -267,7 +268,7 @@ typedef struct CPUArchState {
|
||||
uint64_t xregs[32];
|
||||
uint64_t pc;
|
||||
/* PSTATE isn't an architectural register for ARMv8. However, it is
|
||||
* convenient for us to assemble the underlying state into a 32 bit format
|
||||
* convenient for us to assemble the underlying state into a 64 bit format
|
||||
* identical to the architectural format used for the SPSR. (This is also
|
||||
* what the Linux kernel's 'pstate' field in signal handlers and KVM's
|
||||
* 'pstate' register are.) Of the PSTATE bits:
|
||||
@@ -279,7 +280,7 @@ typedef struct CPUArchState {
|
||||
* SM and ZA are kept in env->svcr
|
||||
* all other bits are stored in their correct places in env->pstate
|
||||
*/
|
||||
uint32_t pstate;
|
||||
uint64_t pstate;
|
||||
bool aarch64; /* True if CPU is in aarch64 state; inverse of PSTATE.nRW */
|
||||
bool thumb; /* True if CPU is in thumb mode; cpsr[5] */
|
||||
|
||||
@@ -368,6 +369,9 @@ typedef struct CPUArchState {
|
||||
uint64_t tcr2_el[3];
|
||||
uint64_t vtcr_el2; /* Virtualization Translation Control. */
|
||||
uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */
|
||||
uint64_t pir_el[4]; /* PIRE0_EL1, PIR_EL1, PIR_EL2, PIR_EL3 */
|
||||
uint64_t pire0_el2;
|
||||
uint64_t s2pir_el2;
|
||||
uint32_t c2_data; /* MPU data cacheable bits. */
|
||||
uint32_t c2_insn; /* MPU instruction cacheable bits. */
|
||||
union { /* MMU domain access control register
|
||||
@@ -576,6 +580,18 @@ typedef struct CPUArchState {
|
||||
|
||||
/* NV2 register */
|
||||
uint64_t vncr_el2;
|
||||
|
||||
uint64_t gcscr_el[4]; /* GCSCRE0_EL1, GCSCR_EL[123] */
|
||||
uint64_t gcspr_el[4]; /* GCSPR_EL[0123] */
|
||||
|
||||
/* MEC registers */
|
||||
uint64_t mecid_p0_el2;
|
||||
uint64_t mecid_a0_el2;
|
||||
uint64_t mecid_p1_el2;
|
||||
uint64_t mecid_a1_el2;
|
||||
uint64_t mecid_rl_a_el3;
|
||||
uint64_t vmecid_p_el2;
|
||||
uint64_t vmecid_a_el2;
|
||||
} cp15;
|
||||
|
||||
struct {
|
||||
@@ -630,13 +646,10 @@ typedef struct CPUArchState {
|
||||
* entry process.
|
||||
*/
|
||||
struct {
|
||||
uint32_t syndrome; /* AArch64 format syndrome register */
|
||||
uint32_t fsr; /* AArch32 format fault status register info */
|
||||
uint64_t syndrome; /* AArch64 format syndrome register */
|
||||
uint64_t vaddress; /* virtual addr associated with exception, if any */
|
||||
uint32_t fsr; /* AArch32 format fault status register info */
|
||||
uint32_t target_el; /* EL the exception should be targeted for */
|
||||
/* If we implement EL2 we will also need to store information
|
||||
* about the intermediate physical address for stage 2 faults.
|
||||
*/
|
||||
} exception;
|
||||
|
||||
/* Information associated with an SError */
|
||||
@@ -1498,6 +1511,7 @@ void pmu_init(ARMCPU *cpu);
|
||||
#define PSTATE_C (1U << 29)
|
||||
#define PSTATE_Z (1U << 30)
|
||||
#define PSTATE_N (1U << 31)
|
||||
#define PSTATE_EXLOCK (1ULL << 34)
|
||||
#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
|
||||
#define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F)
|
||||
#define CACHED_PSTATE_BITS (PSTATE_NZCV | PSTATE_DAIF | PSTATE_BTYPE)
|
||||
@@ -1534,7 +1548,7 @@ static inline unsigned int aarch64_pstate_mode(unsigned int el, bool handler)
|
||||
* interprocessing, so we don't attempt to sync with the cpsr state used by
|
||||
* the 32 bit decoder.
|
||||
*/
|
||||
static inline uint32_t pstate_read(CPUARMState *env)
|
||||
static inline uint64_t pstate_read(CPUARMState *env)
|
||||
{
|
||||
int ZF;
|
||||
|
||||
@@ -1544,7 +1558,7 @@ static inline uint32_t pstate_read(CPUARMState *env)
|
||||
| env->pstate | env->daif | (env->btype << 10);
|
||||
}
|
||||
|
||||
static inline void pstate_write(CPUARMState *env, uint32_t val)
|
||||
static inline void pstate_write(CPUARMState *env, uint64_t val)
|
||||
{
|
||||
env->ZF = (~val) & PSTATE_Z;
|
||||
env->NF = val;
|
||||
@@ -1716,13 +1730,24 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||
#define SCR_ENAS0 (1ULL << 36)
|
||||
#define SCR_ADEN (1ULL << 37)
|
||||
#define SCR_HXEN (1ULL << 38)
|
||||
#define SCR_GCSEN (1ULL << 39)
|
||||
#define SCR_TRNDR (1ULL << 40)
|
||||
#define SCR_ENTP2 (1ULL << 41)
|
||||
#define SCR_TCR2EN (1ULL << 43)
|
||||
#define SCR_SCTLR2EN (1ULL << 44)
|
||||
#define SCR_PIEN (1ULL << 45)
|
||||
#define SCR_GPF (1ULL << 48)
|
||||
#define SCR_MECEN (1ULL << 49)
|
||||
#define SCR_NSE (1ULL << 62)
|
||||
|
||||
/* GCSCR_ELx fields */
|
||||
#define GCSCR_PCRSEL (1ULL << 0)
|
||||
#define GCSCR_RVCHKEN (1ULL << 5)
|
||||
#define GCSCR_EXLOCKEN (1ULL << 6)
|
||||
#define GCSCR_PUSHMEN (1ULL << 8)
|
||||
#define GCSCR_STREN (1ULL << 9)
|
||||
#define GCSCRE0_NTR (1ULL << 10)
|
||||
|
||||
/* Return the current FPSCR value. */
|
||||
uint32_t vfp_get_fpscr(CPUARMState *env);
|
||||
void vfp_set_fpscr(CPUARMState *env, uint32_t val);
|
||||
@@ -2221,6 +2246,7 @@ static inline bool arm_is_el2_enabled(CPUARMState *env)
|
||||
*/
|
||||
uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, ARMSecuritySpace space);
|
||||
uint64_t arm_hcr_el2_eff(CPUARMState *env);
|
||||
uint64_t arm_hcr_el2_nvx_eff(CPUARMState *env);
|
||||
uint64_t arm_hcrx_el2_eff(CPUARMState *env);
|
||||
|
||||
/*
|
||||
@@ -2300,212 +2326,6 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync);
|
||||
|
||||
#define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU
|
||||
|
||||
/* ARM has the following "translation regimes" (as the ARM ARM calls them):
|
||||
*
|
||||
* If EL3 is 64-bit:
|
||||
* + NonSecure EL1 & 0 stage 1
|
||||
* + NonSecure EL1 & 0 stage 2
|
||||
* + NonSecure EL2
|
||||
* + NonSecure EL2 & 0 (ARMv8.1-VHE)
|
||||
* + Secure EL1 & 0 stage 1
|
||||
* + Secure EL1 & 0 stage 2 (FEAT_SEL2)
|
||||
* + Secure EL2 (FEAT_SEL2)
|
||||
* + Secure EL2 & 0 (FEAT_SEL2)
|
||||
* + Realm EL1 & 0 stage 1 (FEAT_RME)
|
||||
* + Realm EL1 & 0 stage 2 (FEAT_RME)
|
||||
* + Realm EL2 (FEAT_RME)
|
||||
* + EL3
|
||||
* If EL3 is 32-bit:
|
||||
* + NonSecure PL1 & 0 stage 1
|
||||
* + NonSecure PL1 & 0 stage 2
|
||||
* + NonSecure PL2
|
||||
* + Secure PL1 & 0
|
||||
* (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.)
|
||||
*
|
||||
* For QEMU, an mmu_idx is not quite the same as a translation regime because:
|
||||
* 1. we need to split the "EL1 & 0" and "EL2 & 0" regimes into two mmu_idxes,
|
||||
* because they may differ in access permissions even if the VA->PA map is
|
||||
* the same
|
||||
* 2. we want to cache in our TLB the full VA->IPA->PA lookup for a stage 1+2
|
||||
* translation, which means that we have one mmu_idx that deals with two
|
||||
* concatenated translation regimes [this sort of combined s1+2 TLB is
|
||||
* architecturally permitted]
|
||||
* 3. we don't need to allocate an mmu_idx to translations that we won't be
|
||||
* handling via the TLB. The only way to do a stage 1 translation without
|
||||
* the immediate stage 2 translation is via the ATS or AT system insns,
|
||||
* which can be slow-pathed and always do a page table walk.
|
||||
* The only use of stage 2 translations is either as part of an s1+2
|
||||
* lookup or when loading the descriptors during a stage 1 page table walk,
|
||||
* and in both those cases we don't use the TLB.
|
||||
* 4. we can also safely fold together the "32 bit EL3" and "64 bit EL3"
|
||||
* translation regimes, because they map reasonably well to each other
|
||||
* and they can't both be active at the same time.
|
||||
* 5. we want to be able to use the TLB for accesses done as part of a
|
||||
* stage1 page table walk, rather than having to walk the stage2 page
|
||||
* table over and over.
|
||||
* 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access
|
||||
* Never (PAN) bit within PSTATE.
|
||||
* 7. we fold together most secure and non-secure regimes for A-profile,
|
||||
* because there are no banked system registers for aarch64, so the
|
||||
* process of switching between secure and non-secure is
|
||||
* already heavyweight.
|
||||
* 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure,
|
||||
* because both are in use simultaneously for Secure EL2.
|
||||
*
|
||||
* This gives us the following list of cases:
|
||||
*
|
||||
* EL0 EL1&0 stage 1+2 (aka NS PL0 PL1&0 stage 1+2)
|
||||
* EL1 EL1&0 stage 1+2 (aka NS PL1 PL1&0 stage 1+2)
|
||||
* EL1 EL1&0 stage 1+2 +PAN (aka NS PL1 P1&0 stage 1+2 +PAN)
|
||||
* EL0 EL2&0
|
||||
* EL2 EL2&0
|
||||
* EL2 EL2&0 +PAN
|
||||
* EL2 (aka NS PL2)
|
||||
* EL3 (aka AArch32 S PL1 PL1&0)
|
||||
* AArch32 S PL0 PL1&0 (we call this EL30_0)
|
||||
* AArch32 S PL1 PL1&0 +PAN (we call this EL30_3_PAN)
|
||||
* Stage2 Secure
|
||||
* Stage2 NonSecure
|
||||
* plus one TLB per Physical address space: S, NS, Realm, Root
|
||||
*
|
||||
* for a total of 16 different mmu_idx.
|
||||
*
|
||||
* R profile CPUs have an MPU, but can use the same set of MMU indexes
|
||||
* as A profile. They only need to distinguish EL0 and EL1 (and
|
||||
* EL2 for cores like the Cortex-R52).
|
||||
*
|
||||
* M profile CPUs are rather different as they do not have a true MMU.
|
||||
* They have the following different MMU indexes:
|
||||
* User
|
||||
* Privileged
|
||||
* User, execution priority negative (ie the MPU HFNMIENA bit may apply)
|
||||
* Privileged, execution priority negative (ditto)
|
||||
* If the CPU supports the v8M Security Extension then there are also:
|
||||
* Secure User
|
||||
* Secure Privileged
|
||||
* Secure User, execution priority negative
|
||||
* Secure Privileged, execution priority negative
|
||||
*
|
||||
* The ARMMMUIdx and the mmu index value used by the core QEMU TLB code
|
||||
* are not quite the same -- different CPU types (most notably M profile
|
||||
* vs A/R profile) would like to use MMU indexes with different semantics,
|
||||
* but since we don't ever need to use all of those in a single CPU we
|
||||
* can avoid having to set NB_MMU_MODES to "total number of A profile MMU
|
||||
* modes + total number of M profile MMU modes". The lower bits of
|
||||
* ARMMMUIdx are the core TLB mmu index, and the higher bits are always
|
||||
* the same for any particular CPU.
|
||||
* Variables of type ARMMUIdx are always full values, and the core
|
||||
* index values are in variables of type 'int'.
|
||||
*
|
||||
* Our enumeration includes at the end some entries which are not "true"
|
||||
* mmu_idx values in that they don't have corresponding TLBs and are only
|
||||
* valid for doing slow path page table walks.
|
||||
*
|
||||
* The constant names here are patterned after the general style of the names
|
||||
* of the AT/ATS operations.
|
||||
* The values used are carefully arranged to make mmu_idx => EL lookup easy.
|
||||
* For M profile we arrange them to have a bit for priv, a bit for negpri
|
||||
* and a bit for secure.
|
||||
*/
|
||||
#define ARM_MMU_IDX_A 0x10 /* A profile */
|
||||
#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */
|
||||
#define ARM_MMU_IDX_M 0x40 /* M profile */
|
||||
|
||||
/* Meanings of the bits for M profile mmu idx values */
|
||||
#define ARM_MMU_IDX_M_PRIV 0x1
|
||||
#define ARM_MMU_IDX_M_NEGPRI 0x2
|
||||
#define ARM_MMU_IDX_M_S 0x4 /* Secure */
|
||||
|
||||
#define ARM_MMU_IDX_TYPE_MASK \
|
||||
(ARM_MMU_IDX_A | ARM_MMU_IDX_M | ARM_MMU_IDX_NOTLB)
|
||||
#define ARM_MMU_IDX_COREIDX_MASK 0xf
|
||||
|
||||
typedef enum ARMMMUIdx {
|
||||
/*
|
||||
* A-profile.
|
||||
*/
|
||||
ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E20_0 = 1 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E20_2 = 3 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E10_1_PAN = 4 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E20_2_PAN = 5 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E30_0 = 8 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_E30_3_PAN = 9 | ARM_MMU_IDX_A,
|
||||
|
||||
/*
|
||||
* Used for second stage of an S12 page table walk, or for descriptor
|
||||
* loads during first stage of an S1 page table walk. Note that both
|
||||
* are in use simultaneously for SecureEL2: the security state for
|
||||
* the S2 ptw is selected by the NS bit from the S1 ptw.
|
||||
*/
|
||||
ARMMMUIdx_Stage2_S = 10 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_Stage2 = 11 | ARM_MMU_IDX_A,
|
||||
|
||||
/* TLBs with 1-1 mapping to the physical address spaces. */
|
||||
ARMMMUIdx_Phys_S = 12 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_Phys_NS = 13 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_Phys_Root = 14 | ARM_MMU_IDX_A,
|
||||
ARMMMUIdx_Phys_Realm = 15 | ARM_MMU_IDX_A,
|
||||
|
||||
/*
|
||||
* These are not allocated TLBs and are used only for AT system
|
||||
* instructions or for the first stage of an S12 page table walk.
|
||||
*/
|
||||
ARMMMUIdx_Stage1_E0 = 0 | ARM_MMU_IDX_NOTLB,
|
||||
ARMMMUIdx_Stage1_E1 = 1 | ARM_MMU_IDX_NOTLB,
|
||||
ARMMMUIdx_Stage1_E1_PAN = 2 | ARM_MMU_IDX_NOTLB,
|
||||
|
||||
/*
|
||||
* M-profile.
|
||||
*/
|
||||
ARMMMUIdx_MUser = ARM_MMU_IDX_M,
|
||||
ARMMMUIdx_MPriv = ARM_MMU_IDX_M | ARM_MMU_IDX_M_PRIV,
|
||||
ARMMMUIdx_MUserNegPri = ARMMMUIdx_MUser | ARM_MMU_IDX_M_NEGPRI,
|
||||
ARMMMUIdx_MPrivNegPri = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_NEGPRI,
|
||||
ARMMMUIdx_MSUser = ARMMMUIdx_MUser | ARM_MMU_IDX_M_S,
|
||||
ARMMMUIdx_MSPriv = ARMMMUIdx_MPriv | ARM_MMU_IDX_M_S,
|
||||
ARMMMUIdx_MSUserNegPri = ARMMMUIdx_MUserNegPri | ARM_MMU_IDX_M_S,
|
||||
ARMMMUIdx_MSPrivNegPri = ARMMMUIdx_MPrivNegPri | ARM_MMU_IDX_M_S,
|
||||
} ARMMMUIdx;
|
||||
|
||||
/*
|
||||
* Bit macros for the core-mmu-index values for each index,
|
||||
* for use when calling tlb_flush_by_mmuidx() and friends.
|
||||
*/
|
||||
#define TO_CORE_BIT(NAME) \
|
||||
ARMMMUIdxBit_##NAME = 1 << (ARMMMUIdx_##NAME & ARM_MMU_IDX_COREIDX_MASK)
|
||||
|
||||
typedef enum ARMMMUIdxBit {
|
||||
TO_CORE_BIT(E10_0),
|
||||
TO_CORE_BIT(E20_0),
|
||||
TO_CORE_BIT(E10_1),
|
||||
TO_CORE_BIT(E10_1_PAN),
|
||||
TO_CORE_BIT(E2),
|
||||
TO_CORE_BIT(E20_2),
|
||||
TO_CORE_BIT(E20_2_PAN),
|
||||
TO_CORE_BIT(E3),
|
||||
TO_CORE_BIT(E30_0),
|
||||
TO_CORE_BIT(E30_3_PAN),
|
||||
TO_CORE_BIT(Stage2),
|
||||
TO_CORE_BIT(Stage2_S),
|
||||
|
||||
TO_CORE_BIT(MUser),
|
||||
TO_CORE_BIT(MPriv),
|
||||
TO_CORE_BIT(MUserNegPri),
|
||||
TO_CORE_BIT(MPrivNegPri),
|
||||
TO_CORE_BIT(MSUser),
|
||||
TO_CORE_BIT(MSPriv),
|
||||
TO_CORE_BIT(MSUserNegPri),
|
||||
TO_CORE_BIT(MSPrivNegPri),
|
||||
} ARMMMUIdxBit;
|
||||
|
||||
#undef TO_CORE_BIT
|
||||
|
||||
#define MMU_USER_IDX 0
|
||||
|
||||
/* Indexes used when registering address spaces with cpu_address_space_init */
|
||||
typedef enum ARMASIdx {
|
||||
ARMASIdx_NS = 0,
|
||||
@@ -2667,6 +2487,9 @@ FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1)
|
||||
FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */
|
||||
FIELD(TBFLAG_A64, NEP, 38, 1) /* FPCR.NEP */
|
||||
FIELD(TBFLAG_A64, ZT0EXC_EL, 39, 2)
|
||||
FIELD(TBFLAG_A64, GCS_EN, 41, 1)
|
||||
FIELD(TBFLAG_A64, GCS_RVCEN, 42, 1)
|
||||
FIELD(TBFLAG_A64, GCSSTR_EL, 43, 2)
|
||||
|
||||
/*
|
||||
* Helpers for using the above. Note that only the A64 accessors use
|
||||
|
||||
@@ -47,6 +47,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
|
||||
case 32:
|
||||
return gdb_get_reg64(mem_buf, env->pc);
|
||||
case 33:
|
||||
/* pstate is now a 64-bit value; can we simply adjust the xml? */
|
||||
return gdb_get_reg32(mem_buf, pstate_read(env));
|
||||
}
|
||||
/* Unknown register. */
|
||||
@@ -75,6 +76,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
return 8;
|
||||
case 33:
|
||||
/* CPSR */
|
||||
/* pstate is now a 64-bit value; can we simply adjust the xml? */
|
||||
pstate_write(env, tmp);
|
||||
return 4;
|
||||
}
|
||||
|
||||
+339
-52
File diff suppressed because it is too large
Load Diff
+25
-126
@@ -34,6 +34,7 @@
|
||||
#include "system/memory.h"
|
||||
#include "syndrome.h"
|
||||
#include "cpu-features.h"
|
||||
#include "mmuidx-internal.h"
|
||||
|
||||
/* register banks for CPU modes */
|
||||
#define BANK_USRSYS 0
|
||||
@@ -250,6 +251,7 @@ FIELD(VSTCR, SA, 30, 1)
|
||||
#define HCRX_MSCEN (1ULL << 11)
|
||||
#define HCRX_TCR2EN (1ULL << 14)
|
||||
#define HCRX_SCTLR2EN (1ULL << 15)
|
||||
#define HCRX_GCSEN (1ULL << 22)
|
||||
|
||||
#define HPFAR_NS (1ULL << 63)
|
||||
|
||||
@@ -304,14 +306,14 @@ FIELD(CNTHCTL, CNTPMASK, 19, 1)
|
||||
* and never returns because we will longjump back up to the CPU main loop.
|
||||
*/
|
||||
G_NORETURN void raise_exception(CPUARMState *env, uint32_t excp,
|
||||
uint32_t syndrome, uint32_t target_el);
|
||||
uint64_t syndrome, uint32_t target_el);
|
||||
|
||||
/*
|
||||
* Similarly, but also use unwinding to restore cpu state.
|
||||
*/
|
||||
G_NORETURN void raise_exception_ra(CPUARMState *env, uint32_t excp,
|
||||
uint32_t syndrome, uint32_t target_el,
|
||||
uintptr_t ra);
|
||||
uint64_t syndrome, uint32_t target_el,
|
||||
uintptr_t ra);
|
||||
|
||||
/*
|
||||
* For AArch64, map a given EL to an index in the banked_spsr array.
|
||||
@@ -752,6 +754,7 @@ struct ARMMMUFaultInfo {
|
||||
bool s1ptw;
|
||||
bool s1ns;
|
||||
bool ea;
|
||||
bool dirtybit; /* FEAT_S1PIE, FEAT_S2PIE */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -983,8 +986,6 @@ static inline ARMMMUIdx core_to_aa64_mmu_idx(int mmu_idx)
|
||||
return mmu_idx | ARM_MMU_IDX_A;
|
||||
}
|
||||
|
||||
int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx);
|
||||
|
||||
/* Return the MMU index for a v7M CPU in the specified security state */
|
||||
ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate);
|
||||
|
||||
@@ -1027,108 +1028,10 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if this address translation regime has two ranges.
|
||||
* Note that this will not return the correct answer for AArch32
|
||||
* Secure PL1&0 (i.e. mmu indexes E3, E30_0, E30_3_PAN), but it is
|
||||
* never called from a context where EL3 can be AArch32. (The
|
||||
* correct return value for ARMMMUIdx_E3 would be different for
|
||||
* that case, so we can't just make the function return the
|
||||
* correct value anyway; we would need an extra "bool e3_is_aarch32"
|
||||
* argument which all the current callsites would pass as 'false'.)
|
||||
*/
|
||||
static inline bool regime_has_2_ranges(ARMMMUIdx mmu_idx)
|
||||
{
|
||||
switch (mmu_idx) {
|
||||
case ARMMMUIdx_Stage1_E0:
|
||||
case ARMMMUIdx_Stage1_E1:
|
||||
case ARMMMUIdx_Stage1_E1_PAN:
|
||||
case ARMMMUIdx_E10_0:
|
||||
case ARMMMUIdx_E10_1:
|
||||
case ARMMMUIdx_E10_1_PAN:
|
||||
case ARMMMUIdx_E20_0:
|
||||
case ARMMMUIdx_E20_2:
|
||||
case ARMMMUIdx_E20_2_PAN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool regime_is_pan(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||
{
|
||||
switch (mmu_idx) {
|
||||
case ARMMMUIdx_Stage1_E1_PAN:
|
||||
case ARMMMUIdx_E10_1_PAN:
|
||||
case ARMMMUIdx_E20_2_PAN:
|
||||
case ARMMMUIdx_E30_3_PAN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool regime_is_stage2(ARMMMUIdx mmu_idx)
|
||||
{
|
||||
return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S;
|
||||
}
|
||||
|
||||
/* Return the exception level which controls this address translation regime */
|
||||
static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||
{
|
||||
switch (mmu_idx) {
|
||||
case ARMMMUIdx_E20_0:
|
||||
case ARMMMUIdx_E20_2:
|
||||
case ARMMMUIdx_E20_2_PAN:
|
||||
case ARMMMUIdx_Stage2:
|
||||
case ARMMMUIdx_Stage2_S:
|
||||
case ARMMMUIdx_E2:
|
||||
return 2;
|
||||
case ARMMMUIdx_E3:
|
||||
case ARMMMUIdx_E30_0:
|
||||
case ARMMMUIdx_E30_3_PAN:
|
||||
return 3;
|
||||
case ARMMMUIdx_E10_0:
|
||||
case ARMMMUIdx_Stage1_E0:
|
||||
case ARMMMUIdx_Stage1_E1:
|
||||
case ARMMMUIdx_Stage1_E1_PAN:
|
||||
case ARMMMUIdx_E10_1:
|
||||
case ARMMMUIdx_E10_1_PAN:
|
||||
case ARMMMUIdx_MPrivNegPri:
|
||||
case ARMMMUIdx_MUserNegPri:
|
||||
case ARMMMUIdx_MPriv:
|
||||
case ARMMMUIdx_MUser:
|
||||
case ARMMMUIdx_MSPrivNegPri:
|
||||
case ARMMMUIdx_MSUserNegPri:
|
||||
case ARMMMUIdx_MSPriv:
|
||||
case ARMMMUIdx_MSUser:
|
||||
return 1;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||
{
|
||||
switch (mmu_idx) {
|
||||
case ARMMMUIdx_E10_0:
|
||||
case ARMMMUIdx_E20_0:
|
||||
case ARMMMUIdx_E30_0:
|
||||
case ARMMMUIdx_Stage1_E0:
|
||||
case ARMMMUIdx_MUser:
|
||||
case ARMMMUIdx_MSUser:
|
||||
case ARMMMUIdx_MUserNegPri:
|
||||
case ARMMMUIdx_MSUserNegPri:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the SCTLR value which controls this address translation regime */
|
||||
static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||
{
|
||||
return env->cp15.sctlr_el[regime_el(env, mmu_idx)];
|
||||
return env->cp15.sctlr_el[regime_el(mmu_idx)];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1160,13 +1063,13 @@ static inline uint64_t regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||
v |= env->cp15.vtcr_el2 & VTCR_SHARED_FIELD_MASK;
|
||||
return v;
|
||||
}
|
||||
return env->cp15.tcr_el[regime_el(env, mmu_idx)];
|
||||
return env->cp15.tcr_el[regime_el(mmu_idx)];
|
||||
}
|
||||
|
||||
/* Return true if the translation regime is using LPAE format page tables */
|
||||
static inline bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||
{
|
||||
int el = regime_el(env, mmu_idx);
|
||||
int el = regime_el(mmu_idx);
|
||||
if (el == 2 || arm_el_is_aa64(env, el)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1378,25 +1281,6 @@ ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx);
|
||||
ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* arm_mmu_idx_is_stage1_of_2:
|
||||
* @mmu_idx: The ARMMMUIdx to test
|
||||
*
|
||||
* Return true if @mmu_idx is a NOTLB mmu_idx that is the
|
||||
* first stage of a two stage regime.
|
||||
*/
|
||||
static inline bool arm_mmu_idx_is_stage1_of_2(ARMMMUIdx mmu_idx)
|
||||
{
|
||||
switch (mmu_idx) {
|
||||
case ARMMMUIdx_Stage1_E0:
|
||||
case ARMMMUIdx_Stage1_E1:
|
||||
case ARMMMUIdx_Stage1_E1_PAN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t aarch32_cpsr_valid_mask(uint64_t features,
|
||||
const ARMISARegisters *id)
|
||||
{
|
||||
@@ -1491,7 +1375,7 @@ static inline int arm_granule_bits(ARMGranuleSize gran)
|
||||
|
||||
/*
|
||||
* Parameters of a given virtual address, as extracted from the
|
||||
* translation control register (TCR) for a given regime.
|
||||
* translation controls for a given regime.
|
||||
*/
|
||||
typedef struct ARMVAParameters {
|
||||
unsigned tsz : 8;
|
||||
@@ -1506,6 +1390,7 @@ typedef struct ARMVAParameters {
|
||||
bool ha : 1;
|
||||
bool hd : 1;
|
||||
ARMGranuleSize gran : 2;
|
||||
bool pie : 1;
|
||||
} ARMVAParameters;
|
||||
|
||||
/**
|
||||
@@ -1576,6 +1461,13 @@ typedef struct ARMCacheAttrs {
|
||||
typedef struct GetPhysAddrResult {
|
||||
CPUTLBEntryFull f;
|
||||
ARMCacheAttrs cacheattrs;
|
||||
/*
|
||||
* For ARMMMUIdx_Stage2*, the protection installed into f.prot
|
||||
* is the result for AccessType_TTW, i.e. the page table walk itself.
|
||||
* The protection installed info s2prot is the one to be merged
|
||||
* with the stage1 protection.
|
||||
*/
|
||||
int s2prot;
|
||||
} GetPhysAddrResult;
|
||||
|
||||
/**
|
||||
@@ -1892,6 +1784,8 @@ void define_tlb_insn_regs(ARMCPU *cpu);
|
||||
void define_at_insn_regs(ARMCPU *cpu);
|
||||
/* Add the cpreg definitions for PM cpregs */
|
||||
void define_pm_cpregs(ARMCPU *cpu);
|
||||
/* Add the cpreg definitions for GCS cpregs */
|
||||
void define_gcs_cpregs(ARMCPU *cpu);
|
||||
|
||||
/* Effective value of MDCR_EL2 */
|
||||
static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env)
|
||||
@@ -2003,8 +1897,13 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env);
|
||||
*/
|
||||
void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask);
|
||||
bool arm_pan_enabled(CPUARMState *env);
|
||||
uint32_t cpsr_read_for_spsr_elx(CPUARMState *env);
|
||||
void cpsr_write_from_spsr_elx(CPUARMState *env, uint32_t val);
|
||||
|
||||
/* Compare uint64_t for qsort and bsearch. */
|
||||
int compare_u64(const void *a, const void *b);
|
||||
|
||||
/* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */
|
||||
#define MECID_WIDTH 16
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user