From patchwork Tue May 26 09:09:44 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aurelien Jarno X-Patchwork-Id: 476417 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id DF5091402B5 for ; Tue, 26 May 2015 19:22:51 +1000 (AEST) Received: from localhost ([::1]:46732 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YxB4T-0005Lx-SA for incoming@patchwork.ozlabs.org; Tue, 26 May 2015 05:22:49 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53817) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YxB47-0004nl-QG for qemu-devel@nongnu.org; Tue, 26 May 2015 05:22:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YxArv-0002jt-4X for qemu-devel@nongnu.org; Tue, 26 May 2015 05:10:27 -0400 Received: from hall.aurel32.net ([2001:bc8:30d7:101::1]:47716) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YxAru-0002jf-PN for qemu-devel@nongnu.org; Tue, 26 May 2015 05:09:50 -0400 Received: from weber.rr44.fr ([2001:470:d4ed:0:7e05:7ff:fe0d:f152]) by hall.aurel32.net with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84) (envelope-from ) id 1YxAru-0001Pv-1f; Tue, 26 May 2015 11:09:50 +0200 Received: from aurel32 by weber.rr44.fr with local (Exim 4.85) (envelope-from ) id 1YxArt-00008e-6z; Tue, 26 May 2015 11:09:49 +0200 From: Aurelien Jarno To: qemu-devel@nongnu.org Date: Tue, 26 May 2015 11:09:44 +0200 Message-Id: <1432631384-470-1-git-send-email-aurelien@aurel32.net> X-Mailer: git-send-email 2.1.4 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:bc8:30d7:101::1 Cc: Alexander Graf , Aurelien Jarno , Richard Henderson Subject: [Qemu-devel] [PATCH v2] target-s390x: fix LOAD MULTIPLE instruction on page boundary X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org When consecutive memory locations are on page boundary a page fault might occur when using the LOAD MULTIPLE instruction. In that case real hardware doesn't load any register. This is an important detail in case the base register is in the list of registers to be loaded. If a page fault occurs this register might be overwritten and when the instruction is later restarted the wrong base register value is useD. Fix this by first loading the first and last value from memory, hence triggering all possible page faults, and then the remaining registers. This fixes random segmentation faults seen in the guest. Cc: Alexander Graf Cc: Richard Henderson Signed-off-by: Aurelien Jarno Reviewed-by: Richard Henderson --- target-s390x/translate.c | 128 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 29 deletions(-) Changes v1->v2: - Do the load in two steps: first and last registers, and then the remaining ones diff --git a/target-s390x/translate.c b/target-s390x/translate.c index 1819217..23c78e7 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -2434,21 +2434,45 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - TCGv_i64 t = tcg_temp_new_i64(); - TCGv_i64 t4 = tcg_const_i64(4); + TCGv_i64 t1, t2; - while (1) { - tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s)); - store_reg32_i64(r1, t); - if (r1 == r3) { - break; - } - tcg_gen_add_i64(o->in2, o->in2, t4); + /* Only one register to read. */ + t1 = tcg_temp_new_i64(); + if (unlikely(r1 == r3)) { + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32_i64(r1, t1); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* First load the values of the first and last registers to trigger + possible page faults. */ + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15)); + tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s)); + store_reg32_i64(r1, t1); + store_reg32_i64(r3, t2); + + /* Only two registers to read. */ + if (((r1 + 1) & 15) == r3) { + tcg_temp_free(t2); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* Then load the remaining registers. Page fault can't occur. */ + r3 = (r3 - 1) & 15; + tcg_gen_movi_i64(t2, 4); + while (r1 != r3) { r1 = (r1 + 1) & 15; + tcg_gen_add_i64(o->in2, o->in2, t2); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32_i64(r1, t1); } + tcg_temp_free(t2); + tcg_temp_free(t1); - tcg_temp_free_i64(t); - tcg_temp_free_i64(t4); return NO_EXIT; } @@ -2456,21 +2480,45 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - TCGv_i64 t = tcg_temp_new_i64(); - TCGv_i64 t4 = tcg_const_i64(4); + TCGv_i64 t1, t2; - while (1) { - tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s)); - store_reg32h_i64(r1, t); - if (r1 == r3) { - break; - } - tcg_gen_add_i64(o->in2, o->in2, t4); + /* Only one register to read. */ + t1 = tcg_temp_new_i64(); + if (unlikely(r1 == r3)) { + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32h_i64(r1, t1); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* First load the values of the first and last registers to trigger + possible page faults. */ + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15)); + tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s)); + store_reg32h_i64(r1, t1); + store_reg32h_i64(r3, t2); + + /* Only two registers to read. */ + if (((r1 + 1) & 15) == r3) { + tcg_temp_free(t2); + tcg_temp_free(t1); + return NO_EXIT; + } + + /* Then load the remaining registers. Page fault can't occur. */ + r3 = (r3 - 1) & 15; + tcg_gen_movi_i64(t2, 4); + while (r1 != r3) { r1 = (r1 + 1) & 15; + tcg_gen_add_i64(o->in2, o->in2, t2); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + store_reg32h_i64(r1, t1); } + tcg_temp_free(t2); + tcg_temp_free(t1); - tcg_temp_free_i64(t); - tcg_temp_free_i64(t4); return NO_EXIT; } @@ -2478,18 +2526,40 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - TCGv_i64 t8 = tcg_const_i64(8); + TCGv_i64 t1, t2; - while (1) { + /* Only one register to read. */ + if (unlikely(r1 == r3)) { tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); - if (r1 == r3) { - break; - } - tcg_gen_add_i64(o->in2, o->in2, t8); + return NO_EXIT; + } + + /* First load the values of the first and last registers to trigger + possible page faults. */ + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(t2, o->in2, 8 * ((r3 - r1) & 15)); + tcg_gen_qemu_ld64(regs[r3], t2, get_mem_index(s)); + tcg_gen_mov_i64(regs[r1], t1); + tcg_temp_free(t2); + + /* Only two registers to read. */ + if (((r1 + 1) & 15) == r3) { + tcg_temp_free(t1); + return NO_EXIT; + } + + /* Then load the remaining registers. Page fault can't occur. */ + r3 = (r3 - 1) & 15; + tcg_gen_movi_i64(t1, 8); + while (r1 != r3) { r1 = (r1 + 1) & 15; + tcg_gen_add_i64(o->in2, o->in2, t1); + tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); } + tcg_temp_free(t1); - tcg_temp_free_i64(t8); return NO_EXIT; }