From nobody Sat Sep 26 20:52:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=linaro.org ARC-Seal: i=1; a=rsa-sha256; t=1789682886; cv=none; d=zohomail.com; s=zohoarc; b=XZZ4vYw/2c48BMGEqH/n3qRGaMKntUFsddH8w9JuRXRKMev+cH6geLV9Hkcj4+TlWyVs12OYTN0KnKK7tZwwnWY2zAj8z+ZxGntBbs+mT4gOFW2vHxLLRZM/ov+a17rruHnmdgDa0tKxYui14VboTJ/wctF7g+3+DwLve7NE5lo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1789682886; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=P7/TiDgWYks57A25lc75uXS1p0NFjUja6QcSsY8XHNQ=; b=XR/PvJRxtlNcpNd1c+zKNTJTtWuf+YoGJWcKJOn6DFg71zUusYk+marcsy+ciIIJsJ4Td9I0MGaO26S8I1Pop+0OOifRBfHI4ji767udxQQPu0UhxQ3r/HdzA6wYiOATIOnpbTcmhLmu3D6jpr7jAJXaCBIAvDsNfRlvVlg/wfY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1789682886022537.9681291150766; Thu, 17 Sep 2026 15:08:06 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x7KGP-0001oo-SS; Thu, 17 Sep 2026 18:07:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x7KGM-0001oL-Ax for qemu-devel@nongnu.org; Thu, 17 Sep 2026 18:07:27 -0400 Received: from mail-pj2-x0e.google.com ([2607:f8b0:4864:39::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1x7KGK-0006H2-PE for qemu-devel@nongnu.org; Thu, 17 Sep 2026 18:07:26 -0400 Received: by mail-pj2-x0e.google.com with SMTP id 98e67ed59e1d1-39b350c69b4so112708a91.2 for ; Thu, 17 Sep 2026 15:07:24 -0700 (PDT) Received: from stoup ([2603:800c:7900:ed00:5a4b:653b:1dce:569f]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-14395d3a31asm14558131c88.9.2026.09.17.15.07.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 17 Sep 2026 15:07:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1789682843; x=1790287643; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=P7/TiDgWYks57A25lc75uXS1p0NFjUja6QcSsY8XHNQ=; b=OnSShqsirurCEEDLVPv/6+AzuGBgWRv1G1DzgpfJtmFu0jHM2dHrqvsv+iHxRaadQo YuXZCJpGDkeVj9AMm0EiUcdPiJaCz7M4VU28w0Tfx/uEDPicZExtx4xv3hX+4jZ2iEw2 HXD+5kZX/QtQJcEGDL6SnOzNkZRYXiP5O4vT1jVS0rpdNm/n4/rmElkHzrOuQMdgbanD 4yxKkgqM8d1LJ5ZhOKyiegUA8Z10aKhBs+S9AZmNx4Z5MRZltWTioYICj3wj0WcU922y 42Uod4MlSeBGmRV0cjAvKPmDAjg+zJQyLLhc9osZeIdXx6d9+3hoyFgdYlZr++fb/6Wv H2+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789682843; x=1790287643; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=P7/TiDgWYks57A25lc75uXS1p0NFjUja6QcSsY8XHNQ=; b=EdQakFMFybBhXHs5/65Vtllosyn5PlJ2hTMWhctSIH94FBFnMwS7ZmZyVj9kIKQZLt MEvkkdzRiF6lhxE6Jl8h1B/6ZSDRhTIg+4+F9lViXy1FYrghFwXT7YlIZd+XJOM9Du/8 4eavuF1SToHFJWJYd1DVIuC30saYjZPpO6TeCiywOe2CkIRiPN4Fztqt0bKtMX5mfIW9 6Qjv1gRE+7WnnaUM1875JKCiQUoX27DMk1HJ5kd+XgvXuoFBGBk1w+gy8lAEmi2WRRUa r4AJB6JxA/gPQRsy3PlYVkKt7yr1WwJhf+scHuPHo2XfnXDt0A/MP9xAK4wB58ZrFYsH PYCQ== X-Gm-Message-State: AFuF++m2Hztq5S0tiPUIKJoZkJ07NkL4IF2pRYzntCqbvrooecfiCcrC JQIbCaHjAF9myG+u7u+8DwS5cuREEG34zX/p8HlFDPV+zjw6IgZzAppMXs8U+yoVyD9B4VRyldN grtNK X-Gm-Gg: AYBFou2dCKTipM+7cGoiPQWZRfWO4PAQJilc+9iUhYGWRdaNTCd4/QfjPLq7mUFFvMs /rv6D1ZkzZLVH45/FG+srtGRBq42dU6JZIpwrL7U3DviOBODVIq4AwwWKenBlnrf1xojgQmaL3/ X0XZNapU+DqG41nUO3w3C4J9FBbtNUGzlM3S3tuCoYg4Dq70bOdFVV9nIaU7NtJrRfPqoTQQwKk pzK9JdVXXRRigi9CZNyY1qJEhyVdLcNDd9684lNO4ScT8Mx4p1A5kkbuPiVVdCtKCtU+aqzP0pd W80QvzpakBfHgxW68hU/JXFQUEAPwOtmAxIlSTkkOI+xRGrs/paPICXECRt61pecR7wWbNQde6h mKEE4m8ElpHuKUqwW6Z+VqrzRGCnJL9J/wWEbb7leAlx9NKdev/pp845BS0JOIUjp+2c4jeR+GL yNLCRB7kyigxPa8YK9Abp7WQYsbQBRCVlfXhKOzwXnPLiSjS9EKTzyaStIerYjWfJ5W7AxKWUzw VL/I4I= X-Received: by 2002:a17:90b:2dc3:b0:39e:3ce1:d22f with SMTP id 98e67ed59e1d1-39e54d3674bmr927647a91.16.1789682843213; Thu, 17 Sep 2026 15:07:23 -0700 (PDT) From: Richard Henderson To: qemu-devel@nongnu.org Cc: mattst88@gmail.com, Pierrick Bouvier Subject: [PATCH v6 1/2] tests/guest-debug/run-test: Add --pargs Date: Thu, 17 Sep 2026 12:07:17 -1000 Message-ID: <20260917220718.60899-2-richard.henderson@linaro.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260917220718.60899-1-richard.henderson@linaro.org> References: <20260917220718.60899-1-richard.henderson@linaro.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:39::e; envelope-from=richard.henderson@linaro.org; helo=mail-pj2-x0e.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1789682886698158500 Content-Type: text/plain; charset="utf-8" Add --pargs to pass command-line arguments to the user-mode program. Signed-off-by: Richard Henderson Reviewed-by: Matt Turner --- Cc: Pierrick Bouvier --- tests/guest-debug/run-test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index 75e9c92e036..a592a69168b 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -24,6 +24,7 @@ def get_args(): parser.add_argument("--qemu", help=3D"Qemu binary for test", required=3DTrue) parser.add_argument("--qargs", help=3D"Qemu arguments for test") + parser.add_argument("--pargs", help=3D"Program arguments for test") parser.add_argument("--binary", help=3D"Binary to debug", required=3DTrue) parser.add_argument("--test", help=3D"GDB test script") @@ -88,6 +89,8 @@ def log(output, msg): suspend =3D '' cmd =3D f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \ f' {args.binary}' + if args.pargs: + cmd +=3D f' {args.pargs}' =20 log(output, "QEMU CMD: %s" % (cmd)) inferior =3D subprocess.Popen(shlex.split(cmd)) --=20 2.53.0 From nobody Sat Sep 26 20:52:33 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=linaro.org ARC-Seal: i=1; a=rsa-sha256; t=1789682886; cv=none; d=zohomail.com; s=zohoarc; b=bQhyoL6nLNHThEp/YHkO5DciDhkmUjoBOMTRtUOKyB7ilns5k+mSoZ/1mud28sNNiv7lFTj+TwKtGAOKzQGKNVDZA1MNzwzYn6faSFT7M8uNBV5lHBk6C5/4c8RjNHMY7ELD9wo4pKN/iZQEjSx7sy/Dkj/dM5OofTMF94Nrv+I= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1789682886; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=9mNCAIqDOepKwDLnim2xcE5EzzCaL66TpsgNkeIyCS4=; b=Bp/BpI79T5F8yw3zN8ZOKhuvQxTUV8dg2MR5W2D+CtvuPXpvS0iUvgkfiuZAH4W0DTWckONb+lyC75Warh/8RWys51f/DSOh28WeTC7L5mYWkM+Bk1xnKtLxF7d7l4rdZN7so6TI7j687mgUlnbcaKpikP8Dd/SroYT+u7yQQNA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17896828864991006.9917639157139; Thu, 17 Sep 2026 15:08:06 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x7KGY-0001pb-BC; Thu, 17 Sep 2026 18:07:38 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x7KGP-0001oh-AC for qemu-devel@nongnu.org; Thu, 17 Sep 2026 18:07:29 -0400 Received: from mail-pz2-x1d.google.com ([2607:f8b0:4864:3b::1d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1x7KGM-0006HB-3F for qemu-devel@nongnu.org; Thu, 17 Sep 2026 18:07:29 -0400 Received: by mail-pz2-x1d.google.com with SMTP id 41be03b00d2f7-cc1cebad4aeso15799a12.2 for ; Thu, 17 Sep 2026 15:07:25 -0700 (PDT) Received: from stoup ([2603:800c:7900:ed00:5a4b:653b:1dce:569f]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-14395d3a31asm14558131c88.9.2026.09.17.15.07.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 17 Sep 2026 15:07:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1789682844; x=1790287644; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=9mNCAIqDOepKwDLnim2xcE5EzzCaL66TpsgNkeIyCS4=; b=dnh9RUktTUytuGItI9fNcOkzuvDn5C3AHrDB5+MdH4h42JDgXzjTOdTLLZxESl6WkY aQizdANAi0Y/y3G8ThTsFGlZHFSl681dlScuuh5kT/xNqbr5ymkjNyXyr6ka/cr2YUj5 p0RFdj1/veetYK90ZPdNtRAJxkzYDuSHesLZNsdyrkaJ8HVHcfsVTn100jl19HAiJ+Hh ppcc1j/BqZxIs8WfyMuTYChFxttiRN0ZyxyG8PUhc4vdtPIgY9MzXOs+BfhbRpF7Nhze ERGuGm9CDwQ5mn92r5gHchXVER4d6V35kjd9ufOK/+Lt6MpWQKLu4IT07XLXqr8VJI9r nPfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789682844; x=1790287644; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=9mNCAIqDOepKwDLnim2xcE5EzzCaL66TpsgNkeIyCS4=; b=LbjIcnSrEsZFD9WdFxW2+TpOc4+yXAnLCyujOW+rG30VFaF2L0/6/45FlaOIv4HQnu n9W1GOmUBXm4S1dOKOlLgS3CVKcWFH/iRvQ9weBXTFXx5Hy8Fm2pgDp4ei9Of762OMxe 9nquV9EtfmR7H6WTTgx9FQgJlifh16BIHqyy1LJYpKOTqyqR/GjWSN2W0rAKDG77lP67 LVP3ISOKA/my0k0v2TlGoaiu6f3F0gwiq3o8vX8Ehtg+p9bVsRRq63QkAf/nEOqnzoyk jVFDpnJsysmyNJ5gh927S9dSn1CV204AGvAgXDQiOIRpBmQPSY4gEZ5zs1GSfLNEq1Hc oMKw== X-Gm-Message-State: AFuF++kTcxqu7BWnVlW/oYBO5Pz49bOS20cOzu7lIVrezhS74NoiBhxa IL2DPvWYArmIx8m0dyAf2iYuw+gKxYAiwN5Coh8cozoQMr1jKIM5kFi0plGh2SysEvJKtFBGoNq e+ZKCwgM= X-Gm-Gg: AYBFou0AKtmYQSjqnc0IdnFh1rvR+aWawWxvHZtFif6n7lHOXVvlpLbrGuyy8iHwv9V DCllkDuAXUrj0ka3i6UbjLgRwS2MXVC/ithQG4VWgStMrsstJpuf6vs4p8yl8xD/6WdBaUYEUTW Uvu/83SNM1pwjr1USEYUD7EnHgr1vpapPyS5lXzsrtCT9k3Kp3t7oHVbL8VSAKx0ovTnqOoQRNx tQMNV8DDuOfIIIB4JdBgkxO4ktCtdjV+rf98N/YM0YfXc6R+OlJfKMzDXiGoSte6Nh3nmPoFkXv /D/mqfPaCl650nr49hWp6ynMeRVC2kXHGj6BGcy7a5EskEx6oaExnbqWMCz3EZAa5OLAIBuKlbJ S0K2oQZkfES0483PKMmYJCDCP2KPQ9U/dHFUt8FIepF+WRXmQrHF+v6mu0VVeCwFee9EHSTY8g5 GkyL65Ci3gsQ7YzY8HcWoyonHXHZv1PoX7317Nxqezl22sAFO9Uisv8oYmGmQfL5csUmDiKkv1h jXI/D+/ X-Received: by 2002:a05:6a20:6a0b:b0:3dd:85a8:4c64 with SMTP id adf61e73a8af0-3dd8c5dbaa5mr597625637.43.1789682844311; Thu, 17 Sep 2026 15:07:24 -0700 (PDT) From: Richard Henderson To: qemu-devel@nongnu.org Cc: mattst88@gmail.com Subject: [PATCH v6 2/2] accel/tcg: Allow cross-page goto_tb chaining in user-only builds Date: Thu, 17 Sep 2026 12:07:18 -1000 Message-ID: <20260917220718.60899-3-richard.henderson@linaro.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260917220718.60899-1-richard.henderson@linaro.org> References: <20260917220718.60899-1-richard.henderson@linaro.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:3b::1d; envelope-from=richard.henderson@linaro.org; helo=mail-pz2-x1d.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @linaro.org) X-ZM-MESSAGEID: 1789682888880158500 Content-Type: text/plain; charset="utf-8" From: Matt Turner translator_use_goto_tb() refuses to chain unless the destination is on the same page as the start of the TB. For guests whose text is much larger than a page this is expensive: an emulated alpha gcc compiling a 255k line translation unit takes the indirect dispatch path for 8.4 billion of its 34.2 billion TB exits, and a large share of those are ordinary direct branches that simply crossed an 8 KiB page boundary. The restriction was made unconditional by d3a2a1d803 ("accel/tcg: Introduce translator_use_goto_tb"), whose rationale was: Various targets avoid the page crossing test for CONFIG_USER_ONLY, but that is wrong: mmap and mprotect can change page permissions. That is true, but in user-only builds the invalidation path already covers it. There are no page tables: every mmap, mprotect and munmap reaches page_set_flags(), which calls tb_invalidate_phys_range() whenever the flags actually change, and tb_phys_invalidate() calls tb_jmp_unlink() to reset incoming jumps. A chained cross-page jump is therefore broken whenever the destination page's permissions change. This is not true in system mode, where TBs are keyed by physical address and a page table change invalidates nothing, so the restriction is kept there. The rule protects one more thing, which the original rationale does not mention: it guarantees that execution cannot enter a page without a TB lookup, and so without check_for_breakpoints(). That is what makes a breakpoint set after a block was translated take effect, since insertion deliberately invalidates nothing. A link established before the breakpoint was set would jump straight over it. So the chaining is only enabled for a run that can never acquire a breakpoint. In user-only mode every breakpoint comes from gdb -- BP_CPU is g_assert_not_reached() there, and the guest cannot ask for one -- and gdb has to be requested with -g before the first block is translated, even though with suspend=3Dn it may connect later. gdb_may_set_breakpoints() reports whether it was, and is fixed for the lifetime of the process. Add tests/tcg/multiarch/test-xpage-chain.c to cover both hazards directly. It writes the last instruction of one page and the first of the next, so that the fall-through between them is a cross-page goto_tb, runs it 200000 times so the chain is established, then checks that mprotect(PROT_NONE) makes the next call fault, and that different code written into the page once it is mapped back runs rather than a stale translation. The two instructions -- set the return value register, and return -- are all the architecture specific code there is; thirteen architectures supply them and the rest skip. The test detects the hazard it is meant to detect: with the tb_invalidate_phys_range() call in page_set_flags() commented out, it fails both phases, executing page B after PROT_NONE and returning the stale result. Run with -b, the same binary stops once the chain is established and lets tests/tcg/multiarch/gdbstub/xpage-bp.py set a breakpoint on the far side of it, which the next call has to stop on. With gdb_may_set_breakpoints() forced to false so that the chaining stays on under gdb, that breakpoint is missed and the test fails, which is what makes it a test of the gate rather than of gdb. Signed-off-by: Matt Turner Reviewed-by: Richard Henderson [rth: Update for meson test infrastructure] Signed-off-by: Richard Henderson Message-ID: <20260901034808.3524945-8-mattst88@gmail.com> --- include/gdbstub/user.h | 11 + accel/tcg/translator.c | 33 ++- gdbstub/user.c | 14 + tests/tcg/multiarch/test-xpage-chain.c | 336 ++++++++++++++++++++++++ tests/tcg/multiarch/gdbstub/xpage-bp.py | 37 +++ tests/tcg/multiarch/meson.build | 6 + 6 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/multiarch/test-xpage-chain.c create mode 100644 tests/tcg/multiarch/gdbstub/xpage-bp.py diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h index 654986d483b..c091cd97586 100644 --- a/include/gdbstub/user.h +++ b/include/gdbstub/user.h @@ -11,6 +11,17 @@ =20 #define MAX_SIGINFO_LENGTH 128 =20 +/** + * gdb_may_set_breakpoints() - whether a breakpoint can ever be inserted + * + * In user-only mode every breakpoint comes from gdb, and gdb is only ever + * reachable if -g was given at startup, before the guest ran a single + * instruction. A run that has no gdbstub can therefore never acquire a + * breakpoint, which lets translation take shortcuts that a breakpoint + * would invalidate. Stays true once true, even if gdb detaches. + */ +bool gdb_may_set_breakpoints(void); + /** * gdb_handlesig() - yield control to gdb * @cpu: CPU diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index e9943006947..7d1caf56553 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -15,6 +15,9 @@ #include "accel/tcg/cpu-mmu-index.h" #include "exec/target_page.h" #include "exec/translator.h" +#ifdef CONFIG_USER_ONLY +#include "gdbstub/user.h" +#endif #include "exec/plugin-gen.h" #include "tcg/tcg-op-common.h" #include "internal-common.h" @@ -110,6 +113,34 @@ bool translator_is_same_page(const DisasContextBase *d= b, vaddr addr) return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) =3D=3D 0; } =20 +/* + * Whether a direct jump may be chained to a destination outside the page + * the TB started in. + * + * In user-only mode there are no page tables. Every mmap, mprotect and + * munmap goes through page_set_flags(), which calls tb_invalidate_phys_ra= nge() + * whenever the flags actually change, and tb_phys_invalidate() unlinks + * incoming jumps. A cross-page link is therefore broken whenever the + * destination page's permissions change. + * + * What the same-page rule also provides is that execution cannot enter a = page + * without a TB lookup, and so without check_for_breakpoints(), which is w= hat + * makes a breakpoint set after a block was translated take effect. Nothi= ng + * invalidates on breakpoint insertion, so a link established beforehand w= ould + * jump straight over it. In user-only mode breakpoints only ever come fr= om + * gdb -- BP_CPU is g_assert_not_reached() there and the guest has no way = to + * ask for one -- and gdb has to be requested with -g before the first blo= ck + * is translated, so a run that has no gdbstub can never acquire a breakpo= int. + */ +static bool use_cross_page_goto_tb(void) +{ +#ifdef CONFIG_USER_ONLY + return !gdb_may_set_breakpoints(); +#else + return false; +#endif +} + bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) { /* Suppress goto_tb if requested. */ @@ -118,7 +149,7 @@ bool translator_use_goto_tb(DisasContextBase *db, vaddr= dest) } =20 /* Check for the dest on the same page as the start of the TB. */ - return translator_is_same_page(db, dest); + return use_cross_page_goto_tb() || translator_is_same_page(db, dest); } =20 void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, diff --git a/gdbstub/user.c b/gdbstub/user.c index 9e6f9a6f376..d810f0f38c3 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -470,6 +470,18 @@ static void *gdbserver_accept_thread(void *arg) =20 #define USAGE "\nUsage: -g {port|path}[,suspend=3D{y|n}]" =20 +/* + * Set before the guest runs and never cleared, so that code translated at + * any point can rely on it: with suspend=3Dn gdb may connect long after + * startup, and once connected it can insert a breakpoint at any time. + */ +static bool gdbserver_requested; + +bool gdb_may_set_breakpoints(void) +{ + return gdbserver_requested; +} + bool gdbserver_start(const char *args, Error **errp) { g_auto(GStrv) argv =3D g_strsplit(args, ",", 0); @@ -513,6 +525,8 @@ bool gdbserver_start(const char *args, Error **errp) return false; } =20 + gdbserver_requested =3D true; + if (suspend) { if (gdbserver_accept(port, gdb_fd, port_or_path)) { gdb_handlesig(first_cpu, 0, NULL, NULL, 0); diff --git a/tests/tcg/multiarch/test-xpage-chain.c b/tests/tcg/multiarch/t= est-xpage-chain.c new file mode 100644 index 00000000000..a4e34149e72 --- /dev/null +++ b/tests/tcg/multiarch/test-xpage-chain.c @@ -0,0 +1,336 @@ +/* + * Cross-page TB chaining hazard test. + * + * Two adjacent pages of hand-written code. The last instruction of page A + * sets the return value and falls through into page B, which returns; a TB + * always ends at a page boundary, so page A reaches page B through a + * cross-page goto_tb. + * + * Phase 1: run it enough times that QEMU chains TB_A -> TB_B. + * Phase 2: mprotect page B away. Re-running must fault. + * Phase 3: map it back and write different code into it. Re-running must + * execute the NEW code, not a stale chained translation. + * + * With -b, phases 2 and 3 are replaced by a stop at break_here(), where t= he + * gdbstub test sets a breakpoint on page B -- after the chain exists -- a= nd + * checks that re-running the chain still stops on it. See + * tests/tcg/multiarch/gdbstub/xpage-bp.py. + * + * The code the two pages hold is architecture specific, so each + * architecture supplies two emitters: + * + * emit_set_ret(p, val) - set the integer return value register to val + * emit_ret(p) - return to the caller + * + * both writing at @p and returning the number of bytes written. Neither + * may contain a branch: the fall-through from page A into page B is the + * whole point, and a delay slot must not straddle the boundary. An + * architecture that supplies neither skips the test. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline size_t put32(void *p, uint32_t insn) +{ + memcpy(p, &insn, sizeof(insn)); + return sizeof(insn); +} + +static inline size_t put16(void *p, uint16_t insn) +{ + memcpy(p, &insn, sizeof(insn)); + return sizeof(insn); +} + +#if defined(__aarch64__) +#define HAVE_EMITTERS +/* movz w0, #val */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x52800000u | ((uint32_t)val << 5)); +} +static size_t emit_ret(void *p) +{ + return put32(p, 0xd65f03c0u); /* ret */ +} +#elif defined(__alpha__) +#define HAVE_EMITTERS +/* lda $0, val($31) */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x201f0000u | (uint16_t)val); +} +static size_t emit_ret(void *p) +{ + return put32(p, 0x6bfa8001u); /* ret */ +} +#elif defined(__arm__) +#define HAVE_EMITTERS +/* mov r0, #val */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0xe3a00000u | (uint8_t)val); +} +static size_t emit_ret(void *p) +{ + return put32(p, 0xe12fff1eu); /* bx lr */ +} +#elif defined(__hppa__) +#define HAVE_EMITTERS +/* ldi val, %ret0 */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x341c0000u | ((uint32_t)val << 1)); +} +static size_t emit_ret(void *p) +{ + size_t n =3D put32(p, 0xe840c000u); /* bv %r0(%rp) */ + return n + put32((char *)p + n, 0x08000240u); /* nop (delay slot= ) */ +} +#elif defined(__i386__) || defined(__x86_64__) +#define HAVE_EMITTERS +/* mov $val, %eax */ +static size_t emit_set_ret(void *p, int val) +{ + uint32_t imm =3D val; + *(unsigned char *)p =3D 0xb8; + return 1 + put32((char *)p + 1, imm); +} +static size_t emit_ret(void *p) +{ + *(unsigned char *)p =3D 0xc3; /* ret */ + return 1; +} +#elif defined(__loongarch64) +#define HAVE_EMITTERS +/* ori $a0, $zero, val */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x03800004u | ((uint32_t)val << 10)); +} +static size_t emit_ret(void *p) +{ + return put32(p, 0x4c000020u); /* jr $ra */ +} +#elif defined(__m68k__) +#define HAVE_EMITTERS +/* moveq #val, %d0 */ +static size_t emit_set_ret(void *p, int val) +{ + return put16(p, 0x7000u | (uint8_t)val); +} +static size_t emit_ret(void *p) +{ + return put16(p, 0x4e75u); /* rts */ +} +#elif defined(__mips__) +#define HAVE_EMITTERS +/* li $v0, val */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x24020000u | (uint16_t)val); +} +static size_t emit_ret(void *p) +{ + size_t n =3D put32(p, 0x03e00008u); /* jr $ra */ + return n + put32((char *)p + n, 0x00000000u); /* nop (delay slot= ) */ +} +/* + * ELFv1 function pointers are descriptors rather than code addresses, so + * there is nothing to call the raw code through. + */ +#elif defined(__powerpc__) && \ + (!defined(__powerpc64__) || (defined(_CALL_ELF) && _CALL_ELF =3D=3D = 2)) +#define HAVE_EMITTERS +/* li r3, val */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x38600000u | (uint16_t)val); +} +static size_t emit_ret(void *p) +{ + return put32(p, 0x4e800020u); /* blr */ +} +#elif defined(__riscv) +#define HAVE_EMITTERS +/* addi a0, zero, val -- the 4 byte form, never c.li */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x00000513u | ((uint32_t)val << 20)); +} +static size_t emit_ret(void *p) +{ + return put32(p, 0x00008067u); /* jalr zero, 0(ra= ) */ +} +#elif defined(__s390x__) +#define HAVE_EMITTERS +/* lghi %r2, val */ +static size_t emit_set_ret(void *p, int val) +{ + size_t n =3D put16(p, 0xa729u); + return n + put16((char *)p + n, (uint16_t)val); +} +static size_t emit_ret(void *p) +{ + return put16(p, 0x07feu); /* br %r14 */ +} +#elif defined(__sh__) +#define HAVE_EMITTERS +/* mov #val, r0 */ +static size_t emit_set_ret(void *p, int val) +{ + return put16(p, 0xe000u | (uint8_t)val); +} +static size_t emit_ret(void *p) +{ + size_t n =3D put16(p, 0x000bu); /* rts */ + return n + put16((char *)p + n, 0x0009u); /* nop (delay slot= ) */ +} +#elif defined(__sparc__) +#define HAVE_EMITTERS +/* mov val, %o0 */ +static size_t emit_set_ret(void *p, int val) +{ + return put32(p, 0x90102000u | (uint32_t)(val & 0x1fff)); +} +static size_t emit_ret(void *p) +{ + size_t n =3D put32(p, 0x81c3e008u); /* retl */ + return n + put32((char *)p + n, 0x01000000u); /* nop (delay slot= ) */ +} +#endif + +/* Where the fall-through lands, for the gdbstub test to breakpoint on. */ +void *page_b_entry; + +/* Somewhere for the gdbstub test to stop once the chain is established. */ +void __attribute__((noinline)) break_here(void) +{ + asm volatile (""); +} + +#ifdef HAVE_EMITTERS +static sigjmp_buf jb; +/* + * Written by the SIGSEGV handler and read by main(), so it must not be + * cached in a register across the faulting call. + */ +static volatile sig_atomic_t caught; + +static void segv(int sig) +{ + caught =3D 1; + siglongjmp(jb, 1); +} +#endif + +int main(int argc, char **argv) +{ + bool bp_mode =3D argc > 1 && strcmp(argv[1], "-b") =3D=3D 0; +#ifndef HAVE_EMITTERS + printf("SKIP: no code emitters for this architecture\n"); + if (bp_mode) { + break_here(); + } + return 0; +#else + unsigned char tmp[16]; + struct sigaction sa; + long (*fn)(void); + size_t setlen, n; + long ps =3D sysconf(_SC_PAGESIZE); + int rc =3D 0; + unsigned char *m =3D mmap(NULL, 2 * ps, PROT_READ | PROT_WRITE | PROT_= EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (m =3D=3D MAP_FAILED) { + perror("mmap"); + return 2; + } + + unsigned char *pb =3D m + ps; + + /* + * Page A ends with the store to the return value register, so that the + * next instruction executed is the first one on page B. + */ + setlen =3D emit_set_ret(tmp, 1); + memcpy(pb - setlen, tmp, setlen); + emit_ret(pb); + __builtin___clear_cache((char *)m, (char *)m + 2 * ps); + + page_b_entry =3D pb; + fn =3D (long (*)(void))(pb - setlen); + + for (int i =3D 0; i < 200000; i++) { + if (fn() !=3D 1) { + printf("FAIL: phase 1 wrong result\n"); + return 1; + } + } + printf("phase 1 ok (chained)\n"); + + if (bp_mode) { + /* + * The chain from page A to page B now exists. gdb puts a breakpo= int + * on page_b_entry here; the call below has to stop on it rather t= han + * jump over it. + */ + break_here(); + if (fn() !=3D 1) { + printf("FAIL: bp phase wrong result\n"); + return 1; + } + printf("bp phase ok\n"); + return 0; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler =3D segv; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGSEGV, &sa, NULL) !=3D 0) { + perror("sigaction"); + return 2; + } + if (mprotect(pb, ps, PROT_NONE) !=3D 0) { + perror("mprotect"); + return 2; + } + if (sigsetjmp(jb, 1) =3D=3D 0) { + fn(); + printf("FAIL: phase 2 executed page B after mprotect(PROT_NONE)\n"= ); + rc =3D 1; + } else if (!caught) { + printf("FAIL: phase 2 longjmp without entering the handler\n"); + rc =3D 1; + } else { + printf("phase 2 ok (faulted)\n"); + } + + /* Phase 3: map back, overwrite, expect the new code to run. */ + if (mprotect(pb, ps, PROT_READ | PROT_WRITE | PROT_EXEC) !=3D 0) { + perror("mprotect back"); + return 2; + } + n =3D emit_set_ret(pb, 2); + emit_ret(pb + n); + __builtin___clear_cache((char *)pb, (char *)pb + ps); + + long r =3D fn(); + if (r !=3D 2) { + printf("FAIL: phase 3 returned %ld, expected 2 (stale chain)\n", r= ); + rc =3D 1; + } else { + printf("phase 3 ok (new code ran)\n"); + } + return rc; +#endif +} diff --git a/tests/tcg/multiarch/gdbstub/xpage-bp.py b/tests/tcg/multiarch/= gdbstub/xpage-bp.py new file mode 100644 index 00000000000..f40024f16de --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/xpage-bp.py @@ -0,0 +1,37 @@ +"""Test that a breakpoint set after a cross-page chain is established is h= it. + +translator_use_goto_tb() lets a direct branch chain to another page in +user-only builds, which is only safe because a run with no gdbstub can nev= er +acquire a breakpoint. This runs with one, so the chaining must be off and +the breakpoint must still be reached. + +This runs as a sourced script (via -x, via run-test.py). + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + gdb.Breakpoint("break_here") + gdb.execute("continue") + + # The chain exists by now; put a breakpoint on the far side of it. + target =3D int(gdb.parse_and_eval("(unsigned long)page_b_entry")) + if target =3D=3D 0: + report(True, "no code emitters for this architecture, skipped") + return + gdb.execute("break *{}".format(target)) + gdb.execute("continue") + + pc =3D int(gdb.parse_and_eval("(unsigned long)$pc")) + report(pc =3D=3D target, "stopped at {:#x}, expected {:#x}".format(pc,= target)) + + gdb.execute("delete") + gdb.execute("continue") + exitcode =3D int(gdb.parse_and_eval("$_exitcode")) + report(exitcode =3D=3D 0, "{} =3D=3D 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/meson.build b/tests/tcg/multiarch/meson.bu= ild index 508fdb585b7..6e68d75fb20 100644 --- a/tests/tcg/multiarch/meson.build +++ b/tests/tcg/multiarch/meson.build @@ -118,6 +118,12 @@ tests +=3D { 'gdb_test': ['--test', files('gdbstub/follow-fork-mode-parent.py')], }, } +tests +=3D { + multiarch/'test-xpage-chain.c': { + 'test_name': 'xpage-chain', + 'gdb_test': ['--test', files('gdbstub/xpage-bp.py'), '--pargs=3D-b'], + }, +} =20 # Specific plugin tests # Test plugin memory access instrumentation --=20 2.53.0