From 86131c71b13257e095d8c4f4453d52cbc6553c07 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 16 Apr 2021 17:49:36 +0200 Subject: [PATCH 1/9] target/s390x: Fix translation exception on illegal instruction Hitting an uretprobe in a s390x TCG guest causes a SIGSEGV. What happens is: * uretprobe maps a userspace page containing an invalid instruction. * uretprobe replaces the target function's return address with the address of that page. * When tb_gen_code() is called on that page, tb->size ends up being 0 (because the page starts with the invalid instruction), which causes virt_page2 to point to the previous page. * The previous page is not mapped, so this causes a spurious translation exception. tb->size must never be 0: even if there is an illegal instruction, the instruction bytes that have been looked at must count towards tb->size. So adjust s390x's translate_one() to act this way for both illegal instructions and instructions that are known to generate exceptions. Signed-off-by: Ilya Leoshkevich Reviewed-by: David Hildenbrand Message-Id: <20210416154939.32404-2-iii@linux.ibm.com> Signed-off-by: Cornelia Huck --- target/s390x/translate.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 4f953ddfba..e243624d2a 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -6412,7 +6412,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n", s->fields.op, s->fields.op2); gen_illegal_opcode(s); - return DISAS_NORETURN; + ret = DISAS_NORETURN; + goto out; } #ifndef CONFIG_USER_ONLY @@ -6428,7 +6429,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* privileged instruction */ if ((s->base.tb->flags & FLAG_MASK_PSTATE) && (insn->flags & IF_PRIV)) { gen_program_exception(s, PGM_PRIVILEGED); - return DISAS_NORETURN; + ret = DISAS_NORETURN; + goto out; } /* if AFP is not enabled, instructions and registers are forbidden */ @@ -6455,7 +6457,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } if (dxc) { gen_data_exception(dxc); - return DISAS_NORETURN; + ret = DISAS_NORETURN; + goto out; } } @@ -6463,7 +6466,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) if (insn->flags & IF_VEC) { if (!((s->base.tb->flags & FLAG_MASK_VECTOR))) { gen_data_exception(0xfe); - return DISAS_NORETURN; + ret = DISAS_NORETURN; + goto out; } } @@ -6484,7 +6488,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) (insn->spec & SPEC_r1_f128 && !is_fp_pair(get_field(s, r1))) || (insn->spec & SPEC_r2_f128 && !is_fp_pair(get_field(s, r2)))) { gen_program_exception(s, PGM_SPECIFICATION); - return DISAS_NORETURN; + ret = DISAS_NORETURN; + goto out; } } @@ -6544,6 +6549,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #endif +out: /* Advance to the next instruction. */ s->base.pc_next = s->pc_tmp; return ret; From 48a130923c59b706e7f33527490028eb8a86b97e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 16 Apr 2021 17:49:37 +0200 Subject: [PATCH 2/9] target/arm: Make sure that commpage's tb->size != 0 tb_gen_code() assumes that tb->size must never be zero, otherwise it may produce spurious exceptions. For ARM this may happen when creating a translation block for the commpage. Fix by pretending that commpage translation blocks have at least one instruction. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Message-Id: <20210416154939.32404-3-iii@linux.ibm.com> Signed-off-by: Cornelia Huck --- target/arm/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/translate.c b/target/arm/translate.c index 455352bcf6..8e0e55c1e0 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -8981,6 +8981,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) unsigned int insn; if (arm_pre_translate_insn(dc)) { + dc->base.pc_next += 4; return; } @@ -9050,6 +9051,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) bool is_16bit; if (arm_pre_translate_insn(dc)) { + dc->base.pc_next += 2; return; } From f689befde664f917510e842660a69961faf8ba7b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 16 Apr 2021 17:49:38 +0200 Subject: [PATCH 3/9] target/xtensa: Make sure that tb->size != 0 tb_gen_code() assumes that tb->size must never be zero, otherwise it may produce spurious exceptions. For xtensa this may happen when decoding an unknown instruction, when handling a write into the CCOUNT or CCOMPARE special register and when single-stepping the first instruction of an exception handler. Fix by pretending that the size of the respective translation block is 1 in all these cases. Signed-off-by: Ilya Leoshkevich Tested-by: Max Filippov Acked-by: Max Filippov Message-Id: <20210416154939.32404-4-iii@linux.ibm.com> Signed-off-by: Cornelia Huck --- target/xtensa/translate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 0ae4efc48a..73584d9d60 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -917,6 +917,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) "unknown instruction length (pc = %08x)\n", dc->pc); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); + dc->base.pc_next = dc->pc + 1; return; } @@ -1274,11 +1275,13 @@ static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) && (dc->base.tb->flags & XTENSA_TBFLAG_YIELD)) { gen_exception(dc, EXCP_YIELD); + dc->base.pc_next = dc->pc + 1; dc->base.is_jmp = DISAS_NORETURN; return; } if (dc->base.tb->flags & XTENSA_TBFLAG_EXCEPTION) { gen_exception(dc, EXCP_DEBUG); + dc->base.pc_next = dc->pc + 1; dc->base.is_jmp = DISAS_NORETURN; return; } From 0b00b0c1e05b34904635cf1b5cfdd945d1a8475e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 16 Apr 2021 17:49:39 +0200 Subject: [PATCH 4/9] accel/tcg: Assert that tb->size != 0 after translation If arch-specific code generates a translation block of size 0, tb_gen_code() may generate a spurious exception. Add an assertion in order to catch such situations early. Signed-off-by: Ilya Leoshkevich Reviewed-by: David Hildenbrand Message-Id: <20210416154939.32404-5-iii@linux.ibm.com> Signed-off-by: Cornelia Huck --- accel/tcg/translate-all.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index fbf8fc630b..640ff6e3e7 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1912,6 +1912,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->cpu = env_cpu(env); gen_intermediate_code(cpu, tb, max_insns); + assert(tb->size != 0); tcg_ctx->cpu = NULL; max_insns = tb->icount; From 6178d4689a1e6a0d2b6dea1dad990e74148fa9d1 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Wed, 21 Apr 2021 17:20:53 +0200 Subject: [PATCH 5/9] vfio-ccw: Permit missing IRQs Commit 690e29b91102 ("vfio-ccw: Refactor ccw irq handler") changed one of the checks for the IRQ notifier registration from saying "the host needs to recognize the only IRQ that exists" to saying "the host needs to recognize ANY IRQ that exists." And this worked fine, because the subsequent change to support the CRW IRQ notifier doesn't get into this code when running on an older kernel, thanks to a guard by a capability region. The later addition of the REQ(uest) IRQ by commit b2f96f9e4f5f ("vfio-ccw: Connect the device request notifier") broke this assumption because there is no matching capability region. Thus, running new QEMU on an older kernel fails with: vfio: unexpected number of irqs 2 Let's adapt the message here so that there's a better clue of what IRQ is missing. Furthermore, let's make the REQ(uest) IRQ not fail when attempting to register it, to permit running vfio-ccw on a newer QEMU with an older kernel. Fixes: b2f96f9e4f5f ("vfio-ccw: Connect the device request notifier") Signed-off-by: Eric Farman Message-Id: <20210421152053.2379873-1-farman@linux.ibm.com> Signed-off-by: Cornelia Huck --- hw/vfio/ccw.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index e752c845e9..7c058d13e8 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -411,8 +411,8 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, } if (vdev->num_irqs < irq + 1) { - error_setg(errp, "vfio: unexpected number of irqs %u", - vdev->num_irqs); + error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", + irq, vdev->num_irqs); return; } @@ -695,13 +695,15 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err); if (err) { - goto out_req_notifier_err; + /* + * Report this error, but do not make it a failing condition. + * Lack of this IRQ in the host does not prevent normal operation. + */ + error_report_err(err); } return; -out_req_notifier_err: - vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); out_crw_notifier_err: vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); out_io_notifier_err: From a6d8b731130bf76c3d2932b067befa5c380e0dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 24 Apr 2021 16:53:13 +0200 Subject: [PATCH 6/9] hw/s390x/ccw: Register qbus type in abstract TYPE_CCW_DEVICE parent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having all TYPE_CCW_DEVICE children set the bus type to TYPE_VIRTUAL_CSS_BUS, do it once in the abstract parent. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Eric Farman Message-Id: <20210424145313.3287400-1-f4bug@amsat.org> Signed-off-by: Cornelia Huck --- hw/s390x/3270-ccw.c | 1 - hw/s390x/ccw-device.c | 1 + hw/s390x/ccw-device.h | 1 + hw/s390x/s390-ccw.c | 2 -- hw/s390x/virtio-ccw.c | 1 - 5 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index 25e628f575..13e93d8d8f 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -158,7 +158,6 @@ static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, emulated_ccw_3270_properties); - dc->bus_type = TYPE_VIRTUAL_CSS_BUS; dc->realize = emulated_ccw_3270_realize; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index c9707110e9..95f269ab44 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -59,6 +59,7 @@ static void ccw_device_class_init(ObjectClass *klass, void *data) k->refill_ids = ccw_device_refill_ids; device_class_set_props(dc, ccw_device_properties); dc->reset = ccw_device_reset; + dc->bus_type = TYPE_VIRTUAL_CSS_BUS; } const VMStateDescription vmstate_ccw_dev = { diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 832c78cd42..6dff95225d 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -14,6 +14,7 @@ #include "qom/object.h" #include "hw/qdev-core.h" #include "hw/s390x/css.h" +#include "hw/s390x/css-bridge.h" struct CcwDevice { DeviceState parent_obj; diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 242491a1ae..c227c77984 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -176,10 +176,8 @@ static void s390_ccw_instance_init(Object *obj) static void s390_ccw_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); - dc->bus_type = TYPE_VIRTUAL_CSS_BUS; cdc->realize = s390_ccw_realize; cdc->unrealize = s390_ccw_unrealize; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 92b950e09a..220b9efcf9 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1234,7 +1234,6 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->unrealize = virtio_ccw_busdev_unrealize; - dc->bus_type = TYPE_VIRTUAL_CSS_BUS; device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); } From dcc9cf3801039741b7a574b5035db283a7fed271 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Wed, 28 Apr 2021 16:36:52 +0200 Subject: [PATCH 7/9] vfio-ccw: Attempt to clean up all IRQs on error The vfio_ccw_unrealize() routine makes an unconditional attempt to unregister every IRQ notifier, though they may not have been registered in the first place (when running on an older kernel, for example). Let's mirror this behavior in the error cleanups in vfio_ccw_realize() so that if/when new IRQs are added, it is less confusing to recognize the necessary procedures. The worst case scenario would be some extra messages about an undefined IRQ, but since this is an error exit that won't be the only thing to worry about. And regarding those messages, let's change it to a warning instead of an error, to better reflect their severity. The existing code in both paths handles everything anyway. Signed-off-by: Eric Farman Acked-by: Matthew Rosato Message-Id: <20210428143652.1571487-1-farman@linux.ibm.com> Signed-off-by: Cornelia Huck --- hw/vfio/ccw.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 7c058d13e8..139a3d9d1b 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -469,7 +469,7 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); + warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } qemu_set_fd_handler(event_notifier_get_fd(notifier), @@ -689,7 +689,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) if (vcdev->crw_region) { vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); if (err) { - goto out_crw_notifier_err; + goto out_irq_notifier_err; } } @@ -704,7 +704,9 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) return; -out_crw_notifier_err: +out_irq_notifier_err: + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX); + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); out_io_notifier_err: vfio_ccw_put_region(vcdev); From 9b21049edd3c352efc615e030cd8e931e0c6f910 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 19 May 2021 06:57:37 +0200 Subject: [PATCH 8/9] target/i386: Make sure that vsyscall's tb->size != 0 tb_gen_code() assumes that tb->size must never be zero, otherwise it may produce spurious exceptions. For x86_64 this may happen when creating a translation block for the vsyscall page. Fix by pretending that vsyscall translation blocks have at least one instruction. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Message-Id: <20210519045738.1335210-2-iii@linux.ibm.com> Signed-off-by: Cornelia Huck --- target/i386/tcg/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index db56a48343..3ab8c23855 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -8583,6 +8583,7 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) */ if ((dc->base.pc_next & TARGET_PAGE_MASK) == TARGET_VSYSCALL_PAGE) { gen_exception(dc, EXCP_VSYSCALL, dc->base.pc_next); + dc->base.pc_next = dc->pc + 1; return; } #endif From f66487756b0553b156d8e3e81bc6411cfc38176e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 19 May 2021 06:57:38 +0200 Subject: [PATCH 9/9] tests/tcg/x86_64: add vsyscall smoke test Having a small test will prevent trivial regressions in the future. Signed-off-by: Ilya Leoshkevich Message-Id: <20210519045738.1335210-3-iii@linux.ibm.com> Reviewed-by: Richard Henderson Signed-off-by: Cornelia Huck --- tests/tcg/x86_64/Makefile.target | 6 +++++- tests/tcg/x86_64/vsyscall.c | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/x86_64/vsyscall.c diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index 20bf96202a..2151ea6302 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -3,14 +3,18 @@ # x86_64 tests - included from tests/tcg/Makefile.target # # Currently we only build test-x86_64 and test-i386-ssse3 from -# $(SRC)/tests/tcg/i386/ +# $(SRC_PATH)/tests/tcg/i386/ # include $(SRC_PATH)/tests/tcg/i386/Makefile.target +X86_64_TESTS += vsyscall TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 QEMU_OPTS += -cpu max test-x86_64: LDFLAGS+=-lm -lc test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + +vsyscall: $(SRC_PATH)/tests/tcg/x86_64/vsyscall.c + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) diff --git a/tests/tcg/x86_64/vsyscall.c b/tests/tcg/x86_64/vsyscall.c new file mode 100644 index 0000000000..786b047053 --- /dev/null +++ b/tests/tcg/x86_64/vsyscall.c @@ -0,0 +1,12 @@ +#include +#include + +#define VSYSCALL_PAGE 0xffffffffff600000 +#define TIME_OFFSET 0x400 +typedef time_t (*time_func)(time_t *); + +int main(void) +{ + printf("%ld\n", ((time_func)(VSYSCALL_PAGE + TIME_OFFSET))(NULL)); + return 0; +}