target-arm queue:

* Clean up handling of bad mode switches writing to CPSR, and implement
    the ARMv8 requirement that they set PSTATE.IL
  * Implement MDCR_EL3.TPM and MDCR_EL2.TPM traps on perf monitor
    register accesses
  * Don't implement stellaris-pl061-only registers on generic-pl061
  * Fix SD card handling for raspi
  * Add missing include files to MAINTAINERS
  * Mark CNTHP_TVAL_EL2 as ARM_CP_NO_RAW
  * Make reserved ranges in ID_AA64* spaces RAZ, not UNDEF
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJW0GzrAAoJEDwlJe0UNgzebMsP/RWlWngvO4lXax+EvXZkIqkL
 Q+V74VaiVNrlWGjbN6PSl13PoihdN4dn3SxFD/1OKEmko4Yr4iQ7i4eVpMq5J2dG
 DSEeSUXcnxyr2Ypz46RbEYiTs2yCNS+KlxgVvTeHlN/KQx2zFQ9kBgXMWm6AdTpw
 kdJqFGxNt9suKsMEJ1jk/FO3myaYqzRLlfhFFSAcuNYZN0aidnoRBzNUWQzWei8c
 GT7sB1VYHNZEty0m1taaB4OjIjVF9RMg5ee6r7QpDYvycMCTrXY6beyEmFPD5XUL
 lLjeyBYmM1EBqGxt+7DMZe5EBTDJ2CICCMy72CspbYxr6kqjLpfNvnRbbrBJ+U7A
 aWoKaEfUhkD7dlYaW1y/hTsRbeswbMChNiwbqYulfivCACWS0XHa35OLbMzxuqOD
 QhGDIpx2Hzwj+5t52MM9kz76oFy6BLKtAs9wdVcjsNNwuxIYCiLaBP+XoUQSJVjZ
 5Cb5YMaxqV9vEiFbbSpRHuEN/IzU27W/HylosXmNOl2N8U/hN0LOgF5smYdoRQZP
 zRYCPGCjsoeOE12mn6610EQ3JykWMr5rDW4KVG+5chsYDojy8DXuQL9N6j+RTcM5
 yIIEkny0/QVyFA4EnMg9g1m7boXOPP0BLO1n0dImk23T85G9zzloLaUPW1Z9DBpM
 n+/kNrH1x9+xOdvcvJe3
 =QMDl
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160226' into staging

target-arm queue:
 * Clean up handling of bad mode switches writing to CPSR, and implement
   the ARMv8 requirement that they set PSTATE.IL
 * Implement MDCR_EL3.TPM and MDCR_EL2.TPM traps on perf monitor
   register accesses
 * Don't implement stellaris-pl061-only registers on generic-pl061
 * Fix SD card handling for raspi
 * Add missing include files to MAINTAINERS
 * Mark CNTHP_TVAL_EL2 as ARM_CP_NO_RAW
 * Make reserved ranges in ID_AA64* spaces RAZ, not UNDEF

# gpg: Signature made Fri 26 Feb 2016 15:19:07 GMT using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"

* remotes/pmaydell/tags/pull-target-arm-20160226:
  target-arm: Make reserved ranges in ID_AA64* spaces RAZ, not UNDEF
  target-arm: Mark CNTHP_TVAL_EL2 as ARM_CP_NO_RAW
  sdhci: add quirk property for card insert interrupt status on Raspberry Pi
  sdhci: Revert "add optional quirk property to disable card insertion/removal interrupts"
  MAINTAINERS: Add some missing ARM related header files
  raspi: fix SD card with recent sdhci changes
  ARM: PL061: Checking register r/w accesses to reserved area
  target-arm: Implement MDCR_EL3.TPM and MDCR_EL2.TPM traps
  target-arm: Fix handling of SDCR for 32-bit code
  target-arm: Make Monitor->NS PL1 mode changes illegal if HCR.TGE is 1
  target-arm: Make mode switches from Hyp via CPS and MRS illegal
  target-arm: In v8, make illegal AArch32 mode changes set PSTATE.IL
  target-arm: Forbid mode switch to Mon from Secure EL1
  target-arm: Add Hyp mode checks to bad_mode_switch()
  target-arm: Add comment about not implementing NSACR.RFR
  target-arm: In cpsr_write() ignore mode switches from User mode
  linux-user: Use restrictive mask when calling cpsr_write()
  target-arm: Raw CPSR writes should skip checks and bank switching
  target-arm: Add write_type argument to cpsr_write()
  target-arm: Give CPSR setting on 32-bit exception return its own helper

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-02-26 16:02:00 +00:00
commit 6e378dd214
20 changed files with 359 additions and 69 deletions

View File

@ -363,6 +363,7 @@ M: Dmitry Solodkiy <d.solodkiy@samsung.com>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/*/exynos*
F: include/hw/arm/exynos4210.h
Calxeda Highbank
M: Rob Herring <robh@kernel.org>
@ -390,6 +391,7 @@ L: qemu-arm@nongnu.org
S: Odd fixes
F: hw/*/imx*
F: hw/arm/kzm.c
F: include/hw/arm/fsl-imx31.h
Integrator CP
M: Peter Maydell <peter.maydell@linaro.org>
@ -432,6 +434,7 @@ F: hw/arm/spitz.c
F: hw/arm/tosa.c
F: hw/arm/z2.c
F: hw/*/pxa2xx*
F: include/hw/arm/pxa.h
Stellaris
M: Peter Maydell <peter.maydell@linaro.org>
@ -768,6 +771,7 @@ OMAP
M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: hw/*/omap*
F: include/hw/arm/omap.h
IPack
M: Alberto Garcia <berto@igalia.com>

View File

@ -182,6 +182,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_ARASANSDIO));
object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->sdhci), "sd-bus",
&err);
if (err) {
error_propagate(errp, err);
return;
}
}
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)

View File

@ -73,6 +73,13 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
return;
}
object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals),
"sd-bus", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
BCM2836_PERI_BASE, 1);

View File

@ -113,6 +113,10 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
static void raspi2_init(MachineState *machine)
{
RasPiState *s = g_new0(RasPiState, 1);
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
DeviceState *carddev;
object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
@ -133,6 +137,18 @@ static void raspi2_init(MachineState *machine)
&error_abort);
object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort);
/* Create and plug in the SD cards */
di = drive_get_next(IF_SD);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus");
if (bus == NULL) {
error_report("No SD bus found in SOC object");
exit(1);
}
carddev = qdev_create(bus, TYPE_SD_CARD);
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
setup_boot(machine, 2, machine->ram_size);
}

View File

@ -60,6 +60,7 @@ typedef struct PL061State {
qemu_irq irq;
qemu_irq out[8];
const unsigned char *id;
uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */
} PL061State;
static const VMStateDescription vmstate_pl061 = {
@ -152,12 +153,15 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
{
PL061State *s = (PL061State *)opaque;
if (offset >= 0xfd0 && offset < 0x1000) {
return s->id[(offset - 0xfd0) >> 2];
}
if (offset < 0x400) {
return s->data & (offset >> 2);
}
if (offset >= s->rsvd_start && offset <= 0xfcc) {
goto err_out;
}
if (offset >= 0xfd0 && offset < 0x1000) {
return s->id[(offset - 0xfd0) >> 2];
}
switch (offset) {
case 0x400: /* Direction */
return s->dir;
@ -198,10 +202,12 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
case 0x528: /* Analog mode select */
return s->amsel;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl061_read: Bad offset %x\n", (int)offset);
return 0;
break;
}
err_out:
qemu_log_mask(LOG_GUEST_ERROR,
"pl061_read: Bad offset %x\n", (int)offset);
return 0;
}
static void pl061_write(void *opaque, hwaddr offset,
@ -216,6 +222,9 @@ static void pl061_write(void *opaque, hwaddr offset,
pl061_update(s);
return;
}
if (offset >= s->rsvd_start) {
goto err_out;
}
switch (offset) {
case 0x400: /* Direction */
s->dir = value & 0xff;
@ -274,10 +283,13 @@ static void pl061_write(void *opaque, hwaddr offset,
s->amsel = value & 0xff;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl061_write: Bad offset %x\n", (int)offset);
goto err_out;
}
pl061_update(s);
return;
err_out:
qemu_log_mask(LOG_GUEST_ERROR,
"pl061_write: Bad offset %x\n", (int)offset);
}
static void pl061_reset(DeviceState *dev)
@ -347,6 +359,7 @@ static void pl061_luminary_init(Object *obj)
PL061State *s = PL061(obj);
s->id = pl061_id_luminary;
s->rsvd_start = 0x52c;
}
static void pl061_init(Object *obj)
@ -354,6 +367,7 @@ static void pl061_init(Object *obj)
PL061State *s = PL061(obj);
s->id = pl061_id;
s->rsvd_start = 0x424;
}
static void pl061_class_init(ObjectClass *klass, void *data)

View File

@ -198,14 +198,13 @@ static void sdhci_reset(SDHCIState *s)
* initialization */
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
if (!s->noeject_quirk) {
/* Reset other state based on current card insertion/readonly status */
sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
}
/* Reset other state based on current card insertion/readonly status */
sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
s->data_count = 0;
s->stopped_state = sdhc_not_stopped;
s->pending_insert_state = false;
}
static void sdhci_data_transfer(void *opaque);
@ -1097,6 +1096,13 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
} else {
s->norintsts &= ~SDHC_NIS_ERR;
}
/* Quirk for Raspberry Pi: pending card insert interrupt
* appears when first enabled after power on */
if ((s->norintstsen & SDHC_NISEN_INSERT) && s->pending_insert_state) {
assert(s->pending_insert_quirk);
s->norintsts |= SDHC_NIS_INSERT;
s->pending_insert_state = false;
}
sdhci_update_irq(s);
break;
case SDHC_NORINTSIGEN:
@ -1183,6 +1189,24 @@ static void sdhci_uninitfn(SDHCIState *s)
s->fifo_buffer = NULL;
}
static bool sdhci_pending_insert_vmstate_needed(void *opaque)
{
SDHCIState *s = opaque;
return s->pending_insert_state;
}
static const VMStateDescription sdhci_pending_insert_vmstate = {
.name = "sdhci/pending-insert",
.version_id = 1,
.minimum_version_id = 1,
.needed = sdhci_pending_insert_vmstate_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(pending_insert_state, SDHCIState),
VMSTATE_END_OF_LIST()
},
};
const VMStateDescription sdhci_vmstate = {
.name = "sdhci",
.version_id = 1,
@ -1217,7 +1241,11 @@ const VMStateDescription sdhci_vmstate = {
VMSTATE_TIMER_PTR(insert_timer, SDHCIState),
VMSTATE_TIMER_PTR(transfer_timer, SDHCIState),
VMSTATE_END_OF_LIST()
}
},
.subsections = (const VMStateDescription*[]) {
&sdhci_pending_insert_vmstate,
NULL
},
};
/* Capabilities registers provide information on supported features of this
@ -1275,7 +1303,8 @@ static Property sdhci_sysbus_properties[] = {
DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
SDHC_CAPAB_REG_DEFAULT),
DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
DEFINE_PROP_BOOL("noeject-quirk", SDHCIState, noeject_quirk, false),
DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk,
false),
DEFINE_PROP_END_OF_LIST(),
};
@ -1303,6 +1332,10 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
SDHC_REGISTERS_MAP_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
if (s->pending_insert_quirk) {
s->pending_insert_state = true;
}
}
static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)

View File

@ -76,7 +76,8 @@ typedef struct SDHCIState {
uint32_t buf_maxsz;
uint16_t data_count; /* current element in FIFO buffer */
uint8_t stopped_state;/* Current SDHC state */
bool noeject_quirk;/* Quirk to disable card insert/remove interrupts */
bool pending_insert_quirk;/* Quirk for Raspberry Pi card insert int */
bool pending_insert_state;
/* Buffer Data Port Register - virtual access point to R and W buffers */
/* Software Reset Register - always reads as 0 */
/* Force Event Auto CMD12 Error Interrupt Reg - write only */

View File

@ -105,7 +105,7 @@ static inline void writeRegister(unsigned int x, unsigned int y)
static inline void writeConditionCodes(unsigned int x)
{
cpsr_write(user_registers,x,CPSR_NZCV);
cpsr_write(user_registers, x, CPSR_NZCV, CPSRWriteByInstr);
}
#define ARM_REG_PC 15

View File

@ -513,7 +513,7 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env)
env->regs[0] = -1;
cpsr &= ~CPSR_C;
}
cpsr_write(env, cpsr, CPSR_C);
cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr);
end_exclusive();
return;
@ -562,7 +562,7 @@ do_kernel_trap(CPUARMState *env)
env->regs[0] = -1;
cpsr &= ~CPSR_C;
}
cpsr_write(env, cpsr, CPSR_C);
cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr);
end_exclusive();
break;
case 0xffff0fe0: /* __kernel_get_tls */
@ -4446,7 +4446,8 @@ int main(int argc, char **argv, char **envp)
#elif defined(TARGET_ARM)
{
int i;
cpsr_write(env, regs->uregs[16], 0xffffffff);
cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC,
CPSRWriteByInstr);
for(i = 0; i < 16; i++) {
env->regs[i] = regs->uregs[i];
}

View File

@ -1611,7 +1611,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka,
env->regs[13] = frame_addr;
env->regs[14] = retcode;
env->regs[15] = handler & (thumb ? ~1 : ~3);
cpsr_write(env, cpsr, 0xffffffff);
cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr);
}
static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env)
@ -1843,7 +1843,7 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc)
__get_user(env->regs[15], &sc->arm_pc);
#ifdef TARGET_CONFIG_CPU_32
__get_user(cpsr, &sc->arm_cpsr);
cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC);
cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr);
#endif
err |= !valid_user_regs(env);

View File

@ -155,6 +155,7 @@ typedef struct ARMCPU {
uint32_t id_mmfr1;
uint32_t id_mmfr2;
uint32_t id_mmfr3;
uint32_t id_mmfr4;
uint32_t id_isar0;
uint32_t id_isar1;
uint32_t id_isar2;

View File

@ -598,6 +598,7 @@ void pmccntr_sync(CPUARMState *env);
#define MDCR_EDAD (1U << 20)
#define MDCR_SPME (1U << 17)
#define MDCR_SDD (1U << 16)
#define MDCR_SPD (3U << 14)
#define MDCR_TDRA (1U << 11)
#define MDCR_TDOSA (1U << 10)
#define MDCR_TDA (1U << 9)
@ -606,6 +607,9 @@ void pmccntr_sync(CPUARMState *env);
#define MDCR_TPM (1U << 6)
#define MDCR_TPMCR (1U << 5)
/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */
#define SDCR_VALID_MASK (MDCR_EPMAD | MDCR_EDAD | MDCR_SPME | MDCR_SPD)
#define CPSR_M (0x1fU)
#define CPSR_T (1U << 5)
#define CPSR_F (1U << 6)
@ -718,8 +722,17 @@ static inline void pstate_write(CPUARMState *env, uint32_t val)
/* Return the current CPSR value. */
uint32_t cpsr_read(CPUARMState *env);
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask);
typedef enum CPSRWriteType {
CPSRWriteByInstr = 0, /* from guest MSR or CPS */
CPSRWriteExceptionReturn = 1, /* from guest exception return insn */
CPSRWriteRaw = 2, /* trust values, do not switch reg banks */
CPSRWriteByGDBStub = 3, /* from the GDB stub */
} CPSRWriteType;
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear.*/
void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask,
CPSRWriteType write_type);
/* Return the current xPSR value. */
static inline uint32_t xpsr_read(CPUARMState *env)

View File

@ -94,7 +94,7 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
return 4;
case 25:
/* CPSR */
cpsr_write(env, tmp, 0xffffffff);
cpsr_write(env, tmp, 0xffffffff, CPSRWriteByGDBStub);
return 4;
}
/* Unknown register. */

View File

@ -439,6 +439,24 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
/* Check for traps to performance monitor registers, which are controlled
* by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3.
*/
static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
int el = arm_current_el(env);
if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TPM)
&& !arm_is_secure_below_el3(env)) {
return CP_ACCESS_TRAP_EL2;
}
if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
}
static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
{
ARMCPU *cpu = arm_env_get_cpu(env);
@ -774,11 +792,22 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
/* Performance monitor registers user accessibility is controlled
* by PMUSERENR.
* by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable
* trapping to EL2 or EL3 for other accesses.
*/
if (arm_current_el(env) == 0 && !env->cp15.c9_pmuserenr) {
int el = arm_current_el(env);
if (el == 0 && !env->cp15.c9_pmuserenr) {
return CP_ACCESS_TRAP;
}
if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TPM)
&& !arm_is_secure_below_el3(env)) {
return CP_ACCESS_TRAP_EL2;
}
if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
}
@ -1101,28 +1130,28 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
.accessfn = pmreg_access },
{ .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
.access = PL0_R | PL1_RW,
.access = PL0_R | PL1_RW, .accessfn = access_tpm,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
.resetvalue = 0,
.writefn = pmuserenr_write, .raw_writefn = raw_write },
{ .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0,
.access = PL0_R | PL1_RW, .type = ARM_CP_ALIAS,
.access = PL0_R | PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
.resetvalue = 0,
.writefn = pmuserenr_write, .raw_writefn = raw_write },
{ .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1,
.access = PL1_RW,
.access = PL1_RW, .accessfn = access_tpm,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.resetvalue = 0,
.writefn = pmintenset_write, .raw_writefn = raw_write },
{ .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_ALIAS,
.access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.writefn = pmintenclr_write, },
{ .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_ALIAS,
.access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.writefn = pmintenclr_write },
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH,
@ -3037,6 +3066,12 @@ static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
static void sdcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
env->cp15.mdcr_el3 = value & SDCR_VALID_MASK;
}
static const ARMCPRegInfo v8_cp_reginfo[] = {
/* Minimal set of EL0-visible registers. This will need to be expanded
* significantly for system emulation of AArch64 CPUs.
@ -3331,6 +3366,15 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 3,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) },
{ .name = "MDCR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 1, .crm = 3, .opc2 = 1,
.resetvalue = 0,
.access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el3) },
{ .name = "SDCR", .type = ARM_CP_ALIAS,
.cp = 15, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 1,
.access = PL1_RW, .accessfn = access_trap_aa32s_el1,
.writefn = sdcr_write,
.fieldoffset = offsetoflow32(CPUARMState, cp15.mdcr_el3) },
REGINFO_SENTINEL
};
@ -3628,7 +3672,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.writefn = gt_hyp_cval_write, .raw_writefn = raw_write },
{ .name = "CNTHP_TVAL_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 2, .opc2 = 0,
.type = ARM_CP_IO, .access = PL2_RW,
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW,
.resetfn = gt_hyp_timer_reset,
.readfn = gt_hyp_tval_read, .writefn = gt_hyp_tval_write },
{ .name = "CNTHP_CTL_EL2", .state = ARM_CP_STATE_BOTH,
@ -3688,14 +3732,6 @@ static const ARMCPRegInfo el3_cp_reginfo[] = {
.access = PL1_RW, .accessfn = access_trap_aa32s_el1,
.fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3),
.writefn = scr_write },
{ .name = "MDCR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 1, .crm = 3, .opc2 = 1,
.resetvalue = 0,
.access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el3) },
{ .name = "SDCR", .type = ARM_CP_ALIAS,
.cp = 15, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 1,
.access = PL1_RW, .accessfn = access_trap_aa32s_el1,
.fieldoffset = offsetoflow32(CPUARMState, cp15.mdcr_el3) },
{ .name = "SDER32_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1,
.access = PL3_RW, .resetvalue = 0,
@ -4280,12 +4316,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_isar5 },
/* 6..7 are as yet unallocated and must RAZ */
{ .name = "ID_ISAR6", .cp = 15, .crn = 0, .crm = 2,
.opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_ISAR7", .cp = 15, .crn = 0, .crm = 2,
.opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST,
{ .name = "ID_MMFR4", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_mmfr4 },
/* 7 is as yet unallocated and must RAZ */
{ .name = "ID_ISAR7_RESERVED", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
REGINFO_SENTINEL
};
@ -4339,7 +4377,11 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, not_v7_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_V8)) {
/* AArch64 ID registers, which all have impdef reset values */
/* AArch64 ID registers, which all have impdef reset values.
* Note that within the ID register ranges the unused slots
* must all RAZ, not UNDEF; future architecture versions may
* define new registers here.
*/
ARMCPRegInfo v8_idregs[] = {
{ .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0,
@ -4349,6 +4391,30 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_aa64pfr1},
{ .name = "ID_AA64PFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64PFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64PFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64PFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64PFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64PFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
@ -4362,6 +4428,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_aa64dfr1 },
{ .name = "ID_AA64DFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64DFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
@ -4370,6 +4444,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_aa64afr1 },
{ .name = "ID_AA64AFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64AFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
@ -4378,6 +4460,30 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_aa64isar1 },
{ .name = "ID_AA64ISAR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64ISAR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
@ -4386,6 +4492,30 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->id_aa64mmfr1 },
{ .name = "ID_AA64MMFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "ID_AA64MMFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "MVFR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST,
@ -4398,6 +4528,26 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->mvfr2 },
{ .name = "MVFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "MVFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "MVFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "MVFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 6,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "MVFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 7,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "PMCEID0", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6,
.access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
@ -5200,23 +5350,47 @@ void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque)
/* Helper coprocessor reset function for do-nothing-on-reset registers */
}
static int bad_mode_switch(CPUARMState *env, int mode)
static int bad_mode_switch(CPUARMState *env, int mode, CPSRWriteType write_type)
{
/* Return true if it is not valid for us to switch to
* this CPU mode (ie all the UNPREDICTABLE cases in
* the ARM ARM CPSRWriteByInstr pseudocode).
*/
/* Changes to or from Hyp via MSR and CPS are illegal. */
if (write_type == CPSRWriteByInstr &&
((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_HYP ||
mode == ARM_CPU_MODE_HYP)) {
return 1;
}
switch (mode) {
case ARM_CPU_MODE_USR:
return 0;
case ARM_CPU_MODE_SYS:
case ARM_CPU_MODE_SVC:
case ARM_CPU_MODE_ABT:
case ARM_CPU_MODE_UND:
case ARM_CPU_MODE_IRQ:
case ARM_CPU_MODE_FIQ:
/* Note that we don't implement the IMPDEF NSACR.RFR which in v7
* allows FIQ mode to be Secure-only. (In v8 this doesn't exist.)
*/
/* If HCR.TGE is set then changes from Monitor to NS PL1 via MSR
* and CPS are treated as illegal mode changes.
*/
if (write_type == CPSRWriteByInstr &&
(env->cp15.hcr_el2 & HCR_TGE) &&
(env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON &&
!arm_is_secure_below_el3(env)) {
return 1;
}
return 0;
case ARM_CPU_MODE_HYP:
return !arm_feature(env, ARM_FEATURE_EL2)
|| arm_current_el(env) < 2 || arm_is_secure(env);
case ARM_CPU_MODE_MON:
return !arm_is_secure(env);
return arm_current_el(env) < 3;
default:
return 1;
}
@ -5233,7 +5407,8 @@ uint32_t cpsr_read(CPUARMState *env)
| (env->GE << 16) | (env->daif & CPSR_AIF);
}
void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask,
CPSRWriteType write_type)
{
uint32_t changed_daif;
@ -5267,7 +5442,7 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
* In a V8 implementation, it is permitted for privileged software to
* change the CPSR A/F bits regardless of the SCR.AW/FW bits.
*/
if (!arm_feature(env, ARM_FEATURE_V8) &&
if (write_type != CPSRWriteRaw && !arm_feature(env, ARM_FEATURE_V8) &&
arm_feature(env, ARM_FEATURE_EL3) &&
!arm_feature(env, ARM_FEATURE_EL2) &&
!arm_is_secure(env)) {
@ -5314,13 +5489,24 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
env->daif &= ~(CPSR_AIF & mask);
env->daif |= val & CPSR_AIF & mask;
if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
if (bad_mode_switch(env, val & CPSR_M)) {
/* Attempt to switch to an invalid mode: this is UNPREDICTABLE.
* We choose to ignore the attempt and leave the CPSR M field
* untouched.
if (write_type != CPSRWriteRaw &&
(env->uncached_cpsr & CPSR_M) != CPSR_USER &&
((env->uncached_cpsr ^ val) & mask & CPSR_M)) {
if (bad_mode_switch(env, val & CPSR_M, write_type)) {
/* Attempt to switch to an invalid mode: this is UNPREDICTABLE in
* v7, and has defined behaviour in v8:
* + leave CPSR.M untouched
* + allow changes to the other CPSR fields
* + set PSTATE.IL
* For user changes via the GDB stub, we don't set PSTATE.IL,
* as this would be unnecessarily harsh for a user error.
*/
mask &= ~CPSR_M;
if (write_type != CPSRWriteByGDBStub &&
arm_feature(env, ARM_FEATURE_V8)) {
mask |= CPSR_IL;
val |= CPSR_IL;
}
} else {
switch_mode(env, val & CPSR_M);
}

View File

@ -57,6 +57,7 @@ DEF_HELPER_2(pre_smc, void, env, i32)
DEF_HELPER_1(check_breakpoints, void, env)
DEF_HELPER_3(cpsr_write, void, env, i32, i32)
DEF_HELPER_2(cpsr_write_eret, void, env, i32)
DEF_HELPER_1(cpsr_read, i32, env)
DEF_HELPER_3(v7m_msr, void, env, i32, i32)

View File

@ -428,7 +428,7 @@ int kvm_arch_get_registers(CPUState *cs)
if (ret) {
return ret;
}
cpsr_write(env, cpsr, 0xffffffff);
cpsr_write(env, cpsr, 0xffffffff, CPSRWriteRaw);
/* Make sure the current mode regs are properly set */
mode = env->uncached_cpsr & CPSR_M;

View File

@ -722,8 +722,7 @@ int kvm_arch_get_registers(CPUState *cs)
if (is_a64(env)) {
pstate_write(env, val);
} else {
env->uncached_cpsr = val & CPSR_M;
cpsr_write(env, val, 0xffffffff);
cpsr_write(env, val, 0xffffffff, CPSRWriteRaw);
}
/* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the

View File

@ -173,9 +173,7 @@ static int get_cpsr(QEMUFile *f, void *opaque, size_t size)
return 0;
}
/* Avoid mode switch when restoring CPSR */
env->uncached_cpsr = val & CPSR_M;
cpsr_write(env, val, 0xffffffff);
cpsr_write(env, val, 0xffffffff, CPSRWriteRaw);
return 0;
}

View File

@ -422,7 +422,13 @@ uint32_t HELPER(cpsr_read)(CPUARMState *env)
void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask)
{
cpsr_write(env, val, mask);
cpsr_write(env, val, mask, CPSRWriteByInstr);
}
/* Write the CPSR for a 32-bit exception return */
void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
{
cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn);
}
/* Access to user mode registers from privileged modes. */
@ -773,8 +779,11 @@ void HELPER(exception_return)(CPUARMState *env)
if (!return_to_aa64) {
env->aarch64 = 0;
env->uncached_cpsr = spsr & CPSR_M;
cpsr_write(env, spsr, ~0);
/* We do a raw CPSR write because aarch64_sync_64_to_32()
* will sort the register banks out for us, and we've already
* caught all the bad-mode cases in el_from_spsr().
*/
cpsr_write(env, spsr, ~0, CPSRWriteRaw);
if (!arm_singlestep_active(env)) {
env->uncached_cpsr &= ~PSTATE_SS;
}

View File

@ -4094,7 +4094,7 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
TCGv_i32 tmp;
store_reg(s, 15, pc);
tmp = load_cpu_field(spsr);
gen_set_cpsr(tmp, CPSR_ERET_MASK);
gen_helper_cpsr_write_eret(cpu_env, tmp);
tcg_temp_free_i32(tmp);
s->is_jmp = DISAS_JUMP;
}
@ -4102,7 +4102,7 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
/* Generate a v6 exception return. Marks both values as dead. */
static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr)
{
gen_set_cpsr(cpsr, CPSR_ERET_MASK);
gen_helper_cpsr_write_eret(cpu_env, cpsr);
tcg_temp_free_i32(cpsr);
store_reg(s, 15, pc);
s->is_jmp = DISAS_JUMP;
@ -9094,7 +9094,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
if (exc_return) {
/* Restore CPSR from SPSR. */
tmp = load_cpu_field(spsr);
gen_set_cpsr(tmp, CPSR_ERET_MASK);
gen_helper_cpsr_write_eret(cpu_env, tmp);
tcg_temp_free_i32(tmp);
s->is_jmp = DISAS_JUMP;
}