From nobody Sat Apr 11 23:04:27 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=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1772954607; cv=none; d=zohomail.com; s=zohoarc; b=mArRObv8A6UZbw4j/pmgYzfc/tWR2x4YArrqb3b/q3YDunWo+Iz4InPxZYQTlO+boBzqi6XqVYvgnj5P8LZ84mtvp36CNqgGjHVOFZk9Bhwy80023oz11rj3VdmhrBCXg7gwu9DUpAvINMBF1VzXckjXVPbk2ppm1ADSZhOML+g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1772954607; h=Content-Type: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:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=h/RSbypv/ZVnxXM1y5bTNKWJzgNjI04NBQqEcovui8E=; b=QoY9OXklMQv7IdFpdiO34VgineOQMwk4RKbrOTsQ3l+gzqEUUBVjKIc06V32wl62faVcW9vwmaNU4lLJt6qS5STl912pTmg+vcZPm6dY22hKTSGDsP8VoeB0qmN0cKdH+0j+gNbjSdYoBbKUEmcn224ZRpG2o+UiwVjKCTl9aTg= 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 lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1772954607960199.11067504391724; Sat, 7 Mar 2026 23:23:27 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vz8T3-0005Lh-8D; Sun, 08 Mar 2026 03:22:25 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vz8Rp-0002gt-Ia for qemu-devel@nongnu.org; Sun, 08 Mar 2026 03:21:11 -0400 Received: from mail-dy1-x1343.google.com ([2607:f8b0:4864:20::1343]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vz8Rm-0003D2-FI for qemu-devel@nongnu.org; Sun, 08 Mar 2026 03:21:09 -0400 Received: by mail-dy1-x1343.google.com with SMTP id 5a478bee46e88-2be27fa54feso7436723eec.0 for ; Sat, 07 Mar 2026 23:21:02 -0800 (PST) Received: from ZEVORN-PC.bbrouter ([38.95.120.198]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2be4f984ceasm6014081eec.32.2026.03.07.23.20.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 07 Mar 2026 23:20:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772954461; x=1773559261; 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; bh=h/RSbypv/ZVnxXM1y5bTNKWJzgNjI04NBQqEcovui8E=; b=ScghuP+I9NFoPGH9pSCm/oeviQBj0k9eRSbahTRiA+ZmVxpBntFTicnp+aCrF2GYZW OOitppxrMVI7kIWMKruwVXjNQJCDXzBxGvBU2Lsw1sb5S4KKjv2//7aTlHRlzNCu5fMb zykOYKgDsiB8Pn+8j7qh66fVWsWswGwc1NI8peghD2DY6LzLF0tT4JBxULPv+UV+J9J4 m1s6wZojAOkIgsteEw8pAts5A5OvunKy/iGioXXonFGIy918h2k4yMHKLuzihz3+8Fxe vmjCbDfrtGkCQsussfWyAW16Aon2/l+C0axUuBzADM6C8rTeZRgYzOlFUefTG32daqqS 7hpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772954461; x=1773559261; 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; bh=h/RSbypv/ZVnxXM1y5bTNKWJzgNjI04NBQqEcovui8E=; b=INhwUUDqx+6wfDvAjxpb70NnFLotKnZB2tNfoNy1E61s/7QLIXoZtS3C6xFI7V6jVH P7FlVtk7HP3EKWJuE4IO9ulsv0zh3PKcw8YrMTipWq8+5270id9hlzlmOxQhMsoS4miw qyFRY4ziBF5XZ5CYxfY2k8Fc303EtL3ys12AszOO57yK62NS6VJ9fY/28TuTJ7IR6C82 D8O8RrL9hyHSRMJ2DhiInVaZwNjXzxIP6CBqmWN20Y2HEOLcWqPaQ0oza1Zo+TEKj2MX b8fHskDQf7oCBfHeEuXUoUdf7zbgon131SyFH8t6fk90nmBDtMzRViIed9YZxPEI7gBY 8C4A== X-Forwarded-Encrypted: i=1; AJvYcCXK2qXkGpx4N3SHpKB24JkWFCMmKOeF+MgaPPbw4j5WmAEBfjHHR5bNr8FCEAemo2Q/WaJOcigDYPyf@nongnu.org X-Gm-Message-State: AOJu0Yz/H8xFoyZYwlt8F92iX5Pk6SOrgOrAOLsCr+T9qB6QNsnkxCXh CggVuiy096CKN8fGUpipGjZjUCbqUMwa4MAc2V+2E6NB53Blsug08zLf X-Gm-Gg: ATEYQzzAeT8F195iDGSB+VREurBPJfULqVkflb7f/qrZQmBSAXRNeGHbbDlEMK70ALq JSXmwK0+z0I+sP+Utv2kmGWd7fVCq5A1JC/owyKR6gQYfagjDSDUvi0NShseiZk4t4HOwGmZH0a B3t0tCI3Dz7n44lxvnk7v7tGGA4oBubd6nhM5fz0u9pmiuaL5IYYqU6WoJM7CQvaUJTJR8Vdy0+ xw0hq5chfW5uSIEOjI0f1W0DC8zgoSS2rKCFLIFk1Tt1BjbkmzEjZT6ialE4QQp2gUU1yvjhiMK v29IGLHABzy9k2CUwBAJD63yZTHAo1ELYyBARV7Hmwq5apC+/PjU0GGTN3Jkc7XVQfACE8R143z 2165YQnJEQQZydDoqaDn3Rbm+GK4+5uxa09+FLqrgA0RcPwfapwP8vWVT6+8hBEWCEhNbU5Qlkz xNjgras6LzIyQMy1pd+CwSPklqlCVwmTb47GRCN2kA+v+EjIqk8I1gjjLR2nDparZQDpNeQwXL7 oW3vxubRoDFKYn07MFoN9EtmII= X-Received: by 2002:a05:7301:5789:b0:2be:1803:7e11 with SMTP id 5a478bee46e88-2be4e08affcmr2422026eec.30.1772954461029; Sat, 07 Mar 2026 23:21:01 -0800 (PST) From: Chao Liu To: Paolo Bonzini , Palmer Dabbelt , Alistair Francis , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Chao Liu , Fabiano Rosas , Laurent Vivier Cc: tangtao1634@phytium.com.cn, qemu-devel@nongnu.org, qemu-riscv@nongnu.org Subject: [PATCH v1 25/28] tests/qtest: extend DM register semantics coverage Date: Sun, 8 Mar 2026 15:17:28 +0800 Message-ID: <21d42ab8e77b205319a2b085371d62edecfdf983.1772936778.git.chao.liu.zevorn@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" 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=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1343; envelope-from=chao.liu.zevorn@gmail.com; helo=mail-dy1-x1343.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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, 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 @gmail.com) X-ZM-MESSAGEID: 1772954609792158500 Add tests for the DMI register interface exercised in qtest mode (no real CPU execution): data-rw, progbuf-rw =E2=80=93 DATA/PROGBUF read-write ro-registers =E2=80=93 read-only register enforcement abstractcs-w1c =E2=80=93 write-1-to-clear cmderr field hart-selection =E2=80=93 hartsel/hasel routing dmcontrol-warz =E2=80=93 write-any-read-zero fields hartreset =E2=80=93 per-hart reset semantics ndmreset-gate =E2=80=93 ndmreset gating other operations optional-regs-absent =E2=80=93 unimplemented register reads deactivate =E2=80=93 DM deactivation clears state ackhavereset =E2=80=93 havereset acknowledgment Also expand the define/macro header to cover the full DMI register map and rewrite the existing four basic tests to use the new constants. Signed-off-by: Chao Liu --- tests/qtest/riscv-dm-test.c | 589 +++++++++++++++++++++++++++++++++--- 1 file changed, 539 insertions(+), 50 deletions(-) diff --git a/tests/qtest/riscv-dm-test.c b/tests/qtest/riscv-dm-test.c index c9e3c22a20..3a0dd1cbd4 100644 --- a/tests/qtest/riscv-dm-test.c +++ b/tests/qtest/riscv-dm-test.c @@ -9,43 +9,150 @@ #include "qemu/osdep.h" #include "libqtest.h" =20 +/* Virt machine memory map base address for the Debug Module */ #define DM_BASE 0x0 =20 -#define ROM_HARTID 0x104 -#define ROM_RESUME 0x10c - -#define A_DATA0 0x10 -#define A_DMCONTROL 0x40 -#define A_DMSTATUS 0x44 -#define A_HARTINFO 0x48 -#define A_ABSTRACTCS 0x58 - +/* + * ROM layout offsets (byte offsets within DM ROM). + * CPU ROM code writes to these addresses to signal state changes to the D= M. + */ +#define ROM_HARTID 0x104 +#define ROM_GOING 0x108 +#define ROM_RESUME 0x10C +#define ROM_EXCP 0x110 +#define ROM_DATA 0x3C0 +#define ROM_FLAGS 0x400 +#define ROM_ENTRY 0x800 + +/* DMI register byte offsets (word address =C3=97 4) */ +#define A_DATA0 0x10 +#define A_DATA1 0x14 +#define A_DMCONTROL 0x40 +#define A_DMSTATUS 0x44 +#define A_HARTINFO 0x48 +#define A_HALTSUM1 0x4C +#define A_HAWINDOWSEL 0x50 +#define A_HAWINDOW 0x54 +#define A_ABSTRACTCS 0x58 +#define A_COMMAND 0x5C +#define A_ABSTRACTAUTO 0x60 +#define A_PROGBUF0 0x80 +#define A_PROGBUF1 0x84 +#define A_AUTHDATA 0xC0 +#define A_DMCS2 0xC8 +#define A_SBCS 0xE0 +#define A_SBADDRESS0 0xE4 +#define A_SBADDRESS1 0xE8 +#define A_SBADDRESS2 0xEC +#define A_SBDATA0 0xF0 +#define A_SBDATA1 0xF4 +#define A_SBDATA2 0xF8 +#define A_SBDATA3 0xFC +#define A_HALTSUM0 0x100 + +/* DMCONTROL fields */ #define DMCONTROL_DMACTIVE (1u << 0) +#define DMCONTROL_NDMRESET (1u << 1) +#define DMCONTROL_CLRRESETHALTREQ (1u << 2) +#define DMCONTROL_SETRESETHALTREQ (1u << 3) +#define DMCONTROL_HARTRESET (1u << 29) #define DMCONTROL_HALTREQ (1u << 31) #define DMCONTROL_RESUMEREQ (1u << 30) - -#define DMSTATUS_VERSION_MASK 0xf +#define DMCONTROL_ACKHAVERESET (1u << 28) +#define DMCONTROL_HASEL (1u << 26) +#define DMCONTROL_HARTSELHI_SHIFT 16 +#define DMCONTROL_HARTSELLO_SHIFT 6 +#define DMCONTROL_HARTSELLO_MASK (0x3FFu << 6) + +/* DMSTATUS fields */ +#define DMSTATUS_VERSION_MASK 0xF #define DMSTATUS_AUTHENTICATED (1u << 7) #define DMSTATUS_ANYHALTED (1u << 8) #define DMSTATUS_ALLHALTED (1u << 9) #define DMSTATUS_ANYRUNNING (1u << 10) #define DMSTATUS_ALLRUNNING (1u << 11) +#define DMSTATUS_ANYUNAVAIL (1u << 12) +#define DMSTATUS_ALLUNAVAIL (1u << 13) +#define DMSTATUS_ANYNONEXISTENT (1u << 14) +#define DMSTATUS_ALLNONEXISTENT (1u << 15) #define DMSTATUS_ANYRESUMEACK (1u << 16) #define DMSTATUS_ALLRESUMEACK (1u << 17) #define DMSTATUS_ANYHAVERESET (1u << 18) +#define DMSTATUS_ALLHAVERESET (1u << 19) #define DMSTATUS_IMPEBREAK (1u << 22) +#define DMSTATUS_NDMRESETPENDING (1u << 24) =20 -#define HARTINFO_DATAADDR_MASK 0xfffu +/* HARTINFO fields */ +#define HARTINFO_DATAADDR_MASK 0xFFFu #define HARTINFO_DATASIZE_SHIFT 12 -#define HARTINFO_DATASIZE_MASK (0xfu << HARTINFO_DATASIZE_SHIFT) +#define HARTINFO_DATASIZE_MASK (0xFu << HARTINFO_DATASIZE_SHIFT) #define HARTINFO_DATAACCESS (1u << 16) #define HARTINFO_NSCRATCH_SHIFT 20 -#define HARTINFO_NSCRATCH_MASK (0xfu << HARTINFO_NSCRATCH_SHIFT) +#define HARTINFO_NSCRATCH_MASK (0xFu << HARTINFO_NSCRATCH_SHIFT) =20 -#define ABSTRACTCS_DATACOUNT_MASK 0xf +/* ABSTRACTCS fields */ +#define ABSTRACTCS_DATACOUNT_MASK 0xF +#define ABSTRACTCS_CMDERR_MASK (0x7u << 8) +#define ABSTRACTCS_CMDERR_SHIFT 8 #define ABSTRACTCS_BUSY (1u << 12) +#define ABSTRACTCS_PROGBUFSIZE_MASK (0x1Fu << 24) #define ABSTRACTCS_PROGBUFSIZE_SHIFT 24 -#define ABSTRACTCS_PROGBUFSIZE_MASK (0x1fu << ABSTRACTCS_PROGBUFSIZE_S= HIFT) + +/* SBCS fields */ +#define SBCS_SBERROR_MASK (0x7u << 12) +#define SBCS_SBBUSYERROR (1u << 22) +#define SBCS_SBVERSION_MASK (0x7u << 29) +#define SBCS_SBVERSION_SHIFT 29 +#define SBCS_SBASIZE_SHIFT 5 +#define SBCS_SBASIZE_MASK (0x7Fu << SBCS_SBASIZE_SHIFT) +#define SBCS_SBACCESS32 (1u << 2) + +/* Abstract command register number space */ +#define REGNO_GPR(n) (0x1000 + (n)) /* x0..x31 */ +#define REGNO_FPR(n) (0x1020 + (n)) /* f0..f31 */ +#define REGNO_CSR(n) (n) /* CSR direct */ +#define REGNO_DPC 0x7b1 +#define REGNO_DCSR 0x7b0 + +/* DCSR fields */ +#define DCSR_STEP (1u << 2) +#define DCSR_CAUSE_SHIFT 6 +#define DCSR_CAUSE_MASK (0x7u << DCSR_CAUSE_SHIFT) +#define DCSR_CAUSE_EBREAK 1 +#define DCSR_CAUSE_TRIGGER 2 +#define DCSR_CAUSE_HALTREQ 3 +#define DCSR_CAUSE_STEP 4 +#define DCSR_CAUSE_RESET 5 + +/* Trigger CSRs (abstract command register numbers) */ +#define REGNO_TSELECT REGNO_CSR(0x7a0) +#define REGNO_TDATA1 REGNO_CSR(0x7a1) +#define REGNO_TDATA2 REGNO_CSR(0x7a2) + +/* mcontrol (type 2) tdata1 field helpers */ +#define TDATA1_TYPE2_TYPE (2u << 28) +#define TDATA1_TYPE2_DMODE (1u << 27) +#define TDATA1_TYPE2_ACTION_DBG (1u << 12) +#define TDATA1_TYPE2_M (1u << 6) +#define TDATA1_TYPE2_S (1u << 4) +#define TDATA1_TYPE2_U (1u << 3) +#define TDATA1_TYPE2_EXEC (1u << 2) +#define TDATA1_TYPE2_STORE (1u << 1) +#define TDATA1_TYPE2_LOAD (1u << 0) + +/* itrigger (type 3) tdata1 field helpers */ +#define TDATA1_ITRIGGER_TYPE (3u << 28) +#define TDATA1_ITRIGGER_ACTION_DBG 1u +#define TDATA1_ITRIGGER_U (1u << 6) +#define TDATA1_ITRIGGER_S (1u << 7) +#define TDATA1_ITRIGGER_M (1u << 9) +#define TDATA1_ITRIGGER_COUNT(n) (((n) & 0x3fffu) << 10) +#define TDATA1_ITRIGGER_COUNT_MASK TDATA1_ITRIGGER_COUNT(0x3fff) + +/* TCG test timing (wall-clock microseconds, CPU runs via MTTCG thread) */ +#define TCG_POLL_STEP_US 10000 /* 10ms per poll step */ +#define TCG_POLL_TIMEOUT_US 5000000 /* 5s max */ +#define TCG_BOOT_US 200000 /* 200ms boot */ =20 static uint32_t dm_read(QTestState *qts, uint32_t reg) { @@ -62,118 +169,491 @@ static void dm_set_active(QTestState *qts) dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE); } =20 +/* Write to DM ROM area (simulates CPU writing from ROM code) */ static void rom_write32(QTestState *qts, uint32_t offset, uint32_t val) { qtest_writel(qts, DM_BASE + offset, val); } =20 +/* + * Simulate the CPU executing the DM ROM halt entry code. + * In real hardware: CPU enters debug mode =E2=86=92 jumps to ROM entry (0= x800) =E2=86=92 + * ROM code writes mhartid to HARTID offset =E2=86=92 DM recognizes hart a= s halted. + */ static void sim_cpu_halt_ack(QTestState *qts, uint32_t hartid) { rom_write32(qts, ROM_HARTID, hartid); } =20 +/* + * Simulate the CPU executing the DM ROM resume handler. + * In real hardware: ROM detects RESUME flag =E2=86=92 writes hartid to RE= SUME offset + * =E2=86=92 DM recognizes hart as resumed =E2=86=92 CPU executes dret. + */ static void sim_cpu_resume_ack(QTestState *qts, uint32_t hartid) { rom_write32(qts, ROM_RESUME, hartid); } =20 + +/* + * Test: dmactive gate. + * When dmactive=3D0 (after reset), all non-DMCONTROL reads should return = 0. + */ static void test_dmactive_gate(void) { QTestState *qts =3D qtest_init("-machine virt"); =20 + /* DM starts inactive after reset */ g_assert_cmpuint(dm_read(qts, A_DMCONTROL), =3D=3D, 0); + + /* All other registers should return 0 when dmactive=3D0 */ g_assert_cmpuint(dm_read(qts, A_DMSTATUS), =3D=3D, 0); - g_assert_cmpuint(dm_read(qts, A_HARTINFO), =3D=3D, 0); g_assert_cmpuint(dm_read(qts, A_ABSTRACTCS), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_HARTINFO), =3D=3D, 0); g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_PROGBUF0), =3D=3D, 0); =20 - dm_write(qts, A_DATA0, 0xdeadbeef); + /* Writes to non-DMCONTROL registers should be ignored */ + dm_write(qts, A_DATA0, 0xDEADBEEF); + /* Still inactive, so read should still return 0 */ g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0); =20 + /* Activate the DM */ dm_set_active(qts); g_assert_cmpuint(dm_read(qts, A_DMCONTROL) & DMCONTROL_DMACTIVE, =3D= =3D, DMCONTROL_DMACTIVE); + + /* Now DMSTATUS should be non-zero (version, authenticated, etc.) */ g_assert_cmpuint(dm_read(qts, A_DMSTATUS), !=3D, 0); =20 qtest_quit(qts); } =20 +/* + * Test: DMSTATUS register fields after activation. + */ static void test_dmstatus(void) { QTestState *qts =3D qtest_init("-machine virt"); - uint32_t status; - dm_set_active(qts); - status =3D dm_read(qts, A_DMSTATUS); =20 + uint32_t status =3D dm_read(qts, A_DMSTATUS); + + /* Version should be 3 (v1.0 spec) */ g_assert_cmpuint(status & DMSTATUS_VERSION_MASK, =3D=3D, 3); + + /* Should be authenticated */ g_assert_cmpuint(status & DMSTATUS_AUTHENTICATED, =3D=3D, DMSTATUS_AUTHENTICATED); - g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, - DMSTATUS_ANYRUNNING); - g_assert_cmpuint(status & DMSTATUS_ALLRUNNING, =3D=3D, - DMSTATUS_ALLRUNNING); + + /* impebreak should be set (default property) */ + g_assert_cmpuint(status & DMSTATUS_IMPEBREAK, =3D=3D, DMSTATUS_IMPEBRE= AK); + + /* Hart 0 should be running (not halted) */ + g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, DMSTATUS_ANYRUN= NING); + g_assert_cmpuint(status & DMSTATUS_ALLRUNNING, =3D=3D, DMSTATUS_ALLRUN= NING); g_assert_cmpuint(status & DMSTATUS_ANYHALTED, =3D=3D, 0); + g_assert_cmpuint(status & DMSTATUS_ALLHALTED, =3D=3D, 0); + + /* Should have havereset after initial reset */ g_assert_cmpuint(status & DMSTATUS_ANYHAVERESET, =3D=3D, DMSTATUS_ANYHAVERESET); - g_assert_cmpuint(status & DMSTATUS_IMPEBREAK, =3D=3D, DMSTATUS_IMPEBRE= AK); + g_assert_cmpuint(status & DMSTATUS_ALLHAVERESET, =3D=3D, + DMSTATUS_ALLHAVERESET); + + /* Hart 0 should not be nonexistent or unavailable */ + g_assert_cmpuint(status & DMSTATUS_ANYNONEXISTENT, =3D=3D, 0); + g_assert_cmpuint(status & DMSTATUS_ANYUNAVAIL, =3D=3D, 0); =20 qtest_quit(qts); } =20 +/* + * Test: HARTINFO fields describe memory-mapped DATA registers. + */ static void test_hartinfo(void) { QTestState *qts =3D qtest_init("-machine virt"); - uint32_t info; - dm_set_active(qts); - info =3D dm_read(qts, A_HARTINFO); =20 - g_assert_cmpuint(info & HARTINFO_DATAADDR_MASK, =3D=3D, 0x3c0); + uint32_t info =3D dm_read(qts, A_HARTINFO); + uint32_t datasize =3D + (info & HARTINFO_DATASIZE_MASK) >> HARTINFO_DATASIZE_SHIFT; + uint32_t nscratch =3D + (info & HARTINFO_NSCRATCH_MASK) >> HARTINFO_NSCRATCH_SHIFT; + g_assert_cmpuint(info & HARTINFO_DATAACCESS, =3D=3D, HARTINFO_DATAACCE= SS); - g_assert_cmpuint((info & HARTINFO_DATASIZE_MASK) >> - HARTINFO_DATASIZE_SHIFT, =3D=3D, 2); - g_assert_cmpuint((info & HARTINFO_NSCRATCH_MASK) >> - HARTINFO_NSCRATCH_SHIFT, =3D=3D, 1); + g_assert_cmpuint(info & HARTINFO_DATAADDR_MASK, =3D=3D, ROM_DATA); + g_assert_cmpuint(datasize, =3D=3D, 2); + g_assert_cmpuint(nscratch, =3D=3D, 1); =20 qtest_quit(qts); } =20 +/* + * Test: ABSTRACTCS config fields (datacount, progbufsize). + */ static void test_abstractcs_config(void) { QTestState *qts =3D qtest_init("-machine virt"); - uint32_t acs; - dm_set_active(qts); - acs =3D dm_read(qts, A_ABSTRACTCS); =20 + uint32_t acs =3D dm_read(qts, A_ABSTRACTCS); + + /* Default datacount =3D 2 */ g_assert_cmpuint(acs & ABSTRACTCS_DATACOUNT_MASK, =3D=3D, 2); - g_assert_cmpuint((acs & ABSTRACTCS_PROGBUFSIZE_MASK) >> - ABSTRACTCS_PROGBUFSIZE_SHIFT, =3D=3D, 8); + + /* Default progbufsize =3D 8 */ + uint32_t progbufsize =3D + (acs & ABSTRACTCS_PROGBUFSIZE_MASK) >> ABSTRACTCS_PROGBUFSIZE_SHIF= T; + g_assert_cmpuint(progbufsize, =3D=3D, 8); + + /* BUSY should be 0 */ g_assert_cmpuint(acs & ABSTRACTCS_BUSY, =3D=3D, 0); =20 + /* CMDERR should be 0 */ + g_assert_cmpuint(acs & ABSTRACTCS_CMDERR_MASK, =3D=3D, 0); + qtest_quit(qts); } =20 -static void test_halt_resume(void) +/* + * Test: DATA register read/write. + */ +static void test_data_rw(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + /* Write to DATA0 and DATA1 */ + dm_write(qts, A_DATA0, 0xCAFEBABE); + dm_write(qts, A_DATA1, 0x12345678); + + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0xCAFEBABE); + g_assert_cmpuint(dm_read(qts, A_DATA1), =3D=3D, 0x12345678); + + /* Overwrite */ + dm_write(qts, A_DATA0, 0x0); + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0x0); + + qtest_quit(qts); +} + +/* + * Test: PROGBUF register read/write. + */ +static void test_progbuf_rw(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + /* Write distinct values to progbuf0..3 */ + for (int i =3D 0; i < 4; i++) { + dm_write(qts, A_PROGBUF0 + i * 4, 0xA0000000 | i); + } + + for (int i =3D 0; i < 4; i++) { + g_assert_cmpuint(dm_read(qts, A_PROGBUF0 + i * 4), =3D=3D, + 0xA0000000 | (uint32_t)i); + } + + qtest_quit(qts); +} + +/* + * Test: Read-only register protection (DMSTATUS, HARTINFO, HALTSUM). + */ +static void test_ro_registers(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + uint32_t orig_status =3D dm_read(qts, A_DMSTATUS); + + /* Try to write DMSTATUS - should be ignored (RO) */ + dm_write(qts, A_DMSTATUS, 0xFFFFFFFF); + g_assert_cmpuint(dm_read(qts, A_DMSTATUS), =3D=3D, orig_status); + + /* Try to write HARTINFO - should be ignored (RO) */ + uint32_t orig_hartinfo =3D dm_read(qts, A_HARTINFO); + dm_write(qts, A_HARTINFO, 0xFFFFFFFF); + g_assert_cmpuint(dm_read(qts, A_HARTINFO), =3D=3D, orig_hartinfo); + + /* Try to write HALTSUM0 - should be ignored (RO) */ + uint32_t orig_haltsum0 =3D dm_read(qts, A_HALTSUM0); + dm_write(qts, A_HALTSUM0, 0xFFFFFFFF); + g_assert_cmpuint(dm_read(qts, A_HALTSUM0), =3D=3D, orig_haltsum0); + + qtest_quit(qts); +} + +/* + * Test: W1C (Write-1-to-Clear) behavior on ABSTRACTCS.CMDERR. + */ +static void test_abstractcs_w1c(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + /* + * CMDERR is 0 initially. To test W1C, we need to trigger an error. + * Issue an invalid command (cmdtype=3D0xFF) to a running hart. + * This should set CMDERR to HALTRESUME (4) since hart is not halted. + */ + dm_write(qts, A_COMMAND, 0xFF000000); + + uint32_t acs =3D dm_read(qts, A_ABSTRACTCS); + uint32_t cmderr =3D (acs & ABSTRACTCS_CMDERR_MASK) >> ABSTRACTCS_CMDER= R_SHIFT; + g_assert_cmpuint(cmderr, !=3D, 0); + + /* Clear CMDERR by writing 1s to the CMDERR field */ + dm_write(qts, A_ABSTRACTCS, ABSTRACTCS_CMDERR_MASK); + + acs =3D dm_read(qts, A_ABSTRACTCS); + cmderr =3D (acs & ABSTRACTCS_CMDERR_MASK) >> ABSTRACTCS_CMDERR_SHIFT; + g_assert_cmpuint(cmderr, =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Test: Hart selection via DMCONTROL.hartsel. + * Select a nonexistent hart and verify DMSTATUS reports it. + */ +static void test_hart_selection(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + /* Default hart 0 is selected and should be running */ + uint32_t status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, DMSTATUS_ANYRUN= NING); + g_assert_cmpuint(status & DMSTATUS_ANYNONEXISTENT, =3D=3D, 0); + + /* Select nonexistent hart 99 */ + uint32_t ctl =3D DMCONTROL_DMACTIVE | + (99u << DMCONTROL_HARTSELLO_SHIFT); + dm_write(qts, A_DMCONTROL, ctl); + + status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_ANYNONEXISTENT, =3D=3D, + DMSTATUS_ANYNONEXISTENT); + g_assert_cmpuint(status & DMSTATUS_ALLNONEXISTENT, =3D=3D, + DMSTATUS_ALLNONEXISTENT); + g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, 0); + + /* Switch back to hart 0 */ + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE); + status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, DMSTATUS_ANYRUN= NING); + g_assert_cmpuint(status & DMSTATUS_ANYNONEXISTENT, =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Test: DMCONTROL WARZ (Write-Any-Read-Zero) fields. + * HALTREQ, RESUMEREQ, ACKHAVERESET etc. should not read back. + */ +static void test_dmcontrol_warz(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + /* Write HALTREQ | DMACTIVE */ + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_HALTREQ); + + /* HALTREQ should not read back */ + uint32_t ctl =3D dm_read(qts, A_DMCONTROL); + g_assert_cmpuint(ctl & DMCONTROL_HALTREQ, =3D=3D, 0); + + /* DMACTIVE should still be set */ + g_assert_cmpuint(ctl & DMCONTROL_DMACTIVE, =3D=3D, DMCONTROL_DMACTIVE); + + /* Write RESUMEREQ | DMACTIVE */ + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_RESUMEREQ); + ctl =3D dm_read(qts, A_DMCONTROL); + g_assert_cmpuint(ctl & DMCONTROL_RESUMEREQ, =3D=3D, 0); + + /* Write ACKHAVERESET | DMACTIVE */ + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_ACKHAVERESET= ); + ctl =3D dm_read(qts, A_DMCONTROL); + g_assert_cmpuint(ctl & DMCONTROL_ACKHAVERESET, =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Test: HARTRESET is implemented and reads back while asserted. + */ +static void test_hartreset(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + uint32_t ctl, status; + + dm_set_active(qts); + + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_HARTRESET); + ctl =3D dm_read(qts, A_DMCONTROL); + g_assert_cmpuint(ctl & DMCONTROL_HARTRESET, =3D=3D, DMCONTROL_HARTRESE= T); + + status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_ANYHAVERESET, =3D=3D, + DMSTATUS_ANYHAVERESET); + + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE); + ctl =3D dm_read(qts, A_DMCONTROL); + g_assert_cmpuint(ctl & DMCONTROL_HARTRESET, =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Test: NDMRESET gates non-DMCONTROL accesses and exposes only pending st= ate. + */ +static void test_ndmreset_gate(void) { QTestState *qts =3D qtest_init("-machine virt"); uint32_t status; =20 dm_set_active(qts); + dm_write(qts, A_DATA0, 0xDEADBEEF); + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0xDEADBEEF); + + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_NDMRESET); + + status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status, =3D=3D, DMSTATUS_NDMRESETPENDING); + + dm_write(qts, A_DATA0, 0x11223344); + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0); + + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE); + status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_NDMRESETPENDING, =3D=3D, 0); + g_assert_cmpuint(status & DMSTATUS_ANYHAVERESET, =3D=3D, + DMSTATUS_ANYHAVERESET); + + qtest_quit(qts); +} + +/* + * Test: Optional registers that are not implemented read as 0 and ignore + * writes. + */ +static void test_optional_regs_absent(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + + dm_set_active(qts); + + dm_write(qts, A_AUTHDATA, 0xFFFFFFFF); + g_assert_cmpuint(dm_read(qts, A_AUTHDATA), =3D=3D, 0); + + dm_write(qts, A_DMCS2, 0xFFFFFFFF); + g_assert_cmpuint(dm_read(qts, A_DMCS2), =3D=3D, 0); + + dm_write(qts, A_SBADDRESS0, 0xFFFFFFFF); + dm_write(qts, A_SBADDRESS1, 0xFFFFFFFF); + dm_write(qts, A_SBADDRESS2, 0xFFFFFFFF); + dm_write(qts, A_SBDATA0, 0xAAAAAAAA); + dm_write(qts, A_SBDATA1, 0xBBBBBBBB); + dm_write(qts, A_SBDATA2, 0xCCCCCCCC); + dm_write(qts, A_SBDATA3, 0xDDDDDDDD); + + g_assert_cmpuint(dm_read(qts, A_SBADDRESS0), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_SBADDRESS1), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_SBADDRESS2), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_SBDATA0), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_SBDATA1), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_SBDATA2), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_SBDATA3), =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Test: Deactivation resets all state. + */ +static void test_deactivate(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + + /* Activate and write some data */ + dm_set_active(qts); + dm_write(qts, A_DATA0, 0xDEADBEEF); + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0xDEADBEEF); + + /* Deactivate: clear dmactive */ + dm_write(qts, A_DMCONTROL, 0); + + /* All registers should return 0 again */ + g_assert_cmpuint(dm_read(qts, A_DMSTATUS), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0); + g_assert_cmpuint(dm_read(qts, A_ABSTRACTCS), =3D=3D, 0); + + /* Re-activate: verify state was reset */ + dm_set_active(qts); + g_assert_cmpuint(dm_read(qts, A_DATA0), =3D=3D, 0); + + uint32_t status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_VERSION_MASK, =3D=3D, 3); + + qtest_quit(qts); +} + +/* + * Test: ACKHAVERESET clears havereset in DMSTATUS. + */ +static void test_ackhavereset(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + /* After reset, havereset should be set */ + uint32_t status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_ANYHAVERESET, =3D=3D, + DMSTATUS_ANYHAVERESET); + + /* Acknowledge havereset */ + dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_ACKHAVERESET= ); + + status =3D dm_read(qts, A_DMSTATUS); + g_assert_cmpuint(status & DMSTATUS_ANYHAVERESET, =3D=3D, 0); + g_assert_cmpuint(status & DMSTATUS_ALLHAVERESET, =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Test: Halt and resume cycle via ROM simulation. + * + * Simulates the full halt/resume flow: + * 1. Debugger writes HALTREQ =E2=86=92 DM signals halt IRQ + * 2. CPU enters debug mode =E2=86=92 ROM entry code writes mhartid to HAR= TID offset + * 3. DM recognizes hart as halted (anyhalted/allhalted set) + * 4. Debugger writes RESUMEREQ =E2=86=92 DM sets FLAGS to RESUME + * 5. CPU ROM resume handler writes hartid to RESUME offset =E2=86=92 dret + * 6. DM recognizes hart as resumed (resumeack set, running) + */ +static void test_halt_resume(void) +{ + QTestState *qts =3D qtest_init("-machine virt"); + dm_set_active(qts); + + uint32_t status; + status =3D dm_read(qts, A_DMSTATUS); - g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, - DMSTATUS_ANYRUNNING); + g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, DMSTATUS_ANYRUN= NING); g_assert_cmpuint(status & DMSTATUS_ANYHALTED, =3D=3D, 0); =20 dm_write(qts, A_DMCONTROL, DMCONTROL_DMACTIVE | DMCONTROL_HALTREQ); sim_cpu_halt_ack(qts, 0); =20 status =3D dm_read(qts, A_DMSTATUS); - g_assert_cmpuint(status & DMSTATUS_ANYHALTED, =3D=3D, - DMSTATUS_ANYHALTED); - g_assert_cmpuint(status & DMSTATUS_ALLHALTED, =3D=3D, - DMSTATUS_ALLHALTED); + g_assert_cmpuint(status & DMSTATUS_ANYHALTED, =3D=3D, DMSTATUS_ANYHALT= ED); + g_assert_cmpuint(status & DMSTATUS_ALLHALTED, =3D=3D, DMSTATUS_ALLHALT= ED); g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, 0); g_assert_cmpuint(status & DMSTATUS_ALLRUNNING, =3D=3D, 0); =20 @@ -185,10 +665,8 @@ static void test_halt_resume(void) DMSTATUS_ANYRESUMEACK); g_assert_cmpuint(status & DMSTATUS_ALLRESUMEACK, =3D=3D, DMSTATUS_ALLRESUMEACK); - g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, - DMSTATUS_ANYRUNNING); - g_assert_cmpuint(status & DMSTATUS_ALLRUNNING, =3D=3D, - DMSTATUS_ALLRUNNING); + g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, =3D=3D, DMSTATUS_ANYRUN= NING); + g_assert_cmpuint(status & DMSTATUS_ALLRUNNING, =3D=3D, DMSTATUS_ALLRUN= NING); g_assert_cmpuint(status & DMSTATUS_ANYHALTED, =3D=3D, 0); =20 qtest_quit(qts); @@ -202,6 +680,17 @@ int main(int argc, char *argv[]) qtest_add_func("/riscv-dm/dmstatus", test_dmstatus); qtest_add_func("/riscv-dm/hartinfo", test_hartinfo); qtest_add_func("/riscv-dm/abstractcs-config", test_abstractcs_config); + qtest_add_func("/riscv-dm/data-rw", test_data_rw); + qtest_add_func("/riscv-dm/progbuf-rw", test_progbuf_rw); + qtest_add_func("/riscv-dm/ro-registers", test_ro_registers); + qtest_add_func("/riscv-dm/abstractcs-w1c", test_abstractcs_w1c); + qtest_add_func("/riscv-dm/hart-selection", test_hart_selection); + qtest_add_func("/riscv-dm/dmcontrol-warz", test_dmcontrol_warz); + qtest_add_func("/riscv-dm/hartreset", test_hartreset); + qtest_add_func("/riscv-dm/ndmreset-gate", test_ndmreset_gate); + qtest_add_func("/riscv-dm/optional-regs-absent", test_optional_regs_ab= sent); + qtest_add_func("/riscv-dm/deactivate", test_deactivate); + qtest_add_func("/riscv-dm/ackhavereset", test_ackhavereset); qtest_add_func("/riscv-dm/halt-resume", test_halt_resume); =20 return g_test_run(); --=20 2.53.0