From ac2810defa9d4ba856adbaa9c899defecd8585de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 27 Jan 2017 15:20:20 +0000 Subject: [PATCH 01/22] aspeed/smc: handle dummy bytes when doing fast reads in command mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When doing fast read, a certain amount of dummy bytes should be sent before the read. This number is configurable in the controler CE0 Control Register and needs to be modeled using fake transfers to the flash module. This only supports command mode. User mode requires more work and a possible extension of the m25p80 device model. Signed-off-by: Cédric Le Goater Acked-by: Marcin Krzemiński Message-id: 1484751701-2646-1-git-send-email-clg@kaod.org Signed-off-by: Peter Maydell --- hw/ssi/aspeed_smc.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index ae1ad2dba6..087b29e8da 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -69,7 +69,9 @@ #define R_CTRL0 (0x10 / 4) #define CTRL_CMD_SHIFT 16 #define CTRL_CMD_MASK 0xff +#define CTRL_DUMMY_HIGH_SHIFT 14 #define CTRL_AST2400_SPI_4BYTE (1 << 13) +#define CTRL_DUMMY_LOW_SHIFT 6 /* 2 bits [7:6] */ #define CTRL_CE_STOP_ACTIVE (1 << 2) #define CTRL_CMD_MODE_MASK 0x3 #define CTRL_READMODE 0x0 @@ -485,6 +487,16 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, return addr; } +static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl) +{ + const AspeedSMCState *s = fl->controller; + uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->id]; + uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1; + uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3; + + return ((dummy_high << 2) | dummy_low) * 8; +} + static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) { const AspeedSMCState *s = fl->controller; @@ -521,6 +533,15 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) aspeed_smc_flash_select(fl); aspeed_smc_flash_send_addr(fl, addr); + /* + * Use fake transfers to model dummy bytes. The value should + * be configured to some non-zero value in fast read mode and + * zero in read mode. + */ + for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { + ssi_transfer(fl->controller->spi, 0xFF); + } + for (i = 0; i < size; i++) { ret |= ssi_transfer(s->spi, 0x0) << (8 * i); } From 58117c9bb429cd9552d998687aa99088eb1d8528 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:20 +0000 Subject: [PATCH 02/22] armv7m: MRS/MSR: handle unprivileged access The MRS and MSR instruction handling has a number of flaws: * unprivileged accesses should only be able to read CONTROL and the xPSR subfields, and only write APSR (others RAZ/WI) * privileged access should not be able to write xPSR subfields other than APSR * accesses to unimplemented registers should log as guest errors, not abort QEMU Signed-off-by: Michael Davidsaver Reviewed-by: Peter Maydell Message-id: 1484937883-1068-2-git-send-email-peter.maydell@linaro.org [PMM: rewrote commit message] Signed-off-by: Peter Maydell --- target/arm/helper.c | 79 +++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7111c8cf18..ad23de3e90 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8243,23 +8243,32 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) { - ARMCPU *cpu = arm_env_get_cpu(env); + uint32_t mask; + unsigned el = arm_current_el(env); + + /* First handle registers which unprivileged can read */ + + switch (reg) { + case 0 ... 7: /* xPSR sub-fields */ + mask = 0; + if ((reg & 1) && el) { + mask |= 0x000001ff; /* IPSR (unpriv. reads as zero) */ + } + if (!(reg & 4)) { + mask |= 0xf8000000; /* APSR */ + } + /* EPSR reads as zero */ + return xpsr_read(env) & mask; + break; + case 20: /* CONTROL */ + return env->v7m.control; + } + + if (el == 0) { + return 0; /* unprivileged reads others as zero */ + } switch (reg) { - case 0: /* APSR */ - return xpsr_read(env) & 0xf8000000; - case 1: /* IAPSR */ - return xpsr_read(env) & 0xf80001ff; - case 2: /* EAPSR */ - return xpsr_read(env) & 0xff00fc00; - case 3: /* xPSR */ - return xpsr_read(env) & 0xff00fdff; - case 5: /* IPSR */ - return xpsr_read(env) & 0x000001ff; - case 6: /* EPSR */ - return xpsr_read(env) & 0x0700fc00; - case 7: /* IEPSR */ - return xpsr_read(env) & 0x0700edff; case 8: /* MSP */ return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13]; case 9: /* PSP */ @@ -8271,40 +8280,26 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) return env->v7m.basepri; case 19: /* FAULTMASK */ return (env->daif & PSTATE_F) != 0; - case 20: /* CONTROL */ - return env->v7m.control; default: - /* ??? For debugging only. */ - cpu_abort(CPU(cpu), "Unimplemented system register read (%d)\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special" + " register %d\n", reg); return 0; } } void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) { - ARMCPU *cpu = arm_env_get_cpu(env); + if (arm_current_el(env) == 0 && reg > 7) { + /* only xPSR sub-fields may be written by unprivileged */ + return; + } switch (reg) { - case 0: /* APSR */ - xpsr_write(env, val, 0xf8000000); - break; - case 1: /* IAPSR */ - xpsr_write(env, val, 0xf8000000); - break; - case 2: /* EAPSR */ - xpsr_write(env, val, 0xfe00fc00); - break; - case 3: /* xPSR */ - xpsr_write(env, val, 0xfe00fc00); - break; - case 5: /* IPSR */ - /* IPSR bits are readonly. */ - break; - case 6: /* EPSR */ - xpsr_write(env, val, 0x0600fc00); - break; - case 7: /* IEPSR */ - xpsr_write(env, val, 0x0600fc00); + case 0 ... 7: /* xPSR sub-fields */ + /* only APSR is actually writable */ + if (reg & 4) { + xpsr_write(env, val, 0xf8000000); /* APSR */ + } break; case 8: /* MSP */ if (env->v7m.current_sp) @@ -8345,8 +8340,8 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) switch_v7m_sp(env, (val & 2) != 0); break; default: - /* ??? For debugging only. */ - cpu_abort(CPU(cpu), "Unimplemented system register write (%d)\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" + " register %d\n", reg); return; } } From 542b3478a00cb7ef51c259255b3ab1e2a7daada2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:21 +0000 Subject: [PATCH 03/22] armv7m: Replace armv7m.hack with unassigned_access handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For v7m we need to catch attempts to execute from special addresses at 0xfffffff0 and above. Previously we did this with the aid of a hacky special purpose lump of memory in the address space and a check in translate.c for whether we were translating code at those addresses. We can implement this more cleanly using a CPU unassigned access handler which throws the exception if the unassigned access is for one of the special addresses. Signed-off-by: Michael Davidsaver Reviewed-by: Alex Bennée Message-id: 1484937883-1068-3-git-send-email-peter.maydell@linaro.org [PMM: * drop the deletion of the "don't interrupt if PC is magic" code in arm_v7m_cpu_exec_interrupt() -- this is still required * don't generate an exception for unassigned accesses which aren't to the magic address -- although doing this is in theory correct in practice it will break currently working guests which rely on the RAZ/WI behaviour when they touch devices which we haven't modelled. * trigger EXCP_EXCEPTION_EXIT on is_exec, not !is_write ] Signed-off-by: Peter Maydell --- hw/arm/armv7m.c | 8 -------- target/arm/cpu.c | 28 ++++++++++++++++++++++++++++ target/arm/translate.c | 12 ++++++------ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 49d30782c8..0c9ca7bfa0 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -180,7 +180,6 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, uint64_t entry; uint64_t lowaddr; int big_endian; - MemoryRegion *hack = g_new(MemoryRegion, 1); if (cpu_model == NULL) { cpu_model = "cortex-m3"; @@ -225,13 +224,6 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, } } - /* Hack to map an additional page of ram at the top of the address - space. This stops qemu complaining about executing code outside RAM - when returning from an exception. */ - memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal); - vmstate_register_ram_global(hack); - memory_region_add_subregion(system_memory, 0xfffff000, hack); - qemu_register_reset(armv7m_reset, cpu); return nvic; } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a941f6611b..907598968c 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -292,6 +292,33 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) +static void arm_v7m_unassigned_access(CPUState *cpu, hwaddr addr, + bool is_write, bool is_exec, int opaque, + unsigned size) +{ + ARMCPU *arm = ARM_CPU(cpu); + CPUARMState *env = &arm->env; + + /* ARMv7-M interrupt return works by loading a magic value into the PC. + * On real hardware the load causes the return to occur. The qemu + * implementation performs the jump normally, then does the exception + * return by throwing a special exception when when the CPU tries to + * execute code at the magic address. + */ + if (env->v7m.exception != 0 && addr >= 0xfffffff0 && is_exec) { + cpu->exception_index = EXCP_EXCEPTION_EXIT; + cpu_loop_exit(cpu); + } + + /* In real hardware an attempt to access parts of the address space + * with nothing there will usually cause an external abort. + * However our QEMU board models are often missing device models where + * the guest can boot anyway with the default read-as-zero/writes-ignored + * behaviour that you get without a QEMU unassigned_access hook. + * So just return here to retain that default behaviour. + */ +} + static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); @@ -1016,6 +1043,7 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data) cc->do_interrupt = arm_v7m_cpu_do_interrupt; #endif + cc->do_unassigned_access = arm_v7m_unassigned_access; cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt; } diff --git a/target/arm/translate.c b/target/arm/translate.c index c9186b6195..a7c2abeffe 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -11719,12 +11719,12 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) break; } #else - if (dc->pc >= 0xfffffff0 && arm_dc_feature(dc, ARM_FEATURE_M)) { - /* We always get here via a jump, so know we are not in a - conditional execution block. */ - gen_exception_internal(EXCP_EXCEPTION_EXIT); - dc->is_jmp = DISAS_EXC; - break; + if (arm_dc_feature(dc, ARM_FEATURE_M)) { + /* Branches to the magic exception-return addresses should + * already have been caught via the arm_v7m_unassigned_access hook, + * and never get here. + */ + assert(dc->pc < 0xfffffff0); } #endif From 1b9ea408fca1ce8caae67b792355b023c69c5ac5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:21 +0000 Subject: [PATCH 04/22] armv7m: Explicit error for bad vector table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give an explicit error and abort when a load from the vector table fails. Architecturally this should HardFault (which will then immediately fail to load the HardFault vector and go into Lockup). Since we don't model Lockup, just report this guest error via cpu_abort(). This is more helpful than the previous behaviour of reading a zero, which is the address of the reset stack pointer and not a sensible location to jump to. Signed-off-by: Michael Davidsaver Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1484937883-1068-4-git-send-email-peter.maydell@linaro.org [PMM: expanded commit message] Signed-off-by: Peter Maydell --- target/arm/helper.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index ad23de3e90..8edb08cbc1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6014,6 +6014,30 @@ static void arm_log_exception(int idx) } } +static uint32_t arm_v7m_load_vector(ARMCPU *cpu) + +{ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + MemTxResult result; + hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4; + uint32_t addr; + + addr = address_space_ldl(cs->as, vec, + MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + /* Architecturally this should cause a HardFault setting HSFR.VECTTBL, + * which would then be immediately followed by our failing to load + * the entry vector for that HardFault, which is a Lockup case. + * Since we don't model Lockup, we just report this guest error + * via cpu_abort(). + */ + cpu_abort(cs, "Failed to read from exception vector table " + "entry %08x\n", (unsigned)vec); + } + return addr; +} + void arm_v7m_cpu_do_interrupt(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -6095,7 +6119,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) /* Clear IT bits */ env->condexec_bits = 0; env->regs[14] = lr; - addr = ldl_phys(cs->as, env->v7m.vecbase + env->v7m.exception * 4); + addr = arm_v7m_load_vector(cpu); env->regs[15] = addr & 0xfffffffe; env->thumb = addr & 1; } From afb3141c660f3dca38227901c5c62cef7af86647 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:21 +0000 Subject: [PATCH 05/22] hw/registerfields.h: Pull FIELD etc macros out of hw/register.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw/register.h provides macros like FIELD which make it easy to define shift, mask and length constants for the fields within a register. Unfortunately register.h also includes a lot of other things, some of which will only compile in the softmmu build. Pull the FIELD macro and friends out into a separate header file, so they can be used in places like target/arm files which also get built in the user-only configs. Signed-off-by: Peter Maydell Reviewed-by: Alistair Francis Reviewed-by: Alex Bennée Message-id: 1484937883-1068-5-git-send-email-peter.maydell@linaro.org --- include/hw/register.h | 47 +---------------------------- include/hw/registerfields.h | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 46 deletions(-) create mode 100644 include/hw/registerfields.h diff --git a/include/hw/register.h b/include/hw/register.h index 5b6dc32091..de2414e6b4 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -13,6 +13,7 @@ #include "hw/qdev-core.h" #include "exec/memory.h" +#include "hw/registerfields.h" typedef struct RegisterInfo RegisterInfo; typedef struct RegisterAccessInfo RegisterAccessInfo; @@ -206,50 +207,4 @@ RegisterInfoArray *register_init_block32(DeviceState *owner, void register_finalize_block(RegisterInfoArray *r_array); -/* Define constants for a 32 bit register */ - -/* This macro will define A_FOO, for the byte address of a register - * as well as R_FOO for the uint32_t[] register number (A_FOO / 4). - */ -#define REG32(reg, addr) \ - enum { A_ ## reg = (addr) }; \ - enum { R_ ## reg = (addr) / 4 }; - -/* Define SHIFT, LENGTH and MASK constants for a field within a register */ - -/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH - * constants for field BAR in register FOO. - */ -#define FIELD(reg, field, shift, length) \ - enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \ - enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \ - enum { R_ ## reg ## _ ## field ## _MASK = \ - MAKE_64BIT_MASK(shift, length)}; - -/* Extract a field from a register */ -#define FIELD_EX32(storage, reg, field) \ - extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ - R_ ## reg ## _ ## field ## _LENGTH) - -/* Extract a field from an array of registers */ -#define ARRAY_FIELD_EX32(regs, reg, field) \ - FIELD_EX32((regs)[R_ ## reg], reg, field) - -/* Deposit a register field. - * Assigning values larger then the target field will result in - * compilation warnings. - */ -#define FIELD_DP32(storage, reg, field, val) ({ \ - struct { \ - unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ - } v = { .v = val }; \ - uint32_t d; \ - d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ - R_ ## reg ## _ ## field ## _LENGTH, v.v); \ - d; }) - -/* Deposit a field to array of registers. */ -#define ARRAY_FIELD_DP32(regs, reg, field, val) \ - (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val); - #endif diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h new file mode 100644 index 0000000000..af101d5ae6 --- /dev/null +++ b/include/hw/registerfields.h @@ -0,0 +1,60 @@ +/* + * Register Definition API: field macros + * + * Copyright (c) 2016 Xilinx Inc. + * Copyright (c) 2013 Peter Crosthwaite + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef REGISTERFIELDS_H +#define REGISTERFIELDS_H + +/* Define constants for a 32 bit register */ + +/* This macro will define A_FOO, for the byte address of a register + * as well as R_FOO for the uint32_t[] register number (A_FOO / 4). + */ +#define REG32(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) / 4 }; + +/* Define SHIFT, LENGTH and MASK constants for a field within a register */ + +/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH + * constants for field BAR in register FOO. + */ +#define FIELD(reg, field, shift, length) \ + enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \ + enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \ + enum { R_ ## reg ## _ ## field ## _MASK = \ + MAKE_64BIT_MASK(shift, length)}; + +/* Extract a field from a register */ +#define FIELD_EX32(storage, reg, field) \ + extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) + +/* Extract a field from an array of registers */ +#define ARRAY_FIELD_EX32(regs, reg, field) \ + FIELD_EX32((regs)[R_ ## reg], reg, field) + +/* Deposit a register field. + * Assigning values larger then the target field will result in + * compilation warnings. + */ +#define FIELD_DP32(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint32_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) + +/* Deposit a field to array of registers. */ +#define ARRAY_FIELD_DP32(regs, reg, field, val) \ + (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val); + +#endif From abc24d86cc0364f402e438fae3acb14289b40734 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:21 +0000 Subject: [PATCH 06/22] armv7m: Fix reads of CONTROL register bit 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v7m CONTROL register bit 1 is SPSEL, which indicates the stack being used. We were storing this information not in v7m.control but in the separate v7m.other_sp structure field. Unfortunately, the code handling reads of the CONTROL register didn't take account of this, and so if SPSEL was updated by an exception entry or exit then a subsequent guest read of CONTROL would get the wrong value. Using a separate structure field doesn't really gain us anything in efficiency, so drop this unnecessary complexity in favour of simply storing all the bits in v7m.control. This is a migration compatibility break for M profile CPUs only. Signed-off-by: Michael Davidsaver Reviewed-by: Alex Bennée Message-id: 1484937883-1068-6-git-send-email-peter.maydell@linaro.org [PMM: rewrote commit message; use deposit32(); use FIELD to define constants for masking and shifting of CONTROL register fields ] Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 - target/arm/helper.c | 35 +++++++++++++++++++++++------------ target/arm/internals.h | 7 +++++++ target/arm/machine.c | 6 ++---- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 151a5d754e..521c11b780 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -405,7 +405,6 @@ typedef struct CPUARMState { uint32_t vecbase; uint32_t basepri; uint32_t control; - int current_sp; int exception; } v7m; diff --git a/target/arm/helper.c b/target/arm/helper.c index 8edb08cbc1..dc383d16bb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5947,14 +5947,19 @@ static uint32_t v7m_pop(CPUARMState *env) } /* Switch to V7M main or process stack pointer. */ -static void switch_v7m_sp(CPUARMState *env, int process) +static void switch_v7m_sp(CPUARMState *env, bool new_spsel) { uint32_t tmp; - if (env->v7m.current_sp != process) { + bool old_spsel = env->v7m.control & R_V7M_CONTROL_SPSEL_MASK; + + if (old_spsel != new_spsel) { tmp = env->v7m.other_sp; env->v7m.other_sp = env->regs[13]; env->regs[13] = tmp; - env->v7m.current_sp = process; + + env->v7m.control = deposit32(env->v7m.control, + R_V7M_CONTROL_SPSEL_SHIFT, + R_V7M_CONTROL_SPSEL_LENGTH, new_spsel); } } @@ -6049,8 +6054,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) arm_log_exception(cs->exception_index); lr = 0xfffffff1; - if (env->v7m.current_sp) + if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) { lr |= 4; + } if (env->v7m.exception == 0) lr |= 8; @@ -8294,9 +8300,11 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) switch (reg) { case 8: /* MSP */ - return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13]; + return (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) ? + env->v7m.other_sp : env->regs[13]; case 9: /* PSP */ - return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp; + return (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) ? + env->regs[13] : env->v7m.other_sp; case 16: /* PRIMASK */ return (env->daif & PSTATE_I) != 0; case 17: /* BASEPRI */ @@ -8326,16 +8334,18 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) } break; case 8: /* MSP */ - if (env->v7m.current_sp) + if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) { env->v7m.other_sp = val; - else + } else { env->regs[13] = val; + } break; case 9: /* PSP */ - if (env->v7m.current_sp) + if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) { env->regs[13] = val; - else + } else { env->v7m.other_sp = val; + } break; case 16: /* PRIMASK */ if (val & 1) { @@ -8360,8 +8370,9 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) } break; case 20: /* CONTROL */ - env->v7m.control = val & 3; - switch_v7m_sp(env, (val & 2) != 0); + switch_v7m_sp(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); + env->v7m.control = val & (R_V7M_CONTROL_SPSEL_MASK | + R_V7M_CONTROL_NPRIV_MASK); break; default: qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" diff --git a/target/arm/internals.h b/target/arm/internals.h index 3cae5ff3b5..2e65bc12fa 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -25,6 +25,8 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +#include "hw/registerfields.h" + /* register banks for CPU modes */ #define BANK_USRSYS 0 #define BANK_SVC 1 @@ -75,6 +77,11 @@ static const char * const excnames[] = { */ #define GTIMER_SCALE 16 +/* Bit definitions for the v7M CONTROL register */ +FIELD(V7M_CONTROL, NPRIV, 0, 1) +FIELD(V7M_CONTROL, SPSEL, 1, 1) +FIELD(V7M_CONTROL, FPCA, 2, 1) + /* * For AArch64, map a given EL to an index in the banked_spsr array. * Note that this mapping and the AArch32 mapping defined in bank_number() diff --git a/target/arm/machine.c b/target/arm/machine.c index 487320db1d..8d93571b19 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -99,15 +99,13 @@ static bool m_needed(void *opaque) static const VMStateDescription vmstate_m = { .name = "cpu/m", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = m_needed, .fields = (VMStateField[]) { - VMSTATE_UINT32(env.v7m.other_sp, ARMCPU), VMSTATE_UINT32(env.v7m.vecbase, ARMCPU), VMSTATE_UINT32(env.v7m.basepri, ARMCPU), VMSTATE_UINT32(env.v7m.control, ARMCPU), - VMSTATE_INT32(env.v7m.current_sp, ARMCPU), VMSTATE_INT32(env.v7m.exception, ARMCPU), VMSTATE_END_OF_LIST() } From a20ee6005564590d33eabec11ed4dc7c432db36b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:22 +0000 Subject: [PATCH 07/22] armv7m: Clear FAULTMASK on return from non-NMI exceptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FAULTMASK must be cleared on return from all exceptions other than NMI. Signed-off-by: Michael Davidsaver Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1484937883-1068-7-git-send-email-peter.maydell@linaro.org --- target/arm/helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index dc383d16bb..cfbc6229c2 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5969,8 +5969,13 @@ static void do_v7m_exception_exit(CPUARMState *env) uint32_t xpsr; type = env->regs[15]; - if (env->v7m.exception != 0) + if (env->v7m.exception != ARMV7M_EXCP_NMI) { + /* Auto-clear FAULTMASK on return from other than NMI */ + env->daif &= ~PSTATE_F; + } + if (env->v7m.exception != 0) { armv7m_nvic_complete_irq(env->nvic, env->v7m.exception); + } /* Switch to the target stack. */ switch_v7m_sp(env, (type & 4) != 0); From feb0b1aa11f14ee71660aba46b46387d1f923c9e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:22 +0000 Subject: [PATCH 08/22] pflash_cfi01: fix per-device sector length in CFI table For configurations of the pflash_cfi01 device which set it up with a device-width not equal to the width (ie where we are emulating multiple narrow flash devices wired up in parallel), we were giving incorrect values in the CFI data table: (1) the sector length entry should specify the sector length for a single device, not the length for the overall collection of devices (2) the number of blocks per device must not be divided by the number of devices because the resulting device size would not match the overall size (3) this then means that the overall write block size must be modified depending on the number of devices because the entry is per device and when the guest writes into the flash it calculates the write size by using the CFI entry (write size per device) multiplied by the number of chips. (It would alternatively be possible to modify the write block size in the CFI table (currently hardcoded at 2048) and leave the overall write block size alone.) This commit corrects these bugs, and adds a hw-compat property to retain the old behaviour on 2.8 and earlier versions. (The only board we have which uses this sort of flash config and has machine versioning is the "virt" board -- the PC uses a single flash device and so behaviour is unaffected whether using old-multiple-chip-handling or not.) Here is a configuration example from the vexpress board: VEXPRESS_FLASH_SIZE = 64M VEXPRESS_FLASH_SECT_SIZE 256K num-blocks = VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE = 256 sector-length = 256K width = 4 device-width = 2 The code will fill the CFI entry with the following entries: num-blocks = 256 sector-length = 128K writeblock_size = 2048 This results in two chips, each with 256 * 128K = 32M device size and a write block size of 2048. A sector erase will be sent to both chips, thus 256K must be erased. When the guest sends a block write command, it will write 4096 bytes data at once (2048 per device). Signed-off-by: David Engraf Reviewed-by: Peter Maydell [PMM: cleaned up and expanded commit message] Signed-off-by: Peter Maydell --- hw/block/pflash_cfi01.c | 22 +++++++++++++++++----- include/hw/compat.h | 4 ++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 5f0ee9db00..71b98a3eef 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -99,6 +99,7 @@ struct pflash_t { char *name; void *storage; VMChangeStateEntry *vmstate; + bool old_multiple_chip_handling; }; static int pflash_post_load(void *opaque, int version_id); @@ -703,7 +704,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pflash_t *pfl = CFI_PFLASH01(dev); uint64_t total_len; int ret; - uint64_t blocks_per_device, device_len; + uint64_t blocks_per_device, sector_len_per_device, device_len; int num_devices; Error *local_err = NULL; @@ -726,8 +727,14 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) * in the cfi_table[]. */ num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1; - blocks_per_device = pfl->nb_blocs / num_devices; - device_len = pfl->sector_len * blocks_per_device; + if (pfl->old_multiple_chip_handling) { + blocks_per_device = pfl->nb_blocs / num_devices; + sector_len_per_device = pfl->sector_len; + } else { + blocks_per_device = pfl->nb_blocs; + sector_len_per_device = pfl->sector_len / num_devices; + } + device_len = sector_len_per_device * blocks_per_device; /* XXX: to be fixed */ #if 0 @@ -832,6 +839,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pfl->cfi_table[0x2A] = 0x0B; } pfl->writeblock_size = 1 << pfl->cfi_table[0x2A]; + if (!pfl->old_multiple_chip_handling && num_devices > 1) { + pfl->writeblock_size *= num_devices; + } pfl->cfi_table[0x2B] = 0x00; /* Number of erase block regions (uniform) */ @@ -839,8 +849,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) /* Erase block region 1 */ pfl->cfi_table[0x2D] = blocks_per_device - 1; pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8; - pfl->cfi_table[0x2F] = pfl->sector_len >> 8; - pfl->cfi_table[0x30] = pfl->sector_len >> 16; + pfl->cfi_table[0x2F] = sector_len_per_device >> 8; + pfl->cfi_table[0x30] = sector_len_per_device >> 16; /* Extended */ pfl->cfi_table[0x31] = 'P'; @@ -898,6 +908,8 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), DEFINE_PROP_STRING("name", struct pflash_t, name), + DEFINE_PROP_BOOL("old-multiple-chip-handling", struct pflash_t, + old_multiple_chip_handling, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/compat.h b/include/hw/compat.h index 34e9b4a660..ee0dd1b5df 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -10,6 +10,10 @@ .driver = "fw_cfg_io",\ .property = "x-file-slots",\ .value = stringify(0x10),\ + },{\ + .driver = "pflash_cfi01",\ + .property = "old-multiple-chip-handling",\ + .value = "on",\ }, #define HW_COMPAT_2_7 \ From 531c60a97ab51618b4b9ccef1c5fe00607079706 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:22 +0000 Subject: [PATCH 09/22] target/arm: Drop IS_M() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only use the IS_M() macro in two places, and it's a bit of a namespace grab to put in cpu.h. Drop it in favour of just explicitly calling arm_feature() in the places where it was used. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1485285380-10565-2-git-send-email-peter.maydell@linaro.org --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 6 ------ target/arm/helper.c | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 907598968c..6395d5adef 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -182,7 +182,7 @@ static void arm_cpu_reset(CPUState *s) /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is * clear at reset. Initial SP and PC are loaded from ROM. */ - if (IS_M(env)) { + if (arm_feature(env, ARM_FEATURE_M)) { uint32_t initial_msp; /* Loaded from 0x0 */ uint32_t initial_pc; /* Loaded from 0x4 */ uint8_t *rom; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 521c11b780..b2cc3298ac 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1762,12 +1762,6 @@ bool write_list_to_cpustate(ARMCPU *cpu); */ bool write_cpustate_to_list(ARMCPU *cpu); -/* Does the core conform to the "MicroController" profile. e.g. Cortex-M3. - Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are - conventional cores (ie. Application or Realtime profile). */ - -#define IS_M(env) arm_feature(env, ARM_FEATURE_M) - #define ARM_CPUID_TI915T 0x54029152 #define ARM_CPUID_TI925T 0x54029252 diff --git a/target/arm/helper.c b/target/arm/helper.c index cfbc6229c2..ce7e43bbac 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6695,7 +6695,7 @@ void arm_cpu_do_interrupt(CPUState *cs) CPUARMState *env = &cpu->env; unsigned int new_el = env->exception.target_el; - assert(!IS_M(env)); + assert(!arm_feature(env, ARM_FEATURE_M)); arm_log_exception(cs->exception_index); qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env), From d713ea6c464918f87d1dd480520dd4aedb685d9a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:22 +0000 Subject: [PATCH 10/22] armv7m_nvic: keep a pointer to the CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many NVIC operations access the CPU state, so store a pointer in struct nvic_state rather than fetching it via qemu_get_cpu() every time we need it. As with the arm_gicv3_common code, we currently just call qemu_get_cpu() in the NVIC's realize method, but in future we might want to use a QOM property to pass the CPU to the NVIC. This imposes an ordering requirement that the CPU is realized before the NVIC, but that is always true since both are dealt with in armv7m_init(). Signed-off-by: Michael Davidsaver Reviewed-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1485285380-10565-3-git-send-email-peter.maydell@linaro.org [PMM: Use qemu_get_cpu(0) rather than first_cpu; expand commit message] Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 06d8db6bd6..81dcb83040 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -23,6 +23,7 @@ typedef struct { GICState gic; + ARMCPU *cpu; struct { uint32_t control; uint32_t reload; @@ -155,7 +156,7 @@ void armv7m_nvic_complete_irq(void *opaque, int irq) static uint32_t nvic_readl(nvic_state *s, uint32_t offset) { - ARMCPU *cpu; + ARMCPU *cpu = s->cpu; uint32_t val; int irq; @@ -187,11 +188,9 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) case 0x1c: /* SysTick Calibration Value. */ return 10000; case 0xd00: /* CPUID Base. */ - cpu = ARM_CPU(qemu_get_cpu(0)); return cpu->midr; case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ - cpu = ARM_CPU(qemu_get_cpu(0)); val = cpu->env.v7m.exception; if (val == 1023) { val = 0; @@ -222,7 +221,6 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) val |= (1 << 31); return val; case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(qemu_get_cpu(0)); return cpu->env.v7m.vecbase; case 0xd0c: /* Application Interrupt/Reset Control. */ return 0xfa050000; @@ -296,7 +294,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) { - ARMCPU *cpu; + ARMCPU *cpu = s->cpu; uint32_t oldval; switch (offset) { case 0x10: /* SysTick Control and Status. */ @@ -349,7 +347,6 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) } break; case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(qemu_get_cpu(0)); cpu->env.v7m.vecbase = value & 0xffffff80; break; case 0xd0c: /* Application Interrupt/Reset Control. */ @@ -495,6 +492,8 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) NVICClass *nc = NVIC_GET_CLASS(s); Error *local_err = NULL; + s->cpu = ARM_CPU(qemu_get_cpu(0)); + assert(s->cpu); /* The NVIC always has only one CPU */ s->gic.num_cpu = 1; /* Tell the common code we're an NVIC */ From 2c4da50d9477fb830d778bb5d6a11215aa359b44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:23 +0000 Subject: [PATCH 11/22] armv7m: add state for v7M CCR, CFSR, HFSR, DFSR, MMFAR, BFAR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the structure fields, VMState fields, reset code and macros for the v7M system control registers CCR, CFSR, HFSR, DFSR, MMFAR and BFAR. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1485285380-10565-4-git-send-email-peter.maydell@linaro.org --- target/arm/cpu.c | 7 ++++++ target/arm/cpu.h | 54 ++++++++++++++++++++++++++++++++++++++++++++ target/arm/machine.c | 10 ++++++-- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6395d5adef..c804f59993 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -188,6 +188,13 @@ static void arm_cpu_reset(CPUState *s) uint8_t *rom; env->daif &= ~PSTATE_I; + + /* The reset value of this bit is IMPDEF, but ARM recommends + * that it resets to 1, so QEMU always does that rather than making + * it dependent on CPU model. + */ + env->v7m.ccr = R_V7M_CCR_STKALIGN_MASK; + rom = rom_ptr(0); if (rom) { /* Address zero is covered by ROM which hasn't yet been diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b2cc3298ac..4b062d20f3 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -21,6 +21,7 @@ #define ARM_CPU_H #include "kvm-consts.h" +#include "hw/registerfields.h" #if defined(TARGET_AARCH64) /* AArch64 definitions */ @@ -405,6 +406,12 @@ typedef struct CPUARMState { uint32_t vecbase; uint32_t basepri; uint32_t control; + uint32_t ccr; /* Configuration and Control */ + uint32_t cfsr; /* Configurable Fault Status */ + uint32_t hfsr; /* HardFault Status */ + uint32_t dfsr; /* Debug Fault Status Register */ + uint32_t mmfar; /* MemManage Fault Address */ + uint32_t bfar; /* BusFault Address */ int exception; } v7m; @@ -1086,6 +1093,53 @@ enum arm_cpu_mode { #define ARM_IWMMXT_wCGR2 10 #define ARM_IWMMXT_wCGR3 11 +/* V7M CCR bits */ +FIELD(V7M_CCR, NONBASETHRDENA, 0, 1) +FIELD(V7M_CCR, USERSETMPEND, 1, 1) +FIELD(V7M_CCR, UNALIGN_TRP, 3, 1) +FIELD(V7M_CCR, DIV_0_TRP, 4, 1) +FIELD(V7M_CCR, BFHFNMIGN, 8, 1) +FIELD(V7M_CCR, STKALIGN, 9, 1) +FIELD(V7M_CCR, DC, 16, 1) +FIELD(V7M_CCR, IC, 17, 1) + +/* V7M CFSR bits for MMFSR */ +FIELD(V7M_CFSR, IACCVIOL, 0, 1) +FIELD(V7M_CFSR, DACCVIOL, 1, 1) +FIELD(V7M_CFSR, MUNSTKERR, 3, 1) +FIELD(V7M_CFSR, MSTKERR, 4, 1) +FIELD(V7M_CFSR, MLSPERR, 5, 1) +FIELD(V7M_CFSR, MMARVALID, 7, 1) + +/* V7M CFSR bits for BFSR */ +FIELD(V7M_CFSR, IBUSERR, 8 + 0, 1) +FIELD(V7M_CFSR, PRECISERR, 8 + 1, 1) +FIELD(V7M_CFSR, IMPRECISERR, 8 + 2, 1) +FIELD(V7M_CFSR, UNSTKERR, 8 + 3, 1) +FIELD(V7M_CFSR, STKERR, 8 + 4, 1) +FIELD(V7M_CFSR, LSPERR, 8 + 5, 1) +FIELD(V7M_CFSR, BFARVALID, 8 + 7, 1) + +/* V7M CFSR bits for UFSR */ +FIELD(V7M_CFSR, UNDEFINSTR, 16 + 0, 1) +FIELD(V7M_CFSR, INVSTATE, 16 + 1, 1) +FIELD(V7M_CFSR, INVPC, 16 + 2, 1) +FIELD(V7M_CFSR, NOCP, 16 + 3, 1) +FIELD(V7M_CFSR, UNALIGNED, 16 + 8, 1) +FIELD(V7M_CFSR, DIVBYZERO, 16 + 9, 1) + +/* V7M HFSR bits */ +FIELD(V7M_HFSR, VECTTBL, 1, 1) +FIELD(V7M_HFSR, FORCED, 30, 1) +FIELD(V7M_HFSR, DEBUGEVT, 31, 1) + +/* V7M DFSR bits */ +FIELD(V7M_DFSR, HALTED, 0, 1) +FIELD(V7M_DFSR, BKPT, 1, 1) +FIELD(V7M_DFSR, DWTTRAP, 2, 1) +FIELD(V7M_DFSR, VCATCH, 3, 1) +FIELD(V7M_DFSR, EXTERNAL, 4, 1) + /* If adding a feature bit which corresponds to a Linux ELF * HWCAP bit, remember to update the feature-bit-to-hwcap * mapping in linux-user/elfload.c:get_elf_hwcap(). diff --git a/target/arm/machine.c b/target/arm/machine.c index 8d93571b19..fa5ec76090 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -99,13 +99,19 @@ static bool m_needed(void *opaque) static const VMStateDescription vmstate_m = { .name = "cpu/m", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.vecbase, ARMCPU), VMSTATE_UINT32(env.v7m.basepri, ARMCPU), VMSTATE_UINT32(env.v7m.control, ARMCPU), + VMSTATE_UINT32(env.v7m.ccr, ARMCPU), + VMSTATE_UINT32(env.v7m.cfsr, ARMCPU), + VMSTATE_UINT32(env.v7m.hfsr, ARMCPU), + VMSTATE_UINT32(env.v7m.dfsr, ARMCPU), + VMSTATE_UINT32(env.v7m.mmfar, ARMCPU), + VMSTATE_UINT32(env.v7m.bfar, ARMCPU), VMSTATE_INT32(env.v7m.exception, ARMCPU), VMSTATE_END_OF_LIST() } From e6b332097d1a4713173a82f17d039b4c78bc6f59 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:23 +0000 Subject: [PATCH 12/22] armv7m: implement CCR, CFSR, HFSR, DFSR, BFAR, and MMFAR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the v7M system registers CCR, CFSR, HFSR, DFSR, BFAR and MMFAR. For the moment these simply read as written (with some basic handling of RAZ/WI bits and W1C semantics). Signed-off-by: Michael Davidsaver Reviewed-by: Alex Bennée Message-id: 1485285380-10565-5-git-send-email-peter.maydell@linaro.org [PMM: drop warning about setting unimplemented CCR bits; tweak commit message; add DFSR] Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 81dcb83040..60e72d7395 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -228,8 +228,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) /* TODO: Implement SLEEPONEXIT. */ return 0; case 0xd14: /* Configuration Control. */ - /* TODO: Implement Configuration Control bits. */ - return 0; + return cpu->env.v7m.ccr; case 0xd24: /* System Handler Status. */ val = 0; if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0); @@ -248,16 +247,19 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); return val; case 0xd28: /* Configurable Fault Status. */ - /* TODO: Implement Fault Status. */ - qemu_log_mask(LOG_UNIMP, "Configurable Fault Status unimplemented\n"); - return 0; + return cpu->env.v7m.cfsr; case 0xd2c: /* Hard Fault Status. */ + return cpu->env.v7m.hfsr; case 0xd30: /* Debug Fault Status. */ - case 0xd34: /* Mem Manage Address. */ + return cpu->env.v7m.dfsr; + case 0xd34: /* MMFAR MemManage Fault Address */ + return cpu->env.v7m.mmfar; case 0xd38: /* Bus Fault Address. */ + return cpu->env.v7m.bfar; case 0xd3c: /* Aux Fault Status. */ /* TODO: Implement fault status registers. */ - qemu_log_mask(LOG_UNIMP, "Fault status registers unimplemented\n"); + qemu_log_mask(LOG_UNIMP, + "Aux Fault status registers unimplemented\n"); return 0; case 0xd40: /* PFR0. */ return 0x00000030; @@ -366,9 +368,19 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) } break; case 0xd10: /* System Control. */ - case 0xd14: /* Configuration Control. */ /* TODO: Implement control registers. */ - qemu_log_mask(LOG_UNIMP, "NVIC: SCR and CCR unimplemented\n"); + qemu_log_mask(LOG_UNIMP, "NVIC: SCR unimplemented\n"); + break; + case 0xd14: /* Configuration Control. */ + /* Enforce RAZ/WI on reserved and must-RAZ/WI bits */ + value &= (R_V7M_CCR_STKALIGN_MASK | + R_V7M_CCR_BFHFNMIGN_MASK | + R_V7M_CCR_DIV_0_TRP_MASK | + R_V7M_CCR_UNALIGN_TRP_MASK | + R_V7M_CCR_USERSETMPEND_MASK | + R_V7M_CCR_NONBASETHRDENA_MASK); + + cpu->env.v7m.ccr = value; break; case 0xd24: /* System Handler Control. */ /* TODO: Real hardware allows you to set/clear the active bits @@ -378,13 +390,23 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; break; case 0xd28: /* Configurable Fault Status. */ + cpu->env.v7m.cfsr &= ~value; /* W1C */ + break; case 0xd2c: /* Hard Fault Status. */ + cpu->env.v7m.hfsr &= ~value; /* W1C */ + break; case 0xd30: /* Debug Fault Status. */ + cpu->env.v7m.dfsr &= ~value; /* W1C */ + break; case 0xd34: /* Mem Manage Address. */ + cpu->env.v7m.mmfar = value; + return; case 0xd38: /* Bus Fault Address. */ + cpu->env.v7m.bfar = value; + return; case 0xd3c: /* Aux Fault Status. */ qemu_log_mask(LOG_UNIMP, - "NVIC: fault status registers unimplemented\n"); + "NVIC: Aux fault status registers unimplemented\n"); break; case 0xf00: /* Software Triggered Interrupt Register */ if ((value & 0x1ff) < s->num_irq) { From dc858c6633a9af8b80c1509cf6f825e4390d3ad1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:23 +0000 Subject: [PATCH 13/22] armv7m: honour CCR.STACKALIGN on exception entry The CCR.STACKALIGN bit controls whether the CPU is supposed to force 8-alignment of the stack pointer on entry to the exception handler. Signed-off-by: Michael Davidsaver Message-id: 1485285380-10565-6-git-send-email-peter.maydell@linaro.org [PMM: commit message and comment tweaks] Signed-off-by: Peter Maydell --- target/arm/helper.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index ce7e43bbac..7dc30f5769 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6110,10 +6110,8 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) return; /* Never happens. Keep compiler happy. */ } - /* Align stack pointer. */ - /* ??? Should only do this if Configuration Control Register - STACKALIGN bit is set. */ - if (env->regs[13] & 4) { + /* Align stack pointer if the guest wants that */ + if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) { env->regs[13] -= 4; xpsr |= 0x200; } From 81dd9648c69bb89afdd6f4bb3ed6f3efdac96524 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:23 +0000 Subject: [PATCH 14/22] armv7m: set CFSR.UNDEFINSTR on undefined instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we take an exception for an undefined instruction, set the appropriate CFSR bit. Signed-off-by: Michael Davidsaver Reviewed-by: Alex Bennée Message-id: 1485285380-10565-7-git-send-email-peter.maydell@linaro.org [PMM: tweaked commit message, comment] Signed-off-by: Peter Maydell --- target/arm/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7dc30f5769..e6b1c36a95 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6072,6 +6072,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) switch (cs->exception_index) { case EXCP_UDEF: armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); + env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK; return; case EXCP_SWI: /* The PC already points to the next instruction. */ From 7517748e3f71a3099e57915fba95c4c308e6d842 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:24 +0000 Subject: [PATCH 15/22] armv7m: Report no-coprocessor faults correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For v7M attempts to access a nonexistent coprocessor are reported differently from plain undefined instructions (as UsageFaults of type NOCP rather than type UNDEFINSTR). Split them out into a new EXCP_NOCP so we can report the FSR value correctly. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1485285380-10565-8-git-send-email-peter.maydell@linaro.org --- linux-user/main.c | 1 + target/arm/cpu.h | 1 + target/arm/helper.c | 4 ++++ target/arm/translate.c | 8 ++++++++ 4 files changed, 14 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index f5c85574f9..30049581ef 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -573,6 +573,7 @@ void cpu_loop(CPUARMState *env) switch(trapnr) { case EXCP_UDEF: + case EXCP_NOCP: { TaskState *ts = cs->opaque; uint32_t opcode; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4b062d20f3..39bff86daf 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -53,6 +53,7 @@ #define EXCP_VIRQ 14 #define EXCP_VFIQ 15 #define EXCP_SEMIHOST 16 /* semihosting call */ +#define EXCP_NOCP 17 /* v7M NOCP UsageFault */ #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 diff --git a/target/arm/helper.c b/target/arm/helper.c index e6b1c36a95..c23df1b133 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6074,6 +6074,10 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK; return; + case EXCP_NOCP: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); + env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK; + return; case EXCP_SWI: /* The PC already points to the next instruction. */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC); diff --git a/target/arm/translate.c b/target/arm/translate.c index a7c2abeffe..493c627bcf 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -10217,6 +10217,14 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw break; case 6: case 7: case 14: case 15: /* Coprocessor. */ + if (arm_dc_feature(s, ARM_FEATURE_M)) { + /* We don't currently implement M profile FP support, + * so this entire space should give a NOCP fault. + */ + gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(), + default_exception_el(s)); + break; + } if (((insn >> 24) & 3) == 3) { /* Translate into the equivalent ARM encoding. */ insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28); From bdd04fc76a78d61ae0f0e93ce345f9cf2e49a9a8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:24 +0000 Subject: [PATCH 16/22] armv7m: Honour CCR.USERSETMPEND MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CCR.USERSETMPEND bit has to be set to permit unprivileged code to write to the Software Triggered Interrupt register; honour this bit rather than letting any code write to the register. Signed-off-by: Michael Davidsaver Reviewed-by: Alex Bennée Message-id: 1485285380-10565-9-git-send-email-peter.maydell@linaro.org [PMM: Tweak commit message, comment, phrasing of condition] Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 60e72d7395..fe5c303de9 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -409,7 +409,10 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) "NVIC: Aux fault status registers unimplemented\n"); break; case 0xf00: /* Software Triggered Interrupt Register */ - if ((value & 0x1ff) < s->num_irq) { + /* user mode can only write to STIR if CCR.USERSETMPEND permits it */ + if ((value & 0x1ff) < s->num_irq && + (arm_current_el(&cpu->env) || + (cpu->env.v7m.ccr & R_V7M_CCR_USERSETMPEND_MASK))) { gic_set_pending_private(&s->gic, 0, value & 0x1ff); } break; From dc7abe4d65ad39390b2db120f5ad18f8f6576f8b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:24 +0000 Subject: [PATCH 17/22] armv7m: FAULTMASK should be 0 on reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For M profile CPUs, FAULTMASK should be 0 on reset, like PRIMASK. QEMU stores FAULTMASK in the PSTATE F bit, so (as with PRIMASK in the I bit) we have to clear these to undo the A profile default of 1. Update the comment accordingly and move it so that it's closer to the code it's referring to. Signed-off-by: Michael Davidsaver Reviewed-by: Alex Bennée Message-id: 1485285380-10565-10-git-send-email-peter.maydell@linaro.org [PMM: rewrote commit message, moved comments] Signed-off-by: Peter Maydell --- target/arm/cpu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c804f59993..0814f73462 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -179,15 +179,16 @@ static void arm_cpu_reset(CPUState *s) /* SVC mode with interrupts disabled. */ env->uncached_cpsr = ARM_CPU_MODE_SVC; env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F; - /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is - * clear at reset. Initial SP and PC are loaded from ROM. - */ + if (arm_feature(env, ARM_FEATURE_M)) { uint32_t initial_msp; /* Loaded from 0x0 */ uint32_t initial_pc; /* Loaded from 0x4 */ uint8_t *rom; - env->daif &= ~PSTATE_I; + /* For M profile we store FAULTMASK and PRIMASK in the + * PSTATE F and I bits; these are both clear at reset. + */ + env->daif &= ~(PSTATE_I | PSTATE_F); /* The reset value of this bit is IMPDEF, but ARM recommends * that it resets to 1, so QEMU always does that rather than making @@ -195,6 +196,7 @@ static void arm_cpu_reset(CPUState *s) */ env->v7m.ccr = R_V7M_CCR_STKALIGN_MASK; + /* Load the initial SP and PC from the vector table at address 0 */ rom = rom_ptr(0); if (rom) { /* Address zero is covered by ROM which hasn't yet been From 056f43df9168413f304500b69c33158d66efb7cf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:24 +0000 Subject: [PATCH 18/22] armv7m: R14 should reset to 0xffffffff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For M profile (unlike A profile) the reset value of R14 is specified as 0xffffffff. (The rationale is that this is an illegal exception return value, so if guest code tries to return to it it will result in a helpful exception.) Registers r0 to r12 and the flags are architecturally UNKNOWN on reset, so we leave those at zero. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1485285380-10565-11-git-send-email-peter.maydell@linaro.org --- target/arm/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 0814f73462..e9f10f7747 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -196,6 +196,9 @@ static void arm_cpu_reset(CPUState *s) */ env->v7m.ccr = R_V7M_CCR_STKALIGN_MASK; + /* Unlike A/R profile, M profile defines the reset LR value */ + env->regs[14] = 0xffffffff; + /* Load the initial SP and PC from the vector table at address 0 */ rom = rom_ptr(0); if (rom) { From d05a86285cacdcbfdd97e437d156545666fa5641 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Jan 2017 15:20:25 +0000 Subject: [PATCH 19/22] arm: stellaris: make MII accesses complete immediately When the guest attempts to start an MII register access via the MCTL register, clear the START bit, so that when the guest reads it back the register transaction will be signalled as having completed. This avoids the guest spinning as it polls the START bit waiting for it to clear (which it previously never would). The MII registers themselves still aren't implemented, but at least we can avoid guests spending quite so much time busy waiting. Signed-off-by: Michael Davidsaver Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 1484938222-1423-1-git-send-email-peter.maydell@linaro.org [PMM: expand commit message] Signed-off-by: Peter Maydell --- hw/net/stellaris_enet.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 957730e023..04bd10ada3 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -416,7 +416,10 @@ static void stellaris_enet_write(void *opaque, hwaddr offset, s->thr = value; break; case 0x20: /* MCTL */ - s->mctl = value; + /* TODO: MII registers aren't modelled. + * Clear START, indicating that the operation completes immediately. + */ + s->mctl = value & ~1; break; case 0x24: /* MDV */ s->mdv = value; From e62694a078f182c822a7b6d3976b1bcc72e78ec2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:25 +0000 Subject: [PATCH 20/22] hw/char/exynos4210_uart: Drop unused local variable frame_size The frame_size local variable in exynos4210_uart_update_parameters() is calculated but never used (and has been this way since the device was introduced in commit e5a4914efc7). The qemu_chr_fe_ioctl() doesn't need this information (if it really wanted it it could calculate it from the parity/data_bits/stop_bits), so just drop the variable entirely. Fixes: https://bugs.launchpad.net/bugs/1655702 Signed-off-by: Peter Maydell Message-id: 1484589515-26353-1-git-send-email-peter.maydell@linaro.org --- hw/char/exynos4210_uart.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 820d1abeb9..0cd3dd3958 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -306,7 +306,7 @@ static void exynos4210_uart_update_irq(Exynos4210UartState *s) static void exynos4210_uart_update_parameters(Exynos4210UartState *s) { - int speed, parity, data_bits, stop_bits, frame_size; + int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; uint64_t uclk_rate; @@ -314,9 +314,7 @@ static void exynos4210_uart_update_parameters(Exynos4210UartState *s) return; } - frame_size = 1; /* start bit */ if (s->reg[I_(ULCON)] & 0x20) { - frame_size++; /* parity bit */ if (s->reg[I_(ULCON)] & 0x28) { parity = 'E'; } else { @@ -334,8 +332,6 @@ static void exynos4210_uart_update_parameters(Exynos4210UartState *s) data_bits = (s->reg[I_(ULCON)] & 0x3) + 5; - frame_size += data_bits + stop_bits; - uclk_rate = 24000000; speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) + From d87576e38df760ef1cb635197d51f207e2a8eda9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 27 Jan 2017 15:20:25 +0000 Subject: [PATCH 21/22] arm_gicv3: Fix broken logic in ELRSR calculation Fix a broken expression in the calculation of ELRSR register bits: instead of "(lr & ICH_LR_EL2_HW) == 1" we want to check for != 0, because the HW bit is not bit 0 so a test for == 1 is always false. Fixes: https://bugs.launchpad.net/bugs/1658506 Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-id: 1485255993-6322-1-git-send-email-peter.maydell@linaro.org --- hw/intc/arm_gicv3_cpuif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index a9ee7fddf9..c25ee03556 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -2430,7 +2430,7 @@ static uint64_t ich_elrsr_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t lr = cs->ich_lr_el2[i]; if ((lr & ICH_LR_EL2_STATE_MASK) == 0 && - ((lr & ICH_LR_EL2_HW) == 1 || (lr & ICH_LR_EL2_EOI) == 0)) { + ((lr & ICH_LR_EL2_HW) != 0 || (lr & ICH_LR_EL2_EOI) == 0)) { value |= (1 << i); } } From 146871c33eb70ca7090a0a55e69e5a8f9b5eb102 Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Fri, 27 Jan 2017 15:20:25 +0000 Subject: [PATCH 22/22] dma: omap: check dma channel data_type When setting dma channel 'data_type', if (value & 3) == 3, the set 'data_type' is said to be bad. This also leads to an OOB access in 'omap_dma_transfer_generic', while doing cpu_physical_memory_r/w operations. Add check to avoid it. Reported-by: Jiang Xin Signed-off-by: Prasad J Pandit Message-id: 20170127120528.30959-1-ppandit@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/dma/omap_dma.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index f6f86f9639..45dfe7aadd 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -878,15 +878,17 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, ch->burst[0] = (value & 0x0180) >> 7; ch->pack[0] = (value & 0x0040) >> 6; ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2); - ch->data_type = 1 << (value & 3); if (ch->port[0] >= __omap_dma_port_last) printf("%s: invalid DMA port %i\n", __FUNCTION__, ch->port[0]); if (ch->port[1] >= __omap_dma_port_last) printf("%s: invalid DMA port %i\n", __FUNCTION__, ch->port[1]); - if ((value & 3) == 3) + ch->data_type = 1 << (value & 3); + if ((value & 3) == 3) { printf("%s: bad data_type for DMA channel\n", __FUNCTION__); + ch->data_type >>= 1; + } break; case 0x02: /* SYS_DMA_CCR_CH0 */ @@ -1988,8 +1990,10 @@ static void omap_dma4_write(void *opaque, hwaddr addr, fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n", __FUNCTION__); ch->data_type = 1 << (value & 3); - if ((value & 3) == 3) + if ((value & 3) == 3) { printf("%s: bad data_type for DMA channel\n", __FUNCTION__); + ch->data_type >>= 1; + } break; case 0x14: /* DMA4_CEN */