From c2d9644e6d517170bf6520f633628259a8460d48 Mon Sep 17 00:00:00 2001 From: Roman Kapl Date: Mon, 20 Aug 2018 11:24:31 +0100 Subject: target/arm: Fix crash on conditional instruction in an IT block If an instruction is conditional (like CBZ) and it is executed conditionally (using the ITx instruction), a jump to an undefined label is generated, and QEMU crashes. CBZ in IT block is an UNPREDICTABLE behavior, but we should not crash. Honouring the condition code is allowed by the spec in this case (constrained unpredictable, ARMv8, section K1.1.7), and matches what we do for other "UNPREDICTABLE inside an IT block" instructions. Fix the 'skip on condition' code to create a new label only if it does not already exist. Previously multiple labels were created, but only the last one of them was set. Signed-off-by: Roman Kapl Reviewed-by: Richard Henderson Message-id: 20180816120533.6587-1-rka@sysgo.com [PMM: fixed ^ 1 being applied to wrong argument, fixed typo] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index f845da7..14aa2cd 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -8480,6 +8480,22 @@ static void gen_srs(DisasContext *s, s->base.is_jmp = DISAS_UPDATE; } +/* Generate a label used for skipping this instruction */ +static void arm_gen_condlabel(DisasContext *s) +{ + if (!s->condjmp) { + s->condlabel = gen_new_label(); + s->condjmp = 1; + } +} + +/* Skip this instruction if the ARM condition is false */ +static void arm_skip_unless(DisasContext *s, uint32_t cond) +{ + arm_gen_condlabel(s); + arm_gen_test_cc(cond ^ 1, s->condlabel); +} + static void disas_arm_insn(DisasContext *s, unsigned int insn) { unsigned int cond, val, op1, i, shift, rm, rs, rn, rd, sh; @@ -8709,9 +8725,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (cond != 0xe) { /* if not always execute, we generate a conditional jump to next instruction */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, cond); } if ((insn & 0x0f900000) == 0x03000000) { if ((insn & (1 << 21)) == 0) { @@ -11205,9 +11219,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) /* Conditional branch. */ op = (insn >> 22) & 0xf; /* Generate a conditional jump to next instruction. */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(op ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, op); /* offset[11:1] = insn[10:0] */ offset = (insn & 0x7ff) << 1; @@ -12131,8 +12143,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) case 1: case 3: case 9: case 11: /* czb */ rm = insn & 7; tmp = load_reg(s, rm); - s->condlabel = gen_new_label(); - s->condjmp = 1; + arm_gen_condlabel(s); if (insn & (1 << 11)) tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel); else @@ -12295,9 +12306,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; } /* generate a conditional jump to next instruction */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, cond); /* jump to the offset */ val = (uint32_t)s->pc + 2; @@ -12676,9 +12685,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) uint32_t cond = dc->condexec_cond; if (cond != 0x0e) { /* Skip conditional when condition is AL. */ - dc->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, dc->condlabel); - dc->condjmp = 1; + arm_skip_unless(dc, cond); } } -- cgit v1.1 From 67c791f611c6d9513a2ae4129ce861f4c655ae0f Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 20 Aug 2018 11:24:31 +0100 Subject: docs/generic-loader: mention U-Boot and Intel HEX executable formats The generic loader device supports the U-Boot and Intel HEX executable formats in addition to the document raw and ELF formats. Reword the documentation to include these formats and explain how various options depend on the executable format. Signed-off-by: Stefan Hajnoczi Reviewed-by: Alistair Francis Message-id: 20180816145554.9814-1-stefanha@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/generic-loader.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/generic-loader.txt b/docs/generic-loader.txt index 31bbcd4..a9603a2 100644 --- a/docs/generic-loader.txt +++ b/docs/generic-loader.txt @@ -56,25 +56,25 @@ An example of setting CPU 0's PC to 0x8000 is: Loading Files ------------- -The loader device also allows files to be loaded into memory. It can load raw -files and ELF executable files. Raw files are loaded verbatim. ELF executable -files are loaded by an ELF loader. The syntax is shown below: +The loader device also allows files to be loaded into memory. It can load ELF, +U-Boot, and Intel HEX executable formats as well as raw images. The syntax is +shown below: -device loader,file=[,addr=][,cpu-num=][,force-raw=] - A file to be loaded into memory - - The addr in memory that the file should be loaded. This is - ignored if you are using an ELF (unless force-raw is true). - This is required if you aren't loading an ELF. + - The memory address where the file should be loaded. This is + required for raw images and ignored for non-raw files. - This specifies the CPU that should be used. This is an optional argument and will cause the CPU's PC to be set to - where the image is stored or in the case of an ELF file to - the value in the header. This option should only be used - for the boot image. + the memory address where the raw file is loaded or the entry + point specified in the executable format header. This option + should only be used for the boot image. This will also cause the image to be written to the specified CPU's address space. If not specified, the default is CPU 0. - Setting force-raw=on forces the file to be treated as a raw - image. This can be used to load ELF files as if they were raw. + image. This can be used to load supported executable formats + as if they were raw. All values are parsed using the standard QemuOps parsing. This allows the user to specify any values in any format supported. By default the values -- cgit v1.1 From 84be3ef1defeefc4ef157f8657d178d204ec0f3a Mon Sep 17 00:00:00 2001 From: Jia He Date: Mon, 20 Aug 2018 11:24:31 +0100 Subject: hw/intc/arm_gicv3_its: downgrade error_report to warn_report in kvm_arm_its_reset In scripts/arch-run.bash of kvm-unit-tests, it will check the qemu output log with: if [ -z "$(echo "$errors" | grep -vi warning)" ]; then Thus without the warning prefix, all of the test fail. Since it is not unrecoverable error in kvm_arm_its_reset for current implementation, downgrading the report from error to warn makes sense. Signed-off-by: Jia He Message-id: 1531969910-32843-1-git-send-email-jia.he@hxt-semitech.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_its_kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 271ebe4..01573ab 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -211,7 +211,7 @@ static void kvm_arm_its_reset(DeviceState *dev) return; } - error_report("ITS KVM: full reset is not supported by the host kernel"); + warn_report("ITS KVM: full reset is not supported by the host kernel"); if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, GITS_CTLR)) { -- cgit v1.1 From 3c54cf7705f243c0d37f254abd0131ab69ae94e5 Mon Sep 17 00:00:00 2001 From: Hans-Erik Floryd Date: Mon, 20 Aug 2018 11:24:31 +0100 Subject: imx_serial: Generate interrupt on receive data ready if enabled Generate an interrupt if USR2_RDR and UCR4_DREN are both set. Signed-off-by: Hans-Erik Floryd Message-id: 1534341354-11956-1-git-send-email-hans-erik.floryd@rt-labs.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/imx_serial.c | 3 ++- include/hw/char/imx_serial.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 0747db9..1e36319 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -74,8 +74,9 @@ static void imx_update(IMXSerialState *s) mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0; /* * TCEN and TXDC are both bit 3 + * RDR and DREN are both bit 0 */ - mask |= s->ucr4 & UCR4_TCEN; + mask |= s->ucr4 & (UCR4_TCEN | UCR4_DREN); usr2 = s->usr2 & mask; diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h index ee80da1..c8b7428 100644 --- a/include/hw/char/imx_serial.h +++ b/include/hw/char/imx_serial.h @@ -68,6 +68,7 @@ #define UCR2_RXEN (1<<1) /* Receiver enable */ #define UCR2_SRST (1<<0) /* Reset complete */ +#define UCR4_DREN BIT(0) /* Receive Data Ready interrupt enable */ #define UCR4_TCEN BIT(3) /* TX complete interrupt enable */ #define UTS1_TXEMPTY (1<<6) -- cgit v1.1 From 55b53c718b2f684793eeefcf1c1a548ee97e23aa Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:31 +0100 Subject: target/arm: Correct typo in HAMAIR1 regdef name We implement the HAMAIR1 register as RAZ/WI; we had a typo in the regdef, though, and were incorrectly naming it HMAIR1 (which is a different register which we also implement as RAZ/WI). Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-2-peter.maydell@linaro.org --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a312408..3e20467 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3773,7 +3773,7 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 0, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, + { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -3925,7 +3925,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, /* HAMAIR1 is mapped to AMAIR_EL2[63:32] */ - { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, + { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, -- cgit v1.1 From b5ede85bfb7ba1a8f6086494c82f400b29969f65 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:31 +0100 Subject: target/arm: Add missing .cp = 15 to HMAIR1 and HAMAIR1 regdefs ARMCPRegInfo structs will default to .cp = 15 if they are ARM_CP_STATE_BOTH, but not if they are ARM_CP_STATE_AA32 (because a coprocessor number of 0 is valid for AArch32). We forgot to explicitly set .cp = 15 for the HMAIR1 and HAMAIR1 regdefs, which meant they would UNDEF when the guest tried to access them under cp15. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-3-peter.maydell@linaro.org --- target/arm/helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3e20467..a68577a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3767,14 +3767,14 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 0, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH, @@ -3917,7 +3917,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]), .resetvalue = 0 }, { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) }, { .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH, @@ -3926,7 +3926,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .resetvalue = 0 }, /* HAMAIR1 is mapped to AMAIR_EL2[63:32] */ { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH, -- cgit v1.1 From d79e0c0608899428281a17c414ccf1a82d86ab85 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: target/arm: Implement AArch32 HVBAR Implement the AArch32 HVBAR register; we can do this just by making the existing VBAR_EL2 regdefs be STATE_BOTH. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-5-peter.maydell@linaro.org --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a68577a..274fb21 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3750,7 +3750,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { /* Used to describe the behaviour of EL2 regs when EL2 does not exist. */ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { - { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, @@ -3899,7 +3899,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, - { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[2]), -- cgit v1.1 From cba517c31e7df8932c4473c477a0f01d8a0adc48 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: target/arm: Implement AArch32 Hyp FARs The AArch32 virtualization extensions support these fault address registers: * HDFAR: aliased with AArch64 FAR_EL2[31:0] and AArch32 DFAR(S) * HIFAR: aliased with AArch64 FAR_EL2[63:32] and AArch32 IFAR(S) Implement the accessors for these. This fixes in passing a bug where we weren't implementing the "RES0 from EL3 if EL2 not implemented" behaviour for AArch64 FAR_EL2. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-7-peter.maydell@linaro.org --- target/arm/helper.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 274fb21..bb08bd4 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3843,6 +3843,13 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HIFAR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_CONST, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, + .access = PL2_RW, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -3891,9 +3898,14 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "ESR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, - { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, + { .name = "HIFAR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, + .access = PL2_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, -- cgit v1.1 From 68e78e332cb1c3f8b0317a0443acb2b5e190f0dd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: target/arm: Implement ESR_EL2/HSR for AArch32 and no-EL2 The AArch32 HSR is the equivalent of AArch64 ESR_EL2; we can implement it by marking our existing ESR_EL2 regdef as STATE_BOTH. It also needs to be "RES0 from EL3 if EL2 not implemented", so add the missing stanza to el3_no_el2_cp_reginfo. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-8-peter.maydell@linaro.org --- target/arm/helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index bb08bd4..c9bce1e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3759,6 +3759,10 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, + { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, + .access = PL2_RW, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -3895,7 +3899,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, - { .name = "ESR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, -- cgit v1.1 From aec4dd09f172ee64c19222b78269d5952fd9c1dc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: target/arm: Permit accesses to ELR_Hyp from Hyp mode via MSR/MRS (banked) The MSR (banked) and MRS (banked) instructions allow accesses to ELR_Hyp from either Monitor or Hyp mode. Our translate time check was overly strict and only permitted access from Monitor mode. The runtime check we do in msr_mrs_banked_exc_checks() had the correct code in it, but never got there because of the earlier "currmode == tgtmode" check. Special case ELR_Hyp. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-9-peter.maydell@linaro.org --- target/arm/op_helper.c | 22 +++++++++++----------- target/arm/translate.c | 10 +++++++--- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index d550978..952b8d1 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -611,6 +611,14 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, */ int curmode = env->uncached_cpsr & CPSR_M; + if (regno == 17) { + /* ELR_Hyp: a special case because access from tgtmode is OK */ + if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { + goto undef; + } + return; + } + if (curmode == tgtmode) { goto undef; } @@ -638,17 +646,9 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, } if (tgtmode == ARM_CPU_MODE_HYP) { - switch (regno) { - case 17: /* ELR_Hyp */ - if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { - goto undef; - } - break; - default: - if (curmode != ARM_CPU_MODE_MON) { - goto undef; - } - break; + /* SPSR_Hyp, r13_hyp: accessible from Monitor mode only */ + if (curmode != ARM_CPU_MODE_MON) { + goto undef; } } diff --git a/target/arm/translate.c b/target/arm/translate.c index 14aa2cd..8405c08 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -4506,10 +4506,14 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, } break; case ARM_CPU_MODE_HYP: - /* Note that we can forbid accesses from EL2 here because they - * must be from Hyp mode itself + /* + * SPSR_hyp and r13_hyp can only be accessed from Monitor mode + * (and so we can forbid accesses from EL2 or below). elr_hyp + * can be accessed also from Hyp mode, so forbid accesses from + * EL0 or EL1. */ - if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) { + if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 || + (s->current_el < 3 && *regno != 17)) { goto undef; } break; -- cgit v1.1 From 55c544ed2709bd202e71e77ddfe3ea0327852211 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: target/arm: Implement AArch32 ERET instruction ARMv7VE introduced the ERET instruction, which is necessary to return from an exception taken to Hyp mode. Implement this. In A32 encoding it is a completely new encoding; in T32 it is an adjustment of the behaviour of the existing "SUBS PC, LR, #" instruction. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Luc Michel Message-id: 20180814124254.5229-10-peter.maydell@linaro.org --- target/arm/translate.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 8405c08..bcfc29c 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -8901,6 +8901,25 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; + case 0x6: /* ERET */ + if (op1 != 3) { + goto illegal_op; + } + if (!arm_dc_feature(s, ARM_FEATURE_V7VE)) { + goto illegal_op; + } + if ((insn & 0x000fff0f) != 0x0000000e) { + /* UNPREDICTABLE; we choose to UNDEF */ + goto illegal_op; + } + + if (s->current_el == 2) { + tmp = load_cpu_field(elr_el[2]); + } else { + tmp = load_reg(s, 14); + } + gen_exception_return(s, tmp); + break; case 7: { int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); @@ -11158,8 +11177,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (rn != 14 || rd != 15) { goto illegal_op; } - tmp = load_reg(s, rn); - tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + if (s->current_el == 2) { + /* ERET from Hyp uses ELR_Hyp, not LR */ + if (insn & 0xff) { + goto illegal_op; + } + tmp = load_cpu_field(elr_el[2]); + } else { + tmp = load_reg(s, rn); + tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + } gen_exception_return(s, tmp); break; case 6: /* MRS */ -- cgit v1.1 From 22907d2b81c4702c0d226ad1364605dc9adac232 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: hw/arm/virt: Add virt-3.1 machine type Signed-off-by: Andrew Jones Reviewed-by: Igor Mammedov Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0807be9..0b57f87 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1791,10 +1791,7 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -#define VIRT_COMPAT_2_12 \ - HW_COMPAT_2_12 - -static void virt_3_0_instance_init(Object *obj) +static void virt_3_1_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); @@ -1864,10 +1861,24 @@ static void virt_3_0_instance_init(Object *obj) vms->irqmap = a15irqmap; } +static void virt_machine_3_1_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(3, 1) + +static void virt_3_0_instance_init(Object *obj) +{ + virt_3_1_instance_init(obj); +} + static void virt_machine_3_0_options(MachineClass *mc) { + virt_machine_3_1_options(mc); } -DEFINE_VIRT_MACHINE_AS_LATEST(3, 0) +DEFINE_VIRT_MACHINE(3, 0) + +#define VIRT_COMPAT_2_12 \ + HW_COMPAT_2_12 static void virt_2_12_instance_init(Object *obj) { -- cgit v1.1 From 6bfd06da6e3b293c9d7f561ea6afc857267c9ecd Mon Sep 17 00:00:00 2001 From: Hans-Erik Floryd Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: sdhci: add i.MX SD Stable Clock bit Add the ESDHC PRSSTAT_SDSTB bit, using the value of SDHC_CLOCK_INT_STABLE. Freescale recommends checking this bit when changing clock frequency. Signed-off-by: Hans-Erik Floryd Message-id: 1534507843-4251-1-git-send-email-hans-erik.floryd@rt-labs.com [PMM: fixed indentation] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sd/sdhci-internal.h | 2 ++ hw/sd/sdhci.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index 756ef3f..19665fd 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -302,4 +302,6 @@ extern const VMStateDescription sdhci_vmstate; #define ESDHC_CTRL_4BITBUS (0x1 << 1) #define ESDHC_CTRL_8BITBUS (0x2 << 1) +#define ESDHC_PRNSTS_SDSTB (1 << 3) + #endif diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 8f58c31..81bbf03 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1651,6 +1651,14 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size) break; + case SDHC_PRNSTS: + /* Add SDSTB (SD Clock Stable) bit to PRNSTS */ + ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB; + if (s->clkcon & SDHC_CLOCK_INT_STABLE) { + ret |= ESDHC_PRNSTS_SDSTB; + } + break; + case ESDHC_DLL_CTRL: case ESDHC_TUNE_CTRL_STATUS: case ESDHC_UNDOCUMENTED_REG27: -- cgit v1.1 From 5321fa68fdb7e285d60d8050057129027a8c1c63 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: hw/ssi/xilinx_spips: Remove unneeded MMIO request_ptr code We now support direct execution from MMIO regions in the core memory subsystem. This means that we don't need to have device-specific support for it, and we can remove the request_ptr handling from the Xilinx SPIPS device. (It was broken anyway due to race conditions, and disabled by default.) This device is the only in-tree user of this API. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Alistair Francis Reviewed-by: KONRAD Frederic Message-id: 20180817114619.22354-2-peter.maydell@linaro.org --- hw/ssi/xilinx_spips.c | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index c052bfc..16f88f7 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1031,14 +1031,6 @@ static const MemoryRegionOps spips_ops = { static void xilinx_qspips_invalidate_mmio_ptr(XilinxQSPIPS *q) { - XilinxSPIPS *s = &q->parent_obj; - - if ((q->mmio_execution_enabled) && (q->lqspi_cached_addr != ~0ULL)) { - /* Invalidate the current mapped mmio */ - memory_region_invalidate_mmio_ptr(&s->mmlqspi, q->lqspi_cached_addr, - LQSPI_CACHE_SIZE); - } - q->lqspi_cached_addr = ~0ULL; } @@ -1207,23 +1199,6 @@ static void lqspi_load_cache(void *opaque, hwaddr addr) } } -static void *lqspi_request_mmio_ptr(void *opaque, hwaddr addr, unsigned *size, - unsigned *offset) -{ - XilinxQSPIPS *q = opaque; - hwaddr offset_within_the_region; - - if (!q->mmio_execution_enabled) { - return NULL; - } - - offset_within_the_region = addr & ~(LQSPI_CACHE_SIZE - 1); - lqspi_load_cache(opaque, offset_within_the_region); - *size = LQSPI_CACHE_SIZE; - *offset = offset_within_the_region; - return q->lqspi_buf; -} - static uint64_t lqspi_read(void *opaque, hwaddr addr, unsigned int size) { @@ -1245,7 +1220,6 @@ lqspi_read(void *opaque, hwaddr addr, unsigned int size) static const MemoryRegionOps lqspi_ops = { .read = lqspi_read, - .request_ptr = lqspi_request_mmio_ptr, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 1, @@ -1322,15 +1296,6 @@ static void xilinx_qspips_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->mmlqspi); q->lqspi_cached_addr = ~0ULL; - - /* mmio_execution breaks migration better aborting than having strange - * bugs. - */ - if (q->mmio_execution_enabled) { - error_setg(&q->migration_blocker, - "enabling mmio_execution breaks migration"); - migrate_add_blocker(q->migration_blocker, &error_fatal); - } } static void xlnx_zynqmp_qspips_realize(DeviceState *dev, Error **errp) @@ -1427,16 +1392,6 @@ static Property xilinx_zynqmp_qspips_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static Property xilinx_qspips_properties[] = { - /* We had to turn this off for 2.10 as it is not compatible with migration. - * It can be enabled but will prevent the device to be migrated. - * This will go aways when a fix will be released. - */ - DEFINE_PROP_BOOL("x-mmio-exec", XilinxQSPIPS, mmio_execution_enabled, - false), - DEFINE_PROP_END_OF_LIST(), -}; - static Property xilinx_spips_properties[] = { DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4), @@ -1450,7 +1405,6 @@ static void xilinx_qspips_class_init(ObjectClass *klass, void * data) XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); dc->realize = xilinx_qspips_realize; - dc->props = xilinx_qspips_properties; xsc->reg_ops = &qspips_ops; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; -- cgit v1.1 From 8c1c245378f646c18ee03a24154bd611e6abe52e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: memory: Remove MMIO request_ptr APIs Remove the obsolete MMIO request_ptr APIs; they have no users now. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Alistair Francis Reviewed-by: KONRAD Frederic Message-id: 20180817114619.22354-3-peter.maydell@linaro.org --- include/exec/memory.h | 35 ---------------- memory.c | 110 -------------------------------------------------- 2 files changed, 145 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 448d41a..6863656 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -141,15 +141,6 @@ struct MemoryRegionOps { uint64_t data, unsigned size, MemTxAttrs attrs); - /* Instruction execution pre-callback: - * @addr is the address of the access relative to the @mr. - * @size is the size of the area returned by the callback. - * @offset is the location of the pointer inside @mr. - * - * Returns a pointer to a location which contains guest code. - */ - void *(*request_ptr)(void *opaque, hwaddr addr, unsigned *size, - unsigned *offset); enum device_endian endianness; /* Guest-visible constraints: */ @@ -1668,32 +1659,6 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview, bool dispatch_tree, bool owner); /** - * memory_region_request_mmio_ptr: request a pointer to an mmio - * MemoryRegion. If it is possible map a RAM MemoryRegion with this pointer. - * When the device wants to invalidate the pointer it will call - * memory_region_invalidate_mmio_ptr. - * - * @mr: #MemoryRegion to check - * @addr: address within that region - * - * Returns true on success, false otherwise. - */ -bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr); - -/** - * memory_region_invalidate_mmio_ptr: invalidate the pointer to an mmio - * previously requested. - * In the end that means that if something wants to execute from this area it - * will need to request the pointer again. - * - * @mr: #MemoryRegion associated to the pointer. - * @offset: offset within the memory region - * @size: size of that area. - */ -void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset, - unsigned size); - -/** * memory_region_dispatch_read: perform a read directly to the specified * MemoryRegion. * diff --git a/memory.c b/memory.c index 2ea16e7..8b44672 100644 --- a/memory.c +++ b/memory.c @@ -29,7 +29,6 @@ #include "exec/ram_addr.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" -#include "hw/misc/mmio_interface.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -2680,115 +2679,6 @@ void memory_listener_unregister(MemoryListener *listener) listener->address_space = NULL; } -bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr) -{ - void *host; - unsigned size = 0; - unsigned offset = 0; - Object *new_interface; - - if (!mr || !mr->ops->request_ptr) { - return false; - } - - /* - * Avoid an update if the request_ptr call - * memory_region_invalidate_mmio_ptr which seems to be likely when we use - * a cache. - */ - memory_region_transaction_begin(); - - host = mr->ops->request_ptr(mr->opaque, addr - mr->addr, &size, &offset); - - if (!host || !size) { - memory_region_transaction_commit(); - return false; - } - - new_interface = object_new("mmio_interface"); - qdev_prop_set_uint64(DEVICE(new_interface), "start", offset); - qdev_prop_set_uint64(DEVICE(new_interface), "end", offset + size - 1); - qdev_prop_set_bit(DEVICE(new_interface), "ro", true); - qdev_prop_set_ptr(DEVICE(new_interface), "host_ptr", host); - qdev_prop_set_ptr(DEVICE(new_interface), "subregion", mr); - object_property_set_bool(OBJECT(new_interface), true, "realized", NULL); - - memory_region_transaction_commit(); - return true; -} - -typedef struct MMIOPtrInvalidate { - MemoryRegion *mr; - hwaddr offset; - unsigned size; - int busy; - int allocated; -} MMIOPtrInvalidate; - -#define MAX_MMIO_INVALIDATE 10 -static MMIOPtrInvalidate mmio_ptr_invalidate_list[MAX_MMIO_INVALIDATE]; - -static void memory_region_do_invalidate_mmio_ptr(CPUState *cpu, - run_on_cpu_data data) -{ - MMIOPtrInvalidate *invalidate_data = (MMIOPtrInvalidate *)data.host_ptr; - MemoryRegion *mr = invalidate_data->mr; - hwaddr offset = invalidate_data->offset; - unsigned size = invalidate_data->size; - MemoryRegionSection section = memory_region_find(mr, offset, size); - - qemu_mutex_lock_iothread(); - - /* Reset dirty so this doesn't happen later. */ - cpu_physical_memory_test_and_clear_dirty(offset, size, 1); - - if (section.mr != mr) { - /* memory_region_find add a ref on section.mr */ - memory_region_unref(section.mr); - if (MMIO_INTERFACE(section.mr->owner)) { - /* We found the interface just drop it. */ - object_property_set_bool(section.mr->owner, false, "realized", - NULL); - object_unref(section.mr->owner); - object_unparent(section.mr->owner); - } - } - - qemu_mutex_unlock_iothread(); - - if (invalidate_data->allocated) { - g_free(invalidate_data); - } else { - invalidate_data->busy = 0; - } -} - -void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset, - unsigned size) -{ - size_t i; - MMIOPtrInvalidate *invalidate_data = NULL; - - for (i = 0; i < MAX_MMIO_INVALIDATE; i++) { - if (atomic_cmpxchg(&(mmio_ptr_invalidate_list[i].busy), 0, 1) == 0) { - invalidate_data = &mmio_ptr_invalidate_list[i]; - break; - } - } - - if (!invalidate_data) { - invalidate_data = g_malloc0(sizeof(MMIOPtrInvalidate)); - invalidate_data->allocated = 1; - } - - invalidate_data->mr = mr; - invalidate_data->offset = offset; - invalidate_data->size = size; - - async_safe_run_on_cpu(first_cpu, memory_region_do_invalidate_mmio_ptr, - RUN_ON_CPU_HOST_PTR(invalidate_data)); -} - void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) { memory_region_ref(root); -- cgit v1.1 From 3eff40dbf44896a8180c86c84dbdefb2eb173fbe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:32 +0100 Subject: hw/misc: Remove mmio_interface device The mmio_interface device was a purely internal artifact of the implementation of the memory subsystem's request_ptr APIs. Now that we have removed those APIs, we can remove the mmio_interface device too. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Alistair Francis Reviewed-by: KONRAD Frederic Message-id: 20180817114619.22354-4-peter.maydell@linaro.org --- hw/misc/Makefile.objs | 1 - hw/misc/mmio_interface.c | 135 --------------------------------------- include/hw/misc/mmio_interface.h | 49 -------------- 3 files changed, 185 deletions(-) delete mode 100644 hw/misc/mmio_interface.c delete mode 100644 include/hw/misc/mmio_interface.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 51d27b3..22714b0 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -71,5 +71,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o obj-$(CONFIG_AUX) += auxbus.o obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o -obj-y += mmio_interface.o obj-$(CONFIG_MSF2) += msf2-sysreg.o diff --git a/hw/misc/mmio_interface.c b/hw/misc/mmio_interface.c deleted file mode 100644 index 3b0e203..0000000 --- a/hw/misc/mmio_interface.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * mmio_interface.c - * - * Copyright (C) 2017 : GreenSocs - * http://www.greensocs.com/ , email: info@greensocs.com - * - * Developed by : - * Frederic Konrad - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option)any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "trace.h" -#include "hw/qdev-properties.h" -#include "hw/misc/mmio_interface.h" -#include "qapi/error.h" - -#ifndef DEBUG_MMIO_INTERFACE -#define DEBUG_MMIO_INTERFACE 0 -#endif - -static uint64_t mmio_interface_counter; - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_MMIO_INTERFACE) { \ - qemu_log("mmio_interface: 0x%" PRIX64 ": " fmt, s->id, ## __VA_ARGS__);\ - } \ -} while (0) - -static void mmio_interface_init(Object *obj) -{ - MMIOInterface *s = MMIO_INTERFACE(obj); - - if (DEBUG_MMIO_INTERFACE) { - s->id = mmio_interface_counter++; - } - - DPRINTF("interface created\n"); - s->host_ptr = 0; - s->subregion = 0; -} - -static void mmio_interface_realize(DeviceState *dev, Error **errp) -{ - MMIOInterface *s = MMIO_INTERFACE(dev); - - DPRINTF("realize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" - " %p\n", s->start, s->end, s->host_ptr); - - if (!s->host_ptr) { - error_setg(errp, "host_ptr property must be set"); - return; - } - - if (!s->subregion) { - error_setg(errp, "subregion property must be set"); - return; - } - - memory_region_init_ram_ptr(&s->ram_mem, OBJECT(s), "ram", - s->end - s->start + 1, s->host_ptr); - memory_region_set_readonly(&s->ram_mem, s->ro); - memory_region_add_subregion(s->subregion, s->start, &s->ram_mem); -} - -static void mmio_interface_unrealize(DeviceState *dev, Error **errp) -{ - MMIOInterface *s = MMIO_INTERFACE(dev); - - DPRINTF("unrealize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" - " %p\n", s->start, s->end, s->host_ptr); - memory_region_del_subregion(s->subregion, &s->ram_mem); -} - -static void mmio_interface_finalize(Object *obj) -{ - MMIOInterface *s = MMIO_INTERFACE(obj); - - DPRINTF("finalize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" - " %p\n", s->start, s->end, s->host_ptr); - object_unparent(OBJECT(&s->ram_mem)); -} - -static Property mmio_interface_properties[] = { - DEFINE_PROP_UINT64("start", MMIOInterface, start, 0), - DEFINE_PROP_UINT64("end", MMIOInterface, end, 0), - DEFINE_PROP_PTR("host_ptr", MMIOInterface, host_ptr), - DEFINE_PROP_BOOL("ro", MMIOInterface, ro, false), - DEFINE_PROP_MEMORY_REGION("subregion", MMIOInterface, subregion), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mmio_interface_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = mmio_interface_realize; - dc->unrealize = mmio_interface_unrealize; - dc->props = mmio_interface_properties; - /* Reason: pointer property "host_ptr", and this device - * is an implementation detail of the memory subsystem, - * not intended to be created directly by the user. - */ - dc->user_creatable = false; -} - -static const TypeInfo mmio_interface_info = { - .name = TYPE_MMIO_INTERFACE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(MMIOInterface), - .instance_init = mmio_interface_init, - .instance_finalize = mmio_interface_finalize, - .class_init = mmio_interface_class_init, -}; - -static void mmio_interface_register_types(void) -{ - type_register_static(&mmio_interface_info); -} - -type_init(mmio_interface_register_types) diff --git a/include/hw/misc/mmio_interface.h b/include/hw/misc/mmio_interface.h deleted file mode 100644 index 90d34fb..0000000 --- a/include/hw/misc/mmio_interface.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * mmio_interface.h - * - * Copyright (C) 2017 : GreenSocs - * http://www.greensocs.com/ , email: info@greensocs.com - * - * Developed by : - * Frederic Konrad - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option)any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#ifndef MMIO_INTERFACE_H -#define MMIO_INTERFACE_H - -#include "exec/memory.h" - -#define TYPE_MMIO_INTERFACE "mmio_interface" -#define MMIO_INTERFACE(obj) OBJECT_CHECK(MMIOInterface, (obj), \ - TYPE_MMIO_INTERFACE) - -typedef struct MMIOInterface { - DeviceState parent_obj; - - MemoryRegion *subregion; - MemoryRegion ram_mem; - uint64_t start; - uint64_t end; - bool ro; - uint64_t id; - void *host_ptr; -} MMIOInterface; - -void mmio_interface_map(MMIOInterface *s); -void mmio_interface_unmap(MMIOInterface *s); - -#endif /* MMIO_INTERFACE_H */ -- cgit v1.1 From 62b9cf0a77b4dd5571a948d798d4604f4ae6bcd5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/timer/m48t59: Move away from old_mmio accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the m48t59 device away from using old_mmio MemoryRegionOps accessors. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Tested-by: Mark Cave-Ayland Message-id: 20180802180602.22047-1-peter.maydell@linaro.org --- hw/timer/m48t59.c | 59 +++++++++++-------------------------------------------- 1 file changed, 11 insertions(+), 48 deletions(-) diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index f299176..ca3ed44 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -493,66 +493,29 @@ static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) return retval; } -static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, value & 0xff); -} - -static void nvram_writew (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, (value >> 8) & 0xff); - m48t59_write(NVRAM, addr + 1, value & 0xff); -} - -static void nvram_writel (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, (value >> 24) & 0xff); - m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff); - m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff); - m48t59_write(NVRAM, addr + 3, value & 0xff); -} - -static uint32_t nvram_readb (void *opaque, hwaddr addr) +static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size) { M48t59State *NVRAM = opaque; return m48t59_read(NVRAM, addr); } -static uint32_t nvram_readw (void *opaque, hwaddr addr) -{ - M48t59State *NVRAM = opaque; - uint32_t retval; - - retval = m48t59_read(NVRAM, addr) << 8; - retval |= m48t59_read(NVRAM, addr + 1); - return retval; -} - -static uint32_t nvram_readl (void *opaque, hwaddr addr) +static void nvram_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { M48t59State *NVRAM = opaque; - uint32_t retval; - retval = m48t59_read(NVRAM, addr) << 24; - retval |= m48t59_read(NVRAM, addr + 1) << 16; - retval |= m48t59_read(NVRAM, addr + 2) << 8; - retval |= m48t59_read(NVRAM, addr + 3); - return retval; + return m48t59_write(NVRAM, addr, value); } static const MemoryRegionOps nvram_ops = { - .old_mmio = { - .read = { nvram_readb, nvram_readw, nvram_readl, }, - .write = { nvram_writeb, nvram_writew, nvram_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, + .read = nvram_read, + .write = nvram_write, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, }; static const VMStateDescription vmstate_m48t59 = { -- cgit v1.1 From 050c2ea07b18b77c7562512a2b73e86e48c5c4b7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/watchdog/cmsdk_apb_watchdog: Implement CMSDK APB watchdog module The Arm Cortex-M System Design Kit includes a simple watchdog module based on a 32-bit down-counter. Implement this. Signed-off-by: Peter Maydell --- MAINTAINERS | 2 + Makefile.objs | 1 + default-configs/arm-softmmu.mak | 1 + hw/watchdog/Makefile.objs | 1 + hw/watchdog/cmsdk-apb-watchdog.c | 326 +++++++++++++++++++++++++++++++ hw/watchdog/trace-events | 6 + include/hw/watchdog/cmsdk-apb-watchdog.h | 59 ++++++ 7 files changed, 396 insertions(+) create mode 100644 hw/watchdog/cmsdk-apb-watchdog.c create mode 100644 hw/watchdog/trace-events create mode 100644 include/hw/watchdog/cmsdk-apb-watchdog.h diff --git a/MAINTAINERS b/MAINTAINERS index 70651f7..6698a7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -456,6 +456,8 @@ F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h +F: hw/watchdog/cmsdk-apb-watchdog.c +F: include/hw/watchdog/cmsdk-apb-watchdog.h F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h F: hw/misc/tz-mpc.c diff --git a/Makefile.objs b/Makefile.objs index 7a9828d..ce9c792 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -240,6 +240,7 @@ trace-events-subdirs += hw/tpm trace-events-subdirs += hw/usb trace-events-subdirs += hw/vfio trace-events-subdirs += hw/virtio +trace-events-subdirs += hw/watchdog trace-events-subdirs += hw/xen trace-events-subdirs += io trace-events-subdirs += linux-user diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 311584f..a92bc34 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -104,6 +104,7 @@ CONFIG_STM32F205_SOC=y CONFIG_CMSDK_APB_TIMER=y CONFIG_CMSDK_APB_UART=y +CONFIG_CMSDK_APB_WATCHDOG=y CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs index 9589bed..3f536d1 100644 --- a/hw/watchdog/Makefile.objs +++ b/hw/watchdog/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += watchdog.o +common-obj-$(CONFIG_CMSDK_APB_WATCHDOG) += cmsdk-apb-watchdog.o common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c new file mode 100644 index 0000000..eb79a73 --- /dev/null +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -0,0 +1,326 @@ +/* + * ARM CMSDK APB watchdog emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB watchdog" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "sysemu/watchdog.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" + +REG32(WDOGLOAD, 0x0) +REG32(WDOGVALUE, 0x4) +REG32(WDOGCONTROL, 0x8) + FIELD(WDOGCONTROL, INTEN, 0, 1) + FIELD(WDOGCONTROL, RESEN, 1, 1) +#define R_WDOGCONTROL_VALID_MASK (R_WDOGCONTROL_INTEN_MASK | \ + R_WDOGCONTROL_RESEN_MASK) +REG32(WDOGINTCLR, 0xc) +REG32(WDOGRIS, 0x10) + FIELD(WDOGRIS, INT, 0, 1) +REG32(WDOGMIS, 0x14) +REG32(WDOGLOCK, 0xc00) +#define WDOG_UNLOCK_VALUE 0x1ACCE551 +REG32(WDOGITCR, 0xf00) + FIELD(WDOGITCR, ENABLE, 0, 1) +#define R_WDOGITCR_VALID_MASK R_WDOGITCR_ENABLE_MASK +REG32(WDOGITOP, 0xf04) + FIELD(WDOGITOP, WDOGRES, 0, 1) + FIELD(WDOGITOP, WDOGINT, 1, 1) +#define R_WDOGITOP_VALID_MASK (R_WDOGITOP_WDOGRES_MASK | \ + R_WDOGITOP_WDOGINT_MASK) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int watchdog_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x24, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static bool cmsdk_apb_watchdog_intstatus(CMSDKAPBWatchdog *s) +{ + /* Return masked interrupt status */ + return s->intstatus && (s->control & R_WDOGCONTROL_INTEN_MASK); +} + +static bool cmsdk_apb_watchdog_resetstatus(CMSDKAPBWatchdog *s) +{ + /* Return masked reset status */ + return s->resetstatus && (s->control & R_WDOGCONTROL_RESEN_MASK); +} + +static void cmsdk_apb_watchdog_update(CMSDKAPBWatchdog *s) +{ + bool wdogint; + bool wdogres; + + if (s->itcr) { + wdogint = s->itop & R_WDOGITOP_WDOGINT_MASK; + wdogres = s->itop & R_WDOGITOP_WDOGRES_MASK; + } else { + wdogint = cmsdk_apb_watchdog_intstatus(s); + wdogres = cmsdk_apb_watchdog_resetstatus(s); + } + + qemu_set_irq(s->wdogint, wdogint); + if (wdogres) { + watchdog_perform_action(); + } +} + +static uint64_t cmsdk_apb_watchdog_read(void *opaque, hwaddr offset, + unsigned size) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque); + uint64_t r; + + switch (offset) { + case A_WDOGLOAD: + r = ptimer_get_limit(s->timer); + break; + case A_WDOGVALUE: + r = ptimer_get_count(s->timer); + break; + case A_WDOGCONTROL: + r = s->control; + break; + case A_WDOGRIS: + r = s->intstatus; + break; + case A_WDOGMIS: + r = cmsdk_apb_watchdog_intstatus(s); + break; + case A_WDOGLOCK: + r = s->lock; + break; + case A_WDOGITCR: + r = s->itcr; + break; + case A_PID4 ... A_CID3: + r = watchdog_id[(offset - A_PID4) / 4]; + break; + case A_WDOGINTCLR: + case A_WDOGITOP: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog read: read of WO offset %x\n", + (int)offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_cmsdk_apb_watchdog_read(offset, r, size); + return r; +} + +static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque); + + trace_cmsdk_apb_watchdog_write(offset, value, size); + + if (s->lock && offset != A_WDOGLOCK) { + /* Write access is disabled via WDOGLOCK */ + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog write: write to locked watchdog\n"); + return; + } + + switch (offset) { + case A_WDOGLOAD: + /* + * Reset the load value and the current count, and make sure + * we're counting. + */ + ptimer_set_limit(s->timer, value, 1); + ptimer_run(s->timer, 0); + break; + case A_WDOGCONTROL: + s->control = value & R_WDOGCONTROL_VALID_MASK; + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGINTCLR: + s->intstatus = 0; + ptimer_set_count(s->timer, ptimer_get_limit(s->timer)); + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGLOCK: + s->lock = (value != WDOG_UNLOCK_VALUE); + break; + case A_WDOGITCR: + s->itcr = value & R_WDOGITCR_VALID_MASK; + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGITOP: + s->itop = value & R_WDOGITOP_VALID_MASK; + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGVALUE: + case A_WDOGRIS: + case A_WDOGMIS: + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog write: write to RO offset 0x%x\n", + (int)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog write: bad offset 0x%x\n", + (int)offset); + break; + } +} + +static const MemoryRegionOps cmsdk_apb_watchdog_ops = { + .read = cmsdk_apb_watchdog_read, + .write = cmsdk_apb_watchdog_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void cmsdk_apb_watchdog_tick(void *opaque) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque); + + if (!s->intstatus) { + /* Count expired for the first time: raise interrupt */ + s->intstatus = R_WDOGRIS_INT_MASK; + } else { + /* Count expired for the second time: raise reset and stop clock */ + s->resetstatus = 1; + ptimer_stop(s->timer); + } + cmsdk_apb_watchdog_update(s); +} + +static void cmsdk_apb_watchdog_reset(DeviceState *dev) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev); + + trace_cmsdk_apb_watchdog_reset(); + s->control = 0; + s->intstatus = 0; + s->lock = 0; + s->itcr = 0; + s->itop = 0; + s->resetstatus = 0; + /* Set the limit and the count */ + ptimer_set_limit(s->timer, 0xffffffff, 1); + ptimer_run(s->timer, 0); +} + +static void cmsdk_apb_watchdog_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj); + + memory_region_init_io(&s->iomem, obj, &cmsdk_apb_watchdog_ops, + s, "cmsdk-apb-watchdog", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->wdogint); +} + +static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev); + QEMUBH *bh; + + if (s->wdogclk_frq == 0) { + error_setg(errp, + "CMSDK APB watchdog: wdogclk-frq property must be set"); + return; + } + + bh = qemu_bh_new(cmsdk_apb_watchdog_tick, s); + s->timer = ptimer_init(bh, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + ptimer_set_freq(s->timer, s->wdogclk_frq); +} + +static const VMStateDescription cmsdk_apb_watchdog_vmstate = { + .name = "cmsdk-apb-watchdog", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(timer, CMSDKAPBWatchdog), + VMSTATE_UINT32(control, CMSDKAPBWatchdog), + VMSTATE_UINT32(intstatus, CMSDKAPBWatchdog), + VMSTATE_UINT32(lock, CMSDKAPBWatchdog), + VMSTATE_UINT32(itcr, CMSDKAPBWatchdog), + VMSTATE_UINT32(itop, CMSDKAPBWatchdog), + VMSTATE_UINT32(resetstatus, CMSDKAPBWatchdog), + VMSTATE_END_OF_LIST() + } +}; + +static Property cmsdk_apb_watchdog_properties[] = { + DEFINE_PROP_UINT32("wdogclk-frq", CMSDKAPBWatchdog, wdogclk_frq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cmsdk_apb_watchdog_realize; + dc->vmsd = &cmsdk_apb_watchdog_vmstate; + dc->reset = cmsdk_apb_watchdog_reset; + dc->props = cmsdk_apb_watchdog_properties; +} + +static const TypeInfo cmsdk_apb_watchdog_info = { + .name = TYPE_CMSDK_APB_WATCHDOG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBWatchdog), + .instance_init = cmsdk_apb_watchdog_init, + .class_init = cmsdk_apb_watchdog_class_init, +}; + +static void cmsdk_apb_watchdog_register_types(void) +{ + type_register_static(&cmsdk_apb_watchdog_info); +} + +type_init(cmsdk_apb_watchdog_register_types); diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events new file mode 100644 index 0000000..fee9584 --- /dev/null +++ b/hw/watchdog/trace-events @@ -0,0 +1,6 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/char/cmsdk_apb_watchdog.c +cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" diff --git a/include/hw/watchdog/cmsdk-apb-watchdog.h b/include/hw/watchdog/cmsdk-apb-watchdog.h new file mode 100644 index 0000000..ab8b598 --- /dev/null +++ b/include/hw/watchdog/cmsdk-apb-watchdog.h @@ -0,0 +1,59 @@ +/* + * ARM CMSDK APB watchdog emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB watchdog" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * + * QEMU interface: + * + QOM property "wdogclk-frq": frequency at which the watchdog is clocked + * + sysbus MMIO region 0: the register bank + * + sysbus IRQ 0: watchdog interrupt + * + * In real hardware the watchdog's reset output is just a GPIO line + * which can then be masked by the board or treated as a simple interrupt. + * (For instance the IoTKit does this with the non-secure watchdog, so that + * secure code can control whether non-secure code can perform a system + * reset via its watchdog.) In QEMU, we just wire up the watchdog reset + * to watchdog_perform_action(), at least for the moment. + */ + +#ifndef CMSDK_APB_WATCHDOG_H +#define CMSDK_APB_WATCHDOG_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" + +#define TYPE_CMSDK_APB_WATCHDOG "cmsdk-apb-watchdog" +#define CMSDK_APB_WATCHDOG(obj) OBJECT_CHECK(CMSDKAPBWatchdog, (obj), \ + TYPE_CMSDK_APB_WATCHDOG) + +typedef struct CMSDKAPBWatchdog { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq wdogint; + uint32_t wdogclk_frq; + struct ptimer_state *timer; + + uint32_t control; + uint32_t intstatus; + uint32_t lock; + uint32_t itcr; + uint32_t itop; + uint32_t resetstatus; +} CMSDKAPBWatchdog; + +#endif -- cgit v1.1 From 514b4f361b65c49303877829561faad5e1d2463c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: nvic: Expose NMI line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On real v7M hardware, the NMI line is an externally visible signal that an SoC or board can toggle to assert an NMI. Expose it in our QEMU NVIC and armv7m container objects so that a board model can wire it up if it needs to. In particular, the MPS2 watchdog is wired to NMI. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/arm/armv7m.c | 1 + hw/intc/armv7m_nvic.c | 19 +++++++++++++++++++ hw/intc/trace-events | 1 + 3 files changed, 21 insertions(+) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 8786139..4bf9131 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -202,6 +202,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp) */ qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL); qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ"); + qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI"); /* Wire the NVIC up to the CPU */ sbd = SYS_BUS_DEVICE(&s->nvic); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 351b69a..0d816fd 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -774,6 +774,24 @@ static void set_irq_level(void *opaque, int n, int level) } } +/* callback when external NMI line is changed */ +static void nvic_nmi_trigger(void *opaque, int n, int level) +{ + NVICState *s = opaque; + + trace_nvic_set_nmi_level(level); + + /* + * The architecture doesn't specify whether NMI should share + * the normal-interrupt behaviour of being resampled on + * exception handler return. We choose not to, so just + * set NMI pending here and don't track the current level. + */ + if (level) { + armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI, false); + } +} + static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) { ARMCPU *cpu = s->cpu; @@ -2382,6 +2400,7 @@ static void armv7m_nvic_instance_init(Object *obj) qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1); qdev_init_gpio_in_named(dev, nvic_systick_trigger, "systick-trigger", M_REG_NUM_BANKS); + qdev_init_gpio_in_named(dev, nvic_nmi_trigger, "NMI", 1); } static void armv7m_nvic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 81c7c39..7769869 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -192,6 +192,7 @@ nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (pr nvic_get_pending_irq_info(int irq, bool secure) "NVIC next IRQ %d: targets_secure: %d" nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)" nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d" +nvic_set_nmi_level(int level) "NVIC external NMI level set to %d" nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" -- cgit v1.1 From aa74e355f12438888eacdd8f3c2fc910b1b621a7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/dma/pl080: Allow use as embedded-struct device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new include file for the pl081's device struct, type macros, etc, so that it can be instantiated using the "embedded struct" coding style. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + hw/dma/pl080.c | 34 ++------------------------- include/hw/dma/pl080.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 include/hw/dma/pl080.h diff --git a/MAINTAINERS b/MAINTAINERS index 6698a7e..6902a56 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -445,6 +445,7 @@ F: hw/char/pl011.c F: include/hw/char/pl011.h F: hw/display/pl110* F: hw/dma/pl080.c +F: include/hw/dma/pl080.h F: hw/dma/pl330.c F: hw/gpio/pl061.c F: hw/input/pl050.c diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 7724c93..0f79c2d 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -11,8 +11,8 @@ #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "qemu/log.h" +#include "hw/dma/pl080.h" -#define PL080_MAX_CHANNELS 8 #define PL080_CONF_E 0x1 #define PL080_CONF_M1 0x2 #define PL080_CONF_M2 0x4 @@ -30,36 +30,6 @@ #define PL080_CCTRL_D 0x02000000 #define PL080_CCTRL_S 0x01000000 -typedef struct { - uint32_t src; - uint32_t dest; - uint32_t lli; - uint32_t ctrl; - uint32_t conf; -} pl080_channel; - -#define TYPE_PL080 "pl080" -#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080) - -typedef struct PL080State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint8_t tc_int; - uint8_t tc_mask; - uint8_t err_int; - uint8_t err_mask; - uint32_t conf; - uint32_t sync; - uint32_t req_single; - uint32_t req_burst; - pl080_channel chan[PL080_MAX_CHANNELS]; - int nchannels; - /* Flag to avoid recursive DMA invocations. */ - int running; - qemu_irq irq; -} PL080State; - static const VMStateDescription vmstate_pl080_channel = { .name = "pl080_channel", .version_id = 1, @@ -408,7 +378,7 @@ static const TypeInfo pl080_info = { }; static const TypeInfo pl081_info = { - .name = "pl081", + .name = TYPE_PL081, .parent = TYPE_PL080, .instance_init = pl081_init, }; diff --git a/include/hw/dma/pl080.h b/include/hw/dma/pl080.h new file mode 100644 index 0000000..7deb46c --- /dev/null +++ b/include/hw/dma/pl080.h @@ -0,0 +1,62 @@ +/* + * ARM PrimeCell PL080/PL081 DMA controller + * + * Copyright (c) 2006 CodeSourcery. + * Copyright (c) 2018 Linaro Limited + * Written by Paul Brook, Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the Arm PrimeCell PL080/PL081 DMA controller: + * The PL080 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0196g/DDI0196.pdf + * and the PL081 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0218e/DDI0218.pdf + * + * QEMU interface: + * + sysbus IRQ: DMACINTR combined interrupt line + * + sysbus MMIO region 0: MemoryRegion for the device's registers + */ + +#ifndef HW_DMA_PL080_H +#define HW_DMA_PL080_H + +#include "hw/sysbus.h" + +#define PL080_MAX_CHANNELS 8 + +typedef struct { + uint32_t src; + uint32_t dest; + uint32_t lli; + uint32_t ctrl; + uint32_t conf; +} pl080_channel; + +#define TYPE_PL080 "pl080" +#define TYPE_PL081 "pl081" +#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080) + +typedef struct PL080State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint8_t tc_int; + uint8_t tc_mask; + uint8_t err_int; + uint8_t err_mask; + uint32_t conf; + uint32_t sync; + uint32_t req_single; + uint32_t req_burst; + pl080_channel chan[PL080_MAX_CHANNELS]; + int nchannels; + /* Flag to avoid recursive DMA invocations. */ + int running; + qemu_irq irq; +} PL080State; + +#endif -- cgit v1.1 From 6d0ed6ba6c1aa0201326d4f0072e91983495bb52 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/dma/pl080: Support all three interrupt lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PL080 and PL081 have three outgoing interrupt lines: * DMACINTERR signals DMA errors * DMACINTTC is the DMA count interrupt * DMACINTR is a combined interrupt, the logical OR of the other two We currently only implement DMACINTR, because that's all the realview and versatile boards needed, but the instances of the PL081 in the MPS2 firmware images use all three interrupt lines. Implement the missing DMACINTERR and DMACINTTC. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/dma/pl080.c | 13 ++++++++----- include/hw/dma/pl080.h | 6 +++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 0f79c2d..301030d 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -75,11 +75,12 @@ static const unsigned char pl081_id[] = static void pl080_update(PL080State *s) { - if ((s->tc_int & s->tc_mask) - || (s->err_int & s->err_mask)) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); + bool tclevel = (s->tc_int & s->tc_mask); + bool errlevel = (s->err_int & s->err_mask); + + qemu_set_irq(s->interr, errlevel); + qemu_set_irq(s->inttc, tclevel); + qemu_set_irq(s->irq, errlevel || tclevel); } static void pl080_run(PL080State *s) @@ -352,6 +353,8 @@ static void pl080_init(Object *obj) memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->interr); + sysbus_init_irq(sbd, &s->inttc); s->nchannels = 8; } diff --git a/include/hw/dma/pl080.h b/include/hw/dma/pl080.h index 7deb46c..7c6a418 100644 --- a/include/hw/dma/pl080.h +++ b/include/hw/dma/pl080.h @@ -17,7 +17,9 @@ * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0218e/DDI0218.pdf * * QEMU interface: - * + sysbus IRQ: DMACINTR combined interrupt line + * + sysbus IRQ 0: DMACINTR combined interrupt line + * + sysbus IRQ 1: DMACINTERR error interrupt request + * + sysbus IRQ 2: DMACINTTC count interrupt request * + sysbus MMIO region 0: MemoryRegion for the device's registers */ @@ -57,6 +59,8 @@ typedef struct PL080State { /* Flag to avoid recursive DMA invocations. */ int running; qemu_irq irq; + qemu_irq interr; + qemu_irq inttc; } PL080State; #endif -- cgit v1.1 From 112a829f8f0add64f73bfea66c153355ea596da9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/dma/pl080: Don't use CPU address space for DMA accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently our PL080/PL081 model uses a combination of the CPU's address space (via cpu_physical_memory_{read,write}()) and the system address space for performing DMA accesses. For the PL081s in the MPS FPGA images, their DMA accesses must go via Master Security Controllers. Switch the PL080/PL081 model to take a MemoryRegion property which defines its downstream for making DMA accesses. Since the PL08x are only used in two board models, we make provision of the 'downstream' link mandatory and convert both users at once, rather than having it be optional with a default to the system address space. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/arm/realview.c | 8 +++++++- hw/arm/versatilepb.c | 9 ++++++++- hw/dma/pl080.c | 35 +++++++++++++++++++++++++++++------ include/hw/dma/pl080.h | 5 +++++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/hw/arm/realview.c b/hw/arm/realview.c index cd585d9..ab8c14f 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -201,7 +201,13 @@ static void realview_init(MachineState *machine, pl011_create(0x1000c000, pic[15], serial_hd(3)); /* DMA controller is optional, apparently. */ - sysbus_create_simple("pl081", 0x10030000, pic[24]); + dev = qdev_create(NULL, "pl081"); + object_property_set_link(OBJECT(dev), OBJECT(sysmem), "downstream", + &error_fatal); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, 0x10030000); + sysbus_connect_irq(busdev, 0, pic[24]); sysbus_create_simple("sp804", 0x10011000, pic[4]); sysbus_create_simple("sp804", 0x10012000, pic[5]); diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index a5a06b6..8b74857 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -287,7 +287,14 @@ static void versatile_init(MachineState *machine, int board_id) pl011_create(0x101f3000, pic[14], serial_hd(2)); pl011_create(0x10009000, sic[6], serial_hd(3)); - sysbus_create_simple("pl080", 0x10130000, pic[17]); + dev = qdev_create(NULL, "pl080"); + object_property_set_link(OBJECT(dev), OBJECT(sysmem), "downstream", + &error_fatal); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, 0x10130000); + sysbus_connect_irq(busdev, 0, pic[17]); + sysbus_create_simple("sp804", 0x101e2000, pic[4]); sysbus_create_simple("sp804", 0x101e3000, pic[5]); diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 301030d..8f9f3e0 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -12,6 +12,7 @@ #include "exec/address-spaces.h" #include "qemu/log.h" #include "hw/dma/pl080.h" +#include "qapi/error.h" #define PL080_CONF_E 0x1 #define PL080_CONF_M1 0x2 @@ -161,14 +162,16 @@ again: swidth = 1 << ((ch->ctrl >> 18) & 7); dwidth = 1 << ((ch->ctrl >> 21) & 7); for (n = 0; n < dwidth; n+= swidth) { - cpu_physical_memory_read(ch->src, buff + n, swidth); + address_space_read(&s->downstream_as, ch->src, + MEMTXATTRS_UNSPECIFIED, buff + n, swidth); if (ch->ctrl & PL080_CCTRL_SI) ch->src += swidth; } xsize = (dwidth < swidth) ? swidth : dwidth; /* ??? This may pad the value incorrectly for dwidth < 32. */ for (n = 0; n < xsize; n += dwidth) { - cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); + address_space_write(&s->downstream_as, ch->dest + n, + MEMTXATTRS_UNSPECIFIED, buff + n, dwidth); if (ch->ctrl & PL080_CCTRL_DI) ch->dest += swidth; } @@ -178,19 +181,19 @@ again: if (size == 0) { /* Transfer complete. */ if (ch->lli) { - ch->src = address_space_ldl_le(&address_space_memory, + ch->src = address_space_ldl_le(&s->downstream_as, ch->lli, MEMTXATTRS_UNSPECIFIED, NULL); - ch->dest = address_space_ldl_le(&address_space_memory, + ch->dest = address_space_ldl_le(&s->downstream_as, ch->lli + 4, MEMTXATTRS_UNSPECIFIED, NULL); - ch->ctrl = address_space_ldl_le(&address_space_memory, + ch->ctrl = address_space_ldl_le(&s->downstream_as, ch->lli + 12, MEMTXATTRS_UNSPECIFIED, NULL); - ch->lli = address_space_ldl_le(&address_space_memory, + ch->lli = address_space_ldl_le(&s->downstream_as, ch->lli + 8, MEMTXATTRS_UNSPECIFIED, NULL); @@ -358,6 +361,18 @@ static void pl080_init(Object *obj) s->nchannels = 8; } +static void pl080_realize(DeviceState *dev, Error **errp) +{ + PL080State *s = PL080(dev); + + if (!s->downstream) { + error_setg(errp, "PL080 'downstream' link not set"); + return; + } + + address_space_init(&s->downstream_as, s->downstream, "pl080-downstream"); +} + static void pl081_init(Object *obj) { PL080State *s = PL080(obj); @@ -365,11 +380,19 @@ static void pl081_init(Object *obj) s->nchannels = 2; } +static Property pl080_properties[] = { + DEFINE_PROP_LINK("downstream", PL080State, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pl080_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->vmsd = &vmstate_pl080; + dc->realize = pl080_realize; + dc->props = pl080_properties; } static const TypeInfo pl080_info = { diff --git a/include/hw/dma/pl080.h b/include/hw/dma/pl080.h index 7c6a418..9d4b3df 100644 --- a/include/hw/dma/pl080.h +++ b/include/hw/dma/pl080.h @@ -21,6 +21,8 @@ * + sysbus IRQ 1: DMACINTERR error interrupt request * + sysbus IRQ 2: DMACINTTC count interrupt request * + sysbus MMIO region 0: MemoryRegion for the device's registers + * + QOM property "downstream": MemoryRegion defining where DMA + * bus master transactions are made */ #ifndef HW_DMA_PL080_H @@ -61,6 +63,9 @@ typedef struct PL080State { qemu_irq irq; qemu_irq interr; qemu_irq inttc; + + MemoryRegion *downstream; + AddressSpace downstream_as; } PL080State; #endif -- cgit v1.1 From c193304d4f9ef21c51ff412f6279ad459eedd438 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/dma/pl080: Provide device reset function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PL080/PL081 model is missing a reset function; implement it. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/dma/pl080.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 8f9f3e0..a7aacad 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -348,6 +348,30 @@ static const MemoryRegionOps pl080_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void pl080_reset(DeviceState *dev) +{ + PL080State *s = PL080(dev); + int i; + + s->tc_int = 0; + s->tc_mask = 0; + s->err_int = 0; + s->err_mask = 0; + s->conf = 0; + s->sync = 0; + s->req_single = 0; + s->req_burst = 0; + s->running = 0; + + for (i = 0; i < s->nchannels; i++) { + s->chan[i].src = 0; + s->chan[i].dest = 0; + s->chan[i].lli = 0; + s->chan[i].ctrl = 0; + s->chan[i].conf = 0; + } +} + static void pl080_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -393,6 +417,7 @@ static void pl080_class_init(ObjectClass *oc, void *data) dc->vmsd = &vmstate_pl080; dc->realize = pl080_realize; dc->props = pl080_properties; + dc->reset = pl080_reset; } static const TypeInfo pl080_info = { -- cgit v1.1 From 156448ab640baaeca185787eb303fe4d63edca26 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/dma/pl080: Correct bug in register address decode logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bug in the handling of the register address decode logic for the PL08x meant that we were incorrectly treating accesses to the DMA channel registers (DMACCxSrcAddr, DMACCxDestaddr, DMACCxLLI, DMACCxControl, DMACCxConfiguration) as bad offsets. Fix this long-standing bug. Fixes: https://bugs.launchpad.net/qemu/+bug/1637974 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/dma/pl080.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index a7aacad..8f92550 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -229,7 +229,7 @@ static uint64_t pl080_read(void *opaque, hwaddr offset, i = (offset & 0xe0) >> 5; if (i >= s->nchannels) goto bad_offset; - switch (offset >> 2) { + switch ((offset >> 2) & 7) { case 0: /* SrcAddr */ return s->chan[i].src; case 1: /* DestAddr */ @@ -290,7 +290,7 @@ static void pl080_write(void *opaque, hwaddr offset, i = (offset & 0xe0) >> 5; if (i >= s->nchannels) goto bad_offset; - switch (offset >> 2) { + switch ((offset >> 2) & 7) { case 0: /* SrcAddr */ s->chan[i].src = value; break; @@ -308,6 +308,7 @@ static void pl080_write(void *opaque, hwaddr offset, pl080_run(s); break; } + return; } switch (offset >> 2) { case 2: /* IntTCClear */ -- cgit v1.1 From b85fad1588e812566f897f747e38da345a7016d6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Aug 2018 11:24:33 +0100 Subject: hw/dma/pl080: Remove hw_error() if DMA is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PL08x model currently will unconditionally call hw_error() if the DMA engine is enabled by the guest. This has been present since the PL080 model was edded in 2006, and is presumably either unintentional debug code left enabled, or a guard against untested DMA engine code being used. Remove the hw_error(), since we now have a guest which will actually try to use the DMA engine (the self-test binary for the AN505 MPS2 FPGA image). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/dma/pl080.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 8f92550..ef15d3e 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -110,7 +110,6 @@ static void pl080_run(PL080State *s) if ((s->conf & PL080_CONF_E) == 0) return; -hw_error("DMA active\n"); /* If we are already in the middle of a DMA operation then indicate that there may be new DMA requests and return immediately. */ if (s->running) { -- cgit v1.1