From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941689293789.043949696173; Mon, 13 Jul 2026 04:21:29 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEi7-000050-Af; Mon, 13 Jul 2026 07:20:31 -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 1wjEi1-0008MQ-EF for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:26 -0400 Received: from mx1.catap.net ([2a01:4f8:271:125e::2]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEhz-00080Z-4P for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:24 -0400 Received: by mx1.catap.net (OpenSMTPD) with ESMTPSA id 6c533594 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id bf80800e; Mon, 13 Jul 2026 13:20:13 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=iU8C/Gx VPk2o4IHrNPKHIKmeiFCDD2vrEtZJU+E5QUUuP4TwjmUYmPZhof9KSZ3ZIzowIloRjcZDV Lf19ZPquO36ieriy9P1/26o1tjnB+w/rDBkMF0PTDZHoZKzJubmzOxMfxToLReIn/v/mI4 Tck03tnCgiuGjUsGSx3XH7y7uk834G761/N7tJgjoOUNp8OFFxDHOI5Xa7b6fxRJYVbhMI p6AhfnX6xb8gj99PGmxuGiOXd7RpilJF9stuTXQRKcMtrMDxVRBtnnrEgeHALHAhJcPqs8 JX0Tke4rpOY1h/dRhXAuM5ogZPUGMYkaGQj3s8+Iq+/wkz8eYlX2uqA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=A HNAZgfLB8xDDhalUO+7466I/1HRryiDJ1zC/K0+gcg=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=cky9pIlhEGU16Wgg6ajO8m94lvHJ8HH3cSGDN5pUx5qBS0X/PhxI0/ubeZrLyrp2gtXa q0LVj6lUH5M07WUIuKjLUQ0p4nNyDpFMZngEdw05t9DuujnyXxb0wg/pV8IvzMuSADCwzc DCKMFE4tFfWS/mLB2/YKriLdF3FaYF2Z8kMEsyGkpwxUUFAihifNsInQRLVaEkE8pWDqMb sZIZPDryZtWeNmGDQRxIQBT1ClEcmbSN6vCtMb/fshKdExUQQ6N3sAf/Pqyf6l+gx7Jh4I zwUFR9IczFc3uOeyCmdyXYK/p8p8QNN7oJLthYbhUiH66NlJrjEUc4q/3o1Tx5lg== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=zdal3EN3; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=INqBjHlp; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=AHNAZgfL B8xDDhalUO+7466I/1HRryiDJ1zC/K0+gcg=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=zdal3EN3Upcej9HfIUNmxpPBURa0P163tXX 21X6fObmCae77URkmNXWSMNQvCLWU8vjdNSmtiniSvHU4z6hVG/aBrG+JtOvvtqtGa2jGW EXL75tv1wqGVZDahk+ACNeoMCkWGPNhRPd7n1cEj6A+Gu6YZ9mNrb5YNoIOojjoN7bY4Xk AOEIgMTMJLEq2uHmDb6fo3pcTQuxlvUIA//CaXy2Pvpj6PRbqD4kpAiaWhv28UA9cq8X7+ zQTK23stg/ykpf+fgwSt+kV9oiSTPfEkgIvBx6DuHjnCNoblaKq6nLkajc3yEQfarCVv9p CZgiBKO+S8NOglRUbKF2kyq4fdA== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=AHNAZgfLB8xDDhalUO+7466I/1HRryiDJ1zC/K0+gcg=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=INqBjHlpL6P9fnEq8C Kwpg6NJTuJgeRlqBRHg7QNnQ5Chv/qGTiwDnAZG+A36Jb8ZCe1ZpDrY3QbolUbQgn4AA== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 1/9] hw/usb/xhci: reassert pending busy interrupts Date: Mon, 13 Jul 2026 13:16:29 +0200 Message-ID: <20260713112004.4956-2-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=2a01:4f8:271:125e::2; envelope-from=kirill@korins.ky; helo=mx1.catap.net 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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941690189158500 Content-Type: text/plain; charset="utf-8" xhci_intr_raise() returned early whenever ERDP.EHB was set; that also skipped IRQ assertion after the guest cleared IMAN.IP while leaving the event handler busy bit asserted. Track the previous IMAN.IP state and suppress only duplicate raises; when ERDP.EHB remains set after IMAN.IP is cleared, assert the IRQ again so the guest can observe the pending event. Signed-off-by: Kirill A. Korinsky --- hw/usb/hcd-xhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 2cdab3ba0e..ce06d52c36 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -577,12 +577,13 @@ static void xhci_intr_update(XHCIState *xhci, int v) static void xhci_intr_raise(XHCIState *xhci, int v) { bool pending =3D (xhci->intr[v].erdp_low & ERDP_EHB); + bool was_raised =3D (xhci->intr[v].iman & IMAN_IP); =20 xhci->intr[v].erdp_low |=3D ERDP_EHB; xhci->intr[v].iman |=3D IMAN_IP; xhci->usbsts |=3D USBSTS_EINT; =20 - if (pending) { + if (pending && was_raised) { return; } if (!(xhci->intr[v].iman & IMAN_IE)) { --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941631372533.2386769617664; Mon, 13 Jul 2026 04:20:31 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEi3-0008PE-5J; Mon, 13 Jul 2026 07:20:27 -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 1wjEhz-0008J9-AV for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:23 -0400 Received: from mx2.catap.net ([65.21.79.163]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEhw-00080U-C6 for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:22 -0400 Received: by mx2.catap.net (OpenSMTPD) with ESMTPSA id 8a3d94d0 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id 98abd146; Mon, 13 Jul 2026 13:20:13 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=IdUbyZH 19RVhzZP+Fho6cj2E414zU20HyK050h3y1UAEAluEsTkx86P+TW8PymoM3L/AbMviwJGBu EHdZw9OAJwtkC+wwgWggvMxn4C1JFbkmYPmPIxXgx3HNaM7X8yae0ON/fFzXIyfo5zovU2 JMMisjKiGYrmzk7hdRt/mhOPLxS+NckA0wiXkoiyDr2+NLLBWffwLCyWoDfcrX6BUcIo9b jbhHNG714raQ+LtH6lz7tueQ2l/mLGUG3BPrOHSdcA/Z73S6HuZkyGmdopipYJ8iVpCx8h XILbejuMsPiWp8qm9JKOGp2svWCGWd9kwns1XjMc72AhZ4PG2uOvUIQ== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=G HiidZPiKTLxR2F5vDvIzmTqt+7dH1VyozQ+gXLxlpo=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=Phx+04CMypDPfogZhOIJDOWwZUOe/fUTwxgL1jIsIj9MiD6IRDldVXkCEGBudIOrgMj+ 469JuBLgS32rEvcIzHDrgGMeO4pypE5dh/vaLyT9N1g/nZ9YBN6fG4MAq+weDtzgEKt2M6 LcA4NiHND6TBN+2J9jcOm37EIrEK2VWCR8mby+Gh5gNKrw2Yhz/TQQkNeAIlX8i71a/tAi /PQoO6XuMdsDdXdsvnG32k7cKZdmy7qbJb2N/KgyOpiME+6oXnOyjcAqX/U0RpWyN3a48O Fy8FOodPFTQEumAiBFG1G6As9ALsdj6E1fvj7rRW+Ftik5wB/JHlHo52gQQvIY1Q== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=wS9fQJ45; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=NTdCWoep; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=GHiidZPi KTLxR2F5vDvIzmTqt+7dH1VyozQ+gXLxlpo=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=wS9fQJ45g2PJpaTP0k5EVe5g9P/Yrmr4H3L Lnkz95DEFAA5gSSno7/REl2T3EfkDombEhz8nex+L/S1Y3x+pp+9FBiwypoXzmjC51eNtw qG56Hy0htjNRPOiut7iTrhFbqmllj0gh9aNv7BRKcjACRCLFmNxv5Lc+iA9sYDvyu8quj5 cKYgvSy6l9KQwySirL0U5Q0kvodk6vGKLe+vKe5FQNK7OUtQ6x1FboflxpJYH8LJ6WTr4K BJQ4cHuXfKTjbsDIpzqbBFK1DmPzoeKRb2ViJw6LtAzwK5QaLCsXZTC7ZxIya9XH1co82H hWbczBlnuGYK8w5A/i+2OMqHvxQ== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=GHiidZPiKTLxR2F5vDvIzmTqt+7dH1VyozQ+gXLxlpo=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=NTdCWoepznJkTEO/nP sSJCQHJ5BctEyvWt+2cAo29VKYNC3Jp3lxdxLVBXE/nj32y1QVtdAz2K5B70IbP6v8Bg== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 2/9] target/mips: make CP0 interrupt state atomic Date: Mon, 13 Jul 2026 13:16:30 +0200 Message-ID: <20260713112004.4956-3-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=65.21.79.163; envelope-from=kirill@korins.ky; helo=mx2.catap.net X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941634050158500 Content-Type: text/plain; charset="utf-8" MTTCG lets interrupt inputs and timer callbacks update CP0_Cause, CP0_Status, and CP0_Count while a vCPU checks interrupt state. Use qatomic accessors for CP0 fields that participate in interrupt and timer decisions, and kick remote CPUs explicitly after raising a hard interrupt; MIPS interrupt line updates no longer depend on taking the BQL. Signed-off-by: Kirill A. Korinsky --- hw/mips/mips_int.c | 17 +++++++------ target/mips/internal.h | 13 ++++++---- target/mips/system/cp0.c | 27 +++++++++++++-------- target/mips/system/cp0_timer.c | 32 ++++++++++++++----------- target/mips/tcg/system/special_helper.c | 17 +++++++------ target/mips/tcg/system/tlb_helper.c | 21 +++++++++------- 6 files changed, 75 insertions(+), 52 deletions(-) diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index 26e7dd6d5f..537394c8fd 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -21,30 +21,33 @@ */ =20 #include "qemu/osdep.h" -#include "qemu/main-loop.h" #include "hw/core/irq.h" #include "target/mips/cpu.h" +#include "target/mips/internal.h" =20 static void cpu_mips_irq_request(void *opaque, int irq, int level) { MIPSCPU *cpu =3D opaque; CPUMIPSState *env =3D &cpu->env; CPUState *cs =3D CPU(cpu); + int32_t irq_bit; =20 if (irq < 0 || irq > 7) { return; } =20 - BQL_LOCK_GUARD(); - + irq_bit =3D 1 << (irq + CP0Ca_IP); if (level) { - env->CP0_Cause |=3D 1 << (irq + CP0Ca_IP); + qatomic_or(&env->CP0_Cause, irq_bit); } else { - env->CP0_Cause &=3D ~(1 << (irq + CP0Ca_IP)); + qatomic_and(&env->CP0_Cause, ~irq_bit); } =20 - if (env->CP0_Cause & CP0Ca_IP_mask) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); + if (qatomic_read(&env->CP0_Cause) & CP0Ca_IP_mask) { + cpu_set_interrupt(cs, CPU_INTERRUPT_HARD); + if (!qemu_cpu_is_self(cs)) { + qemu_cpu_kick(cs); + } } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } diff --git a/target/mips/internal.h b/target/mips/internal.h index c5c286872e..cd06d84140 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -8,6 +8,7 @@ #ifndef MIPS_INTERNAL_H #define MIPS_INTERNAL_H =20 +#include "qemu/atomic.h" #include "exec/memattrs.h" #ifdef CONFIG_TCG #include "tcg/tcg-internal.h" @@ -167,9 +168,11 @@ extern const VMStateDescription vmstate_mips_cpu; =20 static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env) { - return (env->CP0_Status & (1 << CP0St_IE)) && - !(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && + int32_t status =3D qatomic_read(&env->CP0_Status); + + return (status & (1 << CP0St_IE)) && + !(status & (1 << CP0St_EXL)) && + !(status & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM) && /* * Note that the TCStatus IXMT field is initialized to zero, @@ -186,8 +189,8 @@ static inline bool cpu_mips_hw_interrupts_pending(CPUMI= PSState *env) int32_t status; bool r; =20 - pending =3D env->CP0_Cause & CP0Ca_IP_mask; - status =3D env->CP0_Status & CP0Ca_IP_mask; + pending =3D qatomic_read(&env->CP0_Cause) & CP0Ca_IP_mask; + status =3D qatomic_read(&env->CP0_Status) & CP0Ca_IP_mask; =20 if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { /* diff --git a/target/mips/system/cp0.c b/target/mips/system/cp0.c index ff7d3db00c..6f222ad586 100644 --- a/target/mips/system/cp0.c +++ b/target/mips/system/cp0.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "internal.h" #include "exec/cputlb.h" +#include "qemu/atomic.h" =20 /* Called for updates to CP0_Status. */ void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) @@ -61,7 +62,8 @@ void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu,= int tc) void cpu_mips_store_status(CPUMIPSState *env, target_ulong val) { uint32_t mask =3D env->CP0_Status_rw_bitmask; - target_ulong old =3D env->CP0_Status; + target_ulong old; + target_ulong status; =20 if (env->insn_flags & ISA_MIPS_R6) { bool has_supervisor =3D extract32(mask, CP0St_KSU, 2) =3D=3D 0x3; @@ -77,9 +79,11 @@ void cpu_mips_store_status(CPUMIPSState *env, target_ulo= ng val) mask &=3D ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val); } =20 - env->CP0_Status =3D (old & ~mask) | (val & mask); + old =3D qatomic_read(&env->CP0_Status); + status =3D (old & ~mask) | (val & mask); + qatomic_set(&env->CP0_Status, status); #if defined(TARGET_MIPS64) - if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) { + if ((status ^ old) & (old & (7 << CP0St_UX))) { /* Access to at least one of the 64-bit segments has been disabled= */ tlb_flush(env_cpu(env)); } @@ -93,8 +97,9 @@ void cpu_mips_store_status(CPUMIPSState *env, target_ulon= g val) =20 void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) { - uint32_t mask =3D 0x00C00300; - uint32_t old =3D env->CP0_Cause; + int32_t mask =3D 0x00C00300; + int32_t old; + int32_t cause; int i; =20 if (env->insn_flags & ISA_MIPS_R2) { @@ -104,10 +109,12 @@ void cpu_mips_store_cause(CPUMIPSState *env, target_u= long val) mask &=3D ~((1 << CP0Ca_WP) & val); } =20 - env->CP0_Cause =3D (env->CP0_Cause & ~mask) | (val & mask); + old =3D qatomic_fetch_and(&env->CP0_Cause, ~mask); + qatomic_or(&env->CP0_Cause, (int32_t)(val & mask)); + cause =3D qatomic_read(&env->CP0_Cause); =20 - if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) { - if (env->CP0_Cause & (1 << CP0Ca_DC)) { + if ((old ^ cause) & (1 << CP0Ca_DC)) { + if (cause & (1 << CP0Ca_DC)) { cpu_mips_stop_count(env); } else { cpu_mips_start_count(env); @@ -116,8 +123,8 @@ void cpu_mips_store_cause(CPUMIPSState *env, target_ulo= ng val) =20 /* Set/reset software interrupts */ for (i =3D 0 ; i < 2 ; i++) { - if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { - cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i= ))); + if ((old ^ cause) & (1 << (CP0Ca_IP + i))) { + cpu_mips_soft_irq(env, i, cause & (1 << (CP0Ca_IP + i))); } } } diff --git a/target/mips/system/cp0_timer.c b/target/mips/system/cp0_timer.c index 6aed5d3af2..622fee357d 100644 --- a/target/mips/system/cp0_timer.c +++ b/target/mips/system/cp0_timer.c @@ -23,6 +23,7 @@ #include "qemu/osdep.h" #include "hw/core/irq.h" #include "qemu/timer.h" +#include "qemu/atomic.h" #include "internal.h" =20 /* MIPS R4K timer */ @@ -31,7 +32,7 @@ static uint32_t cpu_mips_get_count_val(CPUMIPSState *env) MIPSCPU *cpu =3D env_archcpu(env); int64_t now_ns; now_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - return env->CP0_Count + + return qatomic_read(&env->CP0_Count) + (uint32_t)clock_ns_to_ticks(cpu->count_clock, now_ns); } =20 @@ -56,15 +57,15 @@ static void cpu_mips_timer_expire(CPUMIPSState *env) { cpu_mips_timer_update(env); if (env->insn_flags & ISA_MIPS_R2) { - env->CP0_Cause |=3D 1 << CP0Ca_TI; + qatomic_or(&env->CP0_Cause, 1 << CP0Ca_TI); } qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); } =20 uint32_t cpu_mips_get_count(CPUMIPSState *env) { - if (env->CP0_Cause & (1 << CP0Ca_DC)) { - return env->CP0_Count; + if (qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC)) { + return qatomic_read(&env->CP0_Count); } else { uint64_t now_ns; =20 @@ -86,12 +87,13 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t c= ount) * So env->timer may be NULL, so treat timer as disabled in that case. */ MIPSCPU *cpu =3D env_archcpu(env); - if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer) { - env->CP0_Count =3D count; + if ((qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC)) || !env->timer) { + qatomic_set(&env->CP0_Count, count); } else { /* Store new count register */ - env->CP0_Count =3D count - (uint32_t)clock_ns_to_ticks(cpu->count_= clock, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + qatomic_set(&env->CP0_Count, + count - (uint32_t)clock_ns_to_ticks(cpu->count_clock, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))); /* Update timer timer */ cpu_mips_timer_update(env); } @@ -100,26 +102,28 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t= count) void cpu_mips_store_compare(CPUMIPSState *env, uint32_t value) { env->CP0_Compare =3D value; - if (!(env->CP0_Cause & (1 << CP0Ca_DC))) { + if (!(qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC))) { cpu_mips_timer_update(env); } if (env->insn_flags & ISA_MIPS_R2) { - env->CP0_Cause &=3D ~(1 << CP0Ca_TI); + qatomic_and(&env->CP0_Cause, ~(1 << CP0Ca_TI)); } qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); } =20 void cpu_mips_start_count(CPUMIPSState *env) { - cpu_mips_store_count(env, env->CP0_Count); + cpu_mips_store_count(env, qatomic_read(&env->CP0_Count)); } =20 void cpu_mips_stop_count(CPUMIPSState *env) { /* Store the current value */ MIPSCPU *cpu =3D env_archcpu(env); - env->CP0_Count +=3D (uint32_t)clock_ns_to_ticks(cpu->count_clock, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + + qatomic_add(&env->CP0_Count, + (uint32_t)clock_ns_to_ticks(cpu->count_clock, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))); } =20 static void mips_timer_cb(void *opaque) @@ -128,7 +132,7 @@ static void mips_timer_cb(void *opaque) =20 env =3D opaque; =20 - if (env->CP0_Cause & (1 << CP0Ca_DC)) { + if (qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC)) { return; } =20 diff --git a/target/mips/tcg/system/special_helper.c b/target/mips/tcg/syst= em/special_helper.c index b54cbe88a3..04ae0bc6c3 100644 --- a/target/mips/tcg/system/special_helper.c +++ b/target/mips/tcg/system/special_helper.c @@ -24,21 +24,22 @@ #include "exec/helper-proto.h" #include "exec/translation-block.h" #include "internal.h" +#include "qemu/atomic.h" =20 /* Specials */ target_ulong helper_di(CPUMIPSState *env) { - target_ulong t0 =3D env->CP0_Status; + target_ulong t0 =3D qatomic_read(&env->CP0_Status); =20 - env->CP0_Status =3D t0 & ~(1 << CP0St_IE); + qatomic_set(&env->CP0_Status, t0 & ~(1 << CP0St_IE)); return t0; } =20 target_ulong helper_ei(CPUMIPSState *env) { - target_ulong t0 =3D env->CP0_Status; + target_ulong t0 =3D qatomic_read(&env->CP0_Status); =20 - env->CP0_Status =3D t0 | (1 << CP0St_IE); + qatomic_set(&env->CP0_Status, t0 | (1 << CP0St_IE)); return t0; } =20 @@ -103,13 +104,15 @@ bool mips_io_recompile_replay_branch(CPUState *cs, co= nst TranslationBlock *tb) =20 static inline void exception_return(CPUMIPSState *env) { + target_ulong status =3D qatomic_read(&env->CP0_Status); + debug_pre_eret(env); - if (env->CP0_Status & (1 << CP0St_ERL)) { + if (status & (1 << CP0St_ERL)) { mips_env_set_pc(env, env->CP0_ErrorEPC); - env->CP0_Status &=3D ~(1 << CP0St_ERL); + qatomic_set(&env->CP0_Status, status & ~(1 << CP0St_ERL)); } else { mips_env_set_pc(env, env->CP0_EPC); - env->CP0_Status &=3D ~(1 << CP0St_EXL); + qatomic_set(&env->CP0_Status, status & ~(1 << CP0St_EXL)); } compute_hflags(env); debug_post_eret(env); diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/t= lb_helper.c index 4398c6f80b..9772df5d6c 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -1106,7 +1106,7 @@ void mips_cpu_do_interrupt(CPUState *cs) env->hflags &=3D ~(MIPS_HFLAG_KSU); /* EJTAG probe trap enable is not implemented... */ if (!(env->CP0_Status & (1 << CP0St_EXL))) { - env->CP0_Cause &=3D ~(1U << CP0Ca_BD); + qatomic_and(&env->CP0_Cause, (int32_t)~(1U << CP0Ca_BD)); } env->active_tc.PC =3D env->exception_base + 0x480; set_hflags_for_handler(env); @@ -1134,7 +1134,7 @@ void mips_cpu_do_interrupt(CPUState *cs) env->hflags |=3D MIPS_HFLAG_CP0; env->hflags &=3D ~(MIPS_HFLAG_KSU); if (!(env->CP0_Status & (1 << CP0St_EXL))) { - env->CP0_Cause &=3D ~(1U << CP0Ca_BD); + qatomic_and(&env->CP0_Cause, (int32_t)~(1U << CP0Ca_BD)); } env->active_tc.PC =3D env->exception_base; set_hflags_for_handler(env); @@ -1148,7 +1148,9 @@ void mips_cpu_do_interrupt(CPUState *cs) offset =3D 0x200; } else { uint32_t vector =3D 0; - uint32_t pending =3D (env->CP0_Cause & CP0Ca_IP_mask) >> C= P0Ca_IP; + uint32_t pending =3D + (qatomic_read(&env->CP0_Cause) & CP0Ca_IP_mask) >> + CP0Ca_IP; =20 if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { /* @@ -1246,8 +1248,9 @@ void mips_cpu_do_interrupt(CPUState *cs) case EXCP_CpU: cause =3D 11; update_badinstr =3D 1; - env->CP0_Cause =3D (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | - (env->error_code << CP0Ca_CE); + qatomic_and(&env->CP0_Cause, (int32_t)~(0x3U << CP0Ca_CE)); + qatomic_or(&env->CP0_Cause, + (int32_t)(env->error_code << CP0Ca_CE)); goto set_EPC; case EXCP_OVERFLOW: cause =3D 12; @@ -1305,9 +1308,9 @@ void mips_cpu_do_interrupt(CPUState *cs) set_badinstr_registers(env); } if (env->hflags & MIPS_HFLAG_BMASK) { - env->CP0_Cause |=3D (1U << CP0Ca_BD); + qatomic_or(&env->CP0_Cause, (int32_t)(1U << CP0Ca_BD)); } else { - env->CP0_Cause &=3D ~(1U << CP0Ca_BD); + qatomic_and(&env->CP0_Cause, (int32_t)~(1U << CP0Ca_BD)); } env->CP0_Status |=3D (1 << CP0St_EXL); if (env->insn_flags & ISA_MIPS3) { @@ -1333,8 +1336,8 @@ void mips_cpu_do_interrupt(CPUState *cs) =20 env->active_tc.PC +=3D offset; set_hflags_for_handler(env); - env->CP0_Cause =3D (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | - (cause << CP0Ca_EC); + qatomic_and(&env->CP0_Cause, (int32_t)~(0x1f << CP0Ca_EC)); + qatomic_or(&env->CP0_Cause, cause << CP0Ca_EC); break; default: abort(); --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941685837705.7062298315207; Mon, 13 Jul 2026 04:21:25 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiA-0000CF-2f; Mon, 13 Jul 2026 07:20:34 -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 1wjEi1-0008NU-KQ for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:26 -0400 Received: from mx1.catap.net ([162.55.82.72]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEhz-00080r-Oo for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:25 -0400 Received: by mx1.catap.net (OpenSMTPD) with ESMTPSA id c61819eb (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id 2364ce22; Mon, 13 Jul 2026 13:20:13 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=NL/Oapy lCbwPqAkSIyq0jJKHji1dGAkrtCY0Wcvvvb32A+3/nFOh5skAkmQllvlOWSxsk41+hSH7/ p8+0FaObt9qEyrTZT1iQfHs/bQJmd6PYJKoALiHJhjNbG6rP1RMec0K9kBUJ1MsN+WD1pQ OMk0PuNhOQ6Lu2IIevM7ixgQiu1vmEiqNcDn0iBMnYjkixc2tGi+IixEZLOlxdBKO7OpyX py0U/5nLb+wHs5y6pkpowrWnWio9VgrbP81w8hhjgnf2xsVOk0aJLDVUv6QXOCCnJ5rPb6 gem/Y4hReh0L1lM1O3xgRG2kp/xL+MfICKIsnI9o/ek/vUFP1JpfFNA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=O nOzc/xFLlIzWsGfWekLrWErwBi3UvwPP5rpaHa7JzY=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=vtU4n8WsNoRR1LH6jxVNbItIxhttRuIX8LnbN+ALp73MCXklDCONeE+8wKAx6GMG+Hqj 53a4+8gD3UXlTxd+NRCSfIrGYa0OUlpyQh2fA7DYPmINIQlqpsYAPrmu3zVMEE8NY+qZTs tMTu8cNW4juUmV75Q0c8ucpR65wzECebkDswgSjM1bNo3mjIpB4PdszsrqPaM7uUxoV1iX pT9icZ983cyNULvSfAKNzp/piGi4KJIAxTdHYrtcvhi/FrQJZ68CqOKFi++CwyDfr3u7PX KNxJMV707PbbMyLeZer/kxxG0uY6F+7aYBTqfKd5/mfhXqb+oaFWyae2PcIYcR+w== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=pKG0g+7a; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=ApZiVVbi; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=OnOzc/xF LlIzWsGfWekLrWErwBi3UvwPP5rpaHa7JzY=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=pKG0g+7aJKDqTRkJKU/Ri58DYi0P5S/USnJ JcgaU8qoa8jAi04z5vGGaby8g8KGLpI71fvxHHeab1j8jY08dTPXXLBFrges+ZCfkO6WMr Jmq15DgUrGhf9t8LQoi3Mvde8ESicU8ahbgtPjpFZT98uzTTb39YOGlXHFlfmM0ze+2DAp nvpD91EJQvgyqAYyLHuF4KBuHB3fFTqVrdGZVUrkzKtbrz3gZN4LWPSQ2QIdC2LnBuCjC/ NqyAbE3Zr6JV2kqjACnTH71pXerENA69SPpM3JayZZ6MGT11dCoSW06AiO2fKU/Epda/EE w5UZVshhRGIHsyLMPb3czI8DGyw== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=OnOzc/xFLlIzWsGfWekLrWErwBi3UvwPP5rpaHa7JzY=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=ApZiVVbiwSEunlu8W3 s8X/ze+vUdGIYLUqaCll25heumRDY3wPlPqnOtqXK0DtkMrkso3Abq5KKCkPYGXLZwBw== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 3/9] target/mips: fix MIPS64 segment access checks Date: Mon, 13 Jul 2026 13:16:31 +0200 Message-ID: <20260713112004.4956-4-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=162.55.82.72; envelope-from=kirill@korins.ky; helo=mx1.catap.net X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941686247158500 Content-Type: text/plain; charset="utf-8" The xuseg and xsseg checks only tested UX and SX respectively; kernel and supervisor accesses must instead use the mode bit that applies to the current execution mode. Allow supervisor and kernel mode to use xuseg when SX or KX is set, and allow kernel mode to use xsseg when KX is set. Signed-off-by: Kirill A. Korinsky --- target/mips/system/physaddr.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/mips/system/physaddr.c b/target/mips/system/physaddr.c index 746dbf8aa4..626d7ccc4c 100644 --- a/target/mips/system/physaddr.c +++ b/target/mips/system/physaddr.c @@ -143,7 +143,9 @@ int get_physical_address(CPUMIPSState *env, hwaddr *phy= sical, #if defined(TARGET_MIPS64) } else if (address < 0x4000000000000000ULL) { /* xuseg */ - if (UX && address <=3D (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) { + if (((user_mode && UX) || (supervisor_mode && SX) || + (kernel_mode && KX)) && + address <=3D (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) { ret =3D env->tlb->map_address(env, physical, prot, real_address, access_type); } else { @@ -151,8 +153,8 @@ int get_physical_address(CPUMIPSState *env, hwaddr *phy= sical, } } else if (address < 0x8000000000000000ULL) { /* xsseg */ - if ((supervisor_mode || kernel_mode) && - SX && address <=3D (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) { + if (((supervisor_mode && SX) || (kernel_mode && KX)) && + address <=3D (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) { ret =3D env->tlb->map_address(env, physical, prot, real_address, access_type); } else { --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941643431683.6508408699585; Mon, 13 Jul 2026 04:20:43 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiE-0000G2-Fk; Mon, 13 Jul 2026 07:20: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 1wjEi3-0008Qi-88 for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:27 -0400 Received: from mx1.catap.net ([162.55.82.72]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEi1-00080G-6o for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:26 -0400 Received: by mx1.catap.net (OpenSMTPD) with ESMTPSA id f6712342 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id 5f29de08; Mon, 13 Jul 2026 13:20:14 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=w0SUyWf dUrBsqjIznC2CTeR4iTNV3SzUBHCTsZ4zdOzou3A0pc3UKvJ5GRFH3/OkmegyKK2Z1Wr29 DqvZWegMUagF43H5wypz6/bL4Dba5O6LlYRZqEi8jE7V8wJmv5MM2KJB6MxM/Bk+EGaPq/ RB7gWGwZGr1FU0h26CcTfTeaggIZ+OII2uJDI6fvnGlz9z8srnnSHddF8aQ1gHApdUcrie MlUSeovbTd2qOAE7pjCgtAOv9Wr+V1iPjJ+WD4b7HEwvf/+1tebDV5jMaodpqni8QUpfQq EVLy/Rjk4Fje+gc0UE5A5WIwDStamUJx258yhxz/ZOfVQgbdS7eLEiA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=N QW2Tp5UOyc0O7Lf6TEsWBk57ColeWZGG5gKNEPI2yo=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=ism6jvdWl1jY8WY/Z66hxatl4FTbC5g69MZd4CN9hKftLIcIMAuQC0jGR9mkI8NMAwRV B42uE8//9a3Na6stkpm4d4Q2iAbJA+cDYhTYSHWUEUzImt37bm1rifq84X7om/7enIx7e4 sIMgiL72X8PIzuYharMMPuG1kqu28e8SSxOmfCtL0HtzqI5Le450ObAUrJj2ME1G326dDm ZD52r0hRjZ4B3FssuhB2SSv9vrMTr8xa/CBbMd3SfaQFhiw2x4m1EHL3r3K1YdIHdwZHSd xIRsIuRL7NL1rqds42GT+vJlna8NH6WoMCrc71xrQ/XpCwyWmipdl71aJNuTIdEA== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=ikoK9ZIJ; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=a4/Fmk0U; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=NQW2Tp5U Oyc0O7Lf6TEsWBk57ColeWZGG5gKNEPI2yo=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=ikoK9ZIJgrMyYoeI2s23YKBwbRzcnA/COoq 2YkfyxtSNOBT0JemtBUGDwKnmFi9jDXfpjX9wZ/Z8tF8Uq+kl19LvisIriSgnJC08+MY9c V6ImjZ2GhUFV7u0EWhLdsUnxQPayFOA1jhwQnnBxpyNZP74nML2A8fOCAy2hu3YY8sFrPh Wc4L/EN0iXaZVh/GnSDglU3NZi0DK3CY848QlSW0nldRExNEyla/yQ6Y1weDwxSZCu07KP 5AL/BmBNy44FqSX0uKl0eLJw9847QSb8NUv9pRE+9yzQCm7yZPDlz/dUGl9Qz9va1gS4Pw h4SucBUK/aPdDuqrtEydPyQvhsA== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=NQW2Tp5UOyc0O7Lf6TEsWBk57ColeWZGG5gKNEPI2yo=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=a4/Fmk0UCjAAtGIykJ 5k/6T/qZnZSnYp3rWfCkMw0f5jf3XoZLM9Y+DGftGbcr+NVTPoJ2VAi668a1ivxZv6BA== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 4/9] target/mips: drop hidden TLB shadowing for tlbwr Date: Mon, 13 Jul 2026 13:16:32 +0200 Message-ID: <20260713112004.4956-5-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=162.55.82.72; envelope-from=kirill@korins.ky; helo=mx1.catap.net X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941645990158500 Content-Type: text/plain; charset="utf-8" tlbwr replaces a random architectural TLB entry. Once that happens, the evicted entry must stop translating guest virtual addresses. The current helper keeps the evicted entry in QEMU-only extra TLB state. That makes the old mapping survive until some later operation discards the extra entries. A guest can observe this directly by writing through a TLB mapping, replacing that entry with tlbwr, and writing through the old virtual address again. Invalidate the replaced entry instead of shadowing it. This keeps the translated TLB state aligned with the architectural TLB contents. Signed-off-by: Kirill A. Korinsky --- target/mips/tcg/system/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/t= lb_helper.c index 9772df5d6c..4c0a25e622 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -165,7 +165,7 @@ static void r4k_helper_tlbwr(CPUMIPSState *env) { int r =3D cpu_mips_get_random(env); =20 - r4k_invalidate_tlb(env, r, 1); + r4k_invalidate_tlb(env, r, 0); r4k_fill_tlb(env, r); } =20 --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941667800453.03311186856354; Mon, 13 Jul 2026 04:21:07 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiF-0000HF-Sz; Mon, 13 Jul 2026 07:20:39 -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 1wjEi4-0008U3-33 for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:28 -0400 Received: from mx1.catap.net ([2a01:4f8:271:125e::2]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEi1-00080Z-Pq for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:27 -0400 Received: by mx1.catap.net (OpenSMTPD) with ESMTPSA id 241a4ca5 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id 4426eae8; Mon, 13 Jul 2026 13:20:14 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=kZ/Shac Gr4KmZHFiJMDDeUpyG4LiKS9UVisgYTfMZdpiYEuObaPOW8mVa4EqL1Z02ODsuB4td4kj1 MEe8mQpN8AIHs68e6koMQkseerIPoOuHcvsIFaQiCBZSbL+CO27Y3RnBEcsnhgXU1Wd4LZ dxMUsIWpM43glzR7H/RTrhfDzSZXOPXWzKMtH+t2L13HcQVTwRbwle46kgcyb77C0DBfQ3 AWhKgQei4LJSA57OTdoLdJ1YmkX2effPiIUnoCpQb0J6r/cBYATDz5XLAZPsFu6YsUa5gV IqCdpkgJYGq0gfZNEtsn/tG39QwoqlOSL76Af8RmJvJ42Ajga3xc5lA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=q xt5N3Nng4tirg0uTXWPGdQmo4+L0cGfAZp8AI6ugpo=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=oi7YYTaJcFknreKZupZgWP5zajhPv5HUMO0D1jfmNkXg/lce0qpjRdc9Cw9vGUQWmK5L 9/fMZNFuEU6LN1xw1HfsOwj2iEFXtzHiCNVlokUpGHloXJs+dhCLduUHf5TDQ+RrKQR+0r 00rU4D5CbVY6Oo5mTWMFT7vCKuvZdI9/1ufPdL/YUCz5EcEdxZ1XfAERxKTaeFXcHSwWaJ IOwPVScoQaUPYL+a5Jl8F7zH5qZRzlep9wP5b4Y/mQw35vLX/9MtXze05zKgF6lsjZTLk2 A9XNZZi0bNBbs95giirtRknqa8Ta2W5hl6dRAAm88r3a3nKReKCTucTabqgbQjdw== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=Tc9GiKCz; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=mKWL3JWA; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=qxt5N3Nn g4tirg0uTXWPGdQmo4+L0cGfAZp8AI6ugpo=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=Tc9GiKCzVUjKQrJ+1pHvovlsg2uu/DsVX7C zyAYPOLs8m+plGoPzvVXg9EQQrRX/v7RlEIlQ8UdqAopmx3Iy/+hCBS6hiztVfzIj5B/tx b9HZiMtvyfGq9wM2I6uh+IqeSpQONrftKfh1C7Sj3EiSYyBGA5QAD/HAF6F8q8vBlFEH9x TxqHM8Y84RyOiRZX3qquN4bblPvet/yLthHjkd6jLfW5ysF0DBBVK4SCWBZp+UGdeUrkb6 mg4YL95FxikYqSxJhzlVho+ywRZnLnq9Nrw3XA6Cihmi+x1kz5x5SDCDs+3Vo+whlmnRIT H2E0dYqmG88Db8YsQkGLru3+ddQ== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=qxt5N3Nng4tirg0uTXWPGdQmo4+L0cGfAZp8AI6ugpo=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=mKWL3JWA9bg0M/+ctd wSZ3Me1/dCvfQAOon82HRqkRrO+iQVZK9Aa7MHMkMX12R7KRrha1lA+LqbDdppPEgcDw== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 5/9] target/mips: add Octeon CP0 register state Date: Mon, 13 Jul 2026 13:16:33 +0200 Message-ID: <20260713112004.4956-6-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=2a01:4f8:271:125e::2; envelope-from=kirill@korins.ky; helo=mx1.catap.net 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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941670352158500 Content-Type: text/plain; charset="utf-8" Octeon firmware and kernels use implementation specific CP0 registers for the cycle counter and local memory control. Add CvmCount, CvmCtl, and CvmMemCtl accessors for Octeon CPUs; migrate the new CP0 state, and translate the enabled CVMSEG virtual window to per vCPU backing memory. Signed-off-by: Kirill A. Korinsky --- target/mips/cpu.h | 5 +++ target/mips/system/machine.c | 13 +++++++ target/mips/system/physaddr.c | 22 +++++++++++ target/mips/tcg/op_helper.c | 2 +- target/mips/tcg/system/cp0_helper.c | 10 +++++ target/mips/tcg/system_helper.h.inc | 1 + target/mips/tcg/translate.c | 60 +++++++++++++++++++++++++++++ 7 files changed, 112 insertions(+), 1 deletion(-) diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 319147a948..c6da52e17f 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -341,6 +341,8 @@ typedef struct mips_def_t mips_def_t; #define CP0_REG09__COUNT 0 #define CP0_REG09__SAARI 6 #define CP0_REG09__SAAR 7 +#define CP0_REG09__CVMCOUNT 6 +#define CP0_REG09__CVMCTL 7 /* CP0 Register 10 */ #define CP0_REG10__ENTRYHI 0 #define CP0_REG10__GUESTCTL1 4 @@ -349,6 +351,7 @@ typedef struct mips_def_t mips_def_t; /* CP0 Register 11 */ #define CP0_REG11__COMPARE 0 #define CP0_REG11__GUESTCTL0EXT 4 +#define CP0_REG11__CVMMEMCTL 7 /* CP0 Register 12 */ #define CP0_REG12__STATUS 0 #define CP0_REG12__INTCTL 1 @@ -787,6 +790,7 @@ typedef struct CPUArchState { * CP0 Register 9 */ int32_t CP0_Count; + target_ulong CP0_CvmCtl; #define CP0SAARI_TARGET 0 /* 5..0 */ #define CP0SAAR_BASE 12 /* 43..12 */ #define CP0SAAR_SIZE 1 /* 5..1 */ @@ -801,6 +805,7 @@ typedef struct CPUArchState { * CP0 Register 11 */ int32_t CP0_Compare; + target_ulong CP0_CvmMemCtl; /* * CP0 Register 12 */ diff --git a/target/mips/system/machine.c b/target/mips/system/machine.c index bd1e4002cf..3661da0566 100644 --- a/target/mips/system/machine.c +++ b/target/mips/system/machine.c @@ -372,6 +372,18 @@ static const VMStateDescription mips_vmstate_octeon_cr= ypto =3D { } }; =20 +static const VMStateDescription mips_vmstate_octeon_cp0 =3D { + .name =3D "cpu/octeon_cp0", + .version_id =3D 1, + .minimum_version_id =3D 1, + .needed =3D mips_octeon_needed, + .fields =3D (const VMStateField[]) { + VMSTATE_UINTTL(env.CP0_CvmCtl, MIPSCPU), + VMSTATE_UINTTL(env.CP0_CvmMemCtl, MIPSCPU), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_mips_cpu =3D { .name =3D "cpu", .version_id =3D 21, @@ -490,6 +502,7 @@ const VMStateDescription vmstate_mips_cpu =3D { &mips_vmstate_timer, &mips_vmstate_octeon_multiplier, &mips_vmstate_octeon_crypto, + &mips_vmstate_octeon_cp0, NULL } }; diff --git a/target/mips/system/physaddr.c b/target/mips/system/physaddr.c index 626d7ccc4c..a0538d8b6b 100644 --- a/target/mips/system/physaddr.c +++ b/target/mips/system/physaddr.c @@ -21,6 +21,14 @@ #include "exec/page-protection.h" #include "../internal.h" =20 +/* + * QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM; + * the guest sees the virtual CVMSEG window, not this physical address. + */ +#define OCTEON_CVMSEG_BASE 0x10000000000ULL +#define OCTEON_CVMSEG_SIZE 0x4000ULL +#define OCTEON_CVMSEG_MASK 0x3fffULL + static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) { /* @@ -128,6 +136,20 @@ int get_physical_address(CPUMIPSState *env, hwaddr *ph= ysical, int ret =3D TLBRET_MATCH; target_ulong address =3D real_address; =20 +#if defined(TARGET_MIPS64) + if ((env->insn_flags & INSN_OCTEON) && (env->CP0_CvmMemCtl & 0x100) && + address >=3D 0xffffffffffff8000ULL && + address <=3D 0xffffffffffffbfffULL) { + MIPSCPU *cpu =3D env_archcpu(env); + + *physical =3D OCTEON_CVMSEG_BASE + + CPU(cpu)->cpu_index * OCTEON_CVMSEG_SIZE + + (address & OCTEON_CVMSEG_MASK); + *prot =3D PAGE_READ | PAGE_WRITE; + return TLBRET_MATCH; + } +#endif + if (address <=3D USEG_LIMIT) { /* useg */ uint16_t segctl; diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index df1b5c3734..2bfe661ef2 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -268,7 +268,7 @@ target_ulong helper_rdhwr_cvmcount(CPUMIPSState *env) #ifdef CONFIG_USER_ONLY return cpu_get_host_ticks(); #else - return (uint32_t)cpu_mips_get_count(env); + return helper_mfc0_cvmcount(env); #endif } =20 diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/c= p0_helper.c index ba6b487b75..1c23d2a525 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -23,6 +23,8 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "hw/core/clock.h" #include "cpu.h" #include "internal.h" #include "qemu/host-utils.h" @@ -374,6 +376,14 @@ target_ulong helper_mfc0_count(CPUMIPSState *env) return (int32_t)cpu_mips_get_count(env); } =20 +target_ulong helper_mfc0_cvmcount(CPUMIPSState *env) +{ + MIPSCPU *cpu =3D env_archcpu(env); + + return clock_ns_to_ticks(cpu->clock, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); +} + target_ulong helper_mftc0_entryhi(CPUMIPSState *env) { int other_tc =3D env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); diff --git a/target/mips/tcg/system_helper.h.inc b/target/mips/tcg/system_h= elper.h.inc index eaac5e2c6f..c8b6d8b92c 100644 --- a/target/mips/tcg/system_helper.h.inc +++ b/target/mips/tcg/system_helper.h.inc @@ -31,6 +31,7 @@ DEF_HELPER_1(mftc0_tcschedule, tl, env) DEF_HELPER_1(mfc0_tcschefback, tl, env) DEF_HELPER_1(mftc0_tcschefback, tl, env) DEF_HELPER_1(mfc0_count, tl, env) +DEF_HELPER_1(mfc0_cvmcount, tl, env) DEF_HELPER_1(mftc0_entryhi, tl, env) DEF_HELPER_1(mftc0_status, tl, env) DEF_HELPER_1(mftc0_cause, tl, env) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index e3467d1525..123ba6aecd 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -5460,6 +5460,19 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, in= t reg, int sel) ctx->base.is_jmp =3D DISAS_EXIT; register_name =3D "Count"; break; + case CP0_REG09__CVMCOUNT: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + translator_io_start(&ctx->base); + gen_helper_mfc0_cvmcount(arg, tcg_env); + tcg_gen_ext32s_tl(arg, arg); + register_name =3D "CvmCount"; + break; + case CP0_REG09__CVMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl)= ); + tcg_gen_ext32s_tl(arg, arg); + register_name =3D "CvmCtl"; + break; default: goto cp0_unimplemented; } @@ -5481,6 +5494,13 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, in= t reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare)); register_name =3D "Compare"; break; + case CP0_REG11__CVMMEMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_ld_tl(arg, tcg_env, + offsetof(CPUMIPSState, CP0_CvmMemCtl)); + tcg_gen_ext32s_tl(arg, arg); + register_name =3D "CvmMemCtl"; + break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; @@ -6176,6 +6196,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, in= t reg, int sel) gen_helper_mtc0_count(tcg_env, arg); register_name =3D "Count"; break; + case CP0_REG09__CVMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl)= ); + register_name =3D "CvmCtl"; + break; default: goto cp0_unimplemented; } @@ -6196,6 +6221,13 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, in= t reg, int sel) gen_helper_mtc0_compare(tcg_env, arg); register_name =3D "Compare"; break; + case CP0_REG11__CVMMEMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_st_tl(arg, tcg_env, + offsetof(CPUMIPSState, CP0_CvmMemCtl)); + ctx->base.is_jmp =3D DISAS_STOP; + register_name =3D "CvmMemCtl"; + break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; @@ -6940,6 +6972,17 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, i= nt reg, int sel) ctx->base.is_jmp =3D DISAS_EXIT; register_name =3D "Count"; break; + case CP0_REG09__CVMCOUNT: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + translator_io_start(&ctx->base); + gen_helper_mfc0_cvmcount(arg, tcg_env); + register_name =3D "CvmCount"; + break; + case CP0_REG09__CVMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl)= ); + register_name =3D "CvmCtl"; + break; default: goto cp0_unimplemented; } @@ -6960,6 +7003,12 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, i= nt reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare)); register_name =3D "Compare"; break; + case CP0_REG11__CVMMEMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_ld_tl(arg, tcg_env, + offsetof(CPUMIPSState, CP0_CvmMemCtl)); + register_name =3D "CvmMemCtl"; + break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; @@ -7642,6 +7691,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, i= nt reg, int sel) gen_helper_mtc0_count(tcg_env, arg); register_name =3D "Count"; break; + case CP0_REG09__CVMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl)= ); + register_name =3D "CvmCtl"; + break; default: goto cp0_unimplemented; } @@ -7664,6 +7718,12 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, i= nt reg, int sel) gen_helper_mtc0_compare(tcg_env, arg); register_name =3D "Compare"; break; + case CP0_REG11__CVMMEMCTL: + CP0_CHECK(ctx->insn_flags & INSN_OCTEON); + tcg_gen_st_tl(arg, tcg_env, + offsetof(CPUMIPSState, CP0_CvmMemCtl)); + register_name =3D "CvmMemCtl"; + break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941680931969.6520860658027; Mon, 13 Jul 2026 04:21:20 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiG-0000Im-42; Mon, 13 Jul 2026 07:20:40 -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 1wjEi1-0008MM-EH for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:26 -0400 Received: from mx2.catap.net ([2a01:4f9:3b:5410::2]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEhx-00080d-OD for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:24 -0400 Received: by mx2.catap.net (OpenSMTPD) with ESMTPSA id 2ac6f388 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id 64a294db; Mon, 13 Jul 2026 13:20:14 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=ELfIxP2 2Pd+XKTA763zWZ9LtlSupbog/cRQ5zCj5pyvMbmbN7mvqeo47WAhCiP88a69NK9RURwpKL TdYYA6cuzp/wW4zM9qp/wRqgESzdHaCt0EzxHpvJdULsDDzyG6Wcx+evTrVi6mJwoINqSb Ra5+GsIXlDjDshcYKYrKAWHgORzw2ePIXjsl5eAA+Whi2RviVAnoj/eENr5TN8J+QMtLp2 PMtZIdbk3gs1ZYGxYSLTVMjIkv2HhsORgIm1liDV3n8IUOZ8Mx4szWQ/wxTYlu2gp1yxtE 6n2whO+lulBhTaawxkf+RwUa5gbpXFQGF1GL4fgntLeXC/yWXmMUOxA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=7 J2f9XYzkeb1DC/7GzPyKngK9FJeYiQ/Ftyiu+NCBDA=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=Y5CHYXl5e8D6vrvHmyLHCCe6XoXvmW8KHF8s9PeYjNOk4rs4lK0xGUHDNgodCH268Lpr agTP3QFVziF7J/sVZPxFexskZk3qQOoUyejGFklNVIq9YvY4OF67g7C1BPtpCBaHS7+D57 QhXjf6645wP7W4wK4RGOy+VlNXXdw8H8mkLKz/IIn5yR//Tp3/pqwAazbrZOixR7cWsgS5 +4uYC+bXf9NcGjNQvaLgZ+W56oeUW+PIL9zv91M9DiFaIp6CZ2HPbBJITAnmJfX8sejKlr NumNZcw4WO87bedn2rHs73F78e8rcaNmIZ2uwEVOixJGAPcZ/Zj+77t939yyttGw== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=hr5b0X7+; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=7gL/U7Wk; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=7J2f9XYz keb1DC/7GzPyKngK9FJeYiQ/Ftyiu+NCBDA=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=hr5b0X7+ROtpgXKOWcKQNUc0c7yAgNSid/S E2+PrZWyYehebHF4xtJ9V9QeimuwvQphlze13atth/cqkwevtXUgrqnHCwmRdVtJsZdkV4 nxlGLZabxdhyyPDSiV9/3cXvFiYdR9hNJLytlyF+hcY2T2F9o80puPUoNTmi7oeXw4yhYt 10OI6SKL1cgrxZ2hWNtg5pIPDlFpxwh7ZaWcAt0AYi9DS0Vf8cozqqc0CfUApFCLsnVngR xQXAHqizGy1QXEsrCX5FmgCZx88V5uwhKg1MZjpFAS1A3+W/b+kUnChZPkiF/CLzccnjLE 4797k4EuK/cMRuFras4FXYbmVyQ== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=7J2f9XYzkeb1DC/7GzPyKngK9FJeYiQ/Ftyiu+NCBDA=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=7gL/U7Wko4Yo3VCl0y YdY2ZkeYLLSt/KnXW5qfPHa0wk28PuZfRgkF3AyEUDgzJ1w+F2+LbQ/iJWHwVmUTJWAQ== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 6/9] target/mips: add Octeon CN73xx CPU model Date: Mon, 13 Jul 2026 13:16:34 +0200 Message-ID: <20260713112004.4956-7-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=2a01:4f9:3b:5410::2; envelope-from=kirill@korins.ky; helo=mx2.catap.net 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_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941682305158500 Content-Type: text/plain; charset="utf-8" Add a CN73xx CPU definition for the Octeon III machine. The PRId, FPU FIR, and L2 cache geometry come from EdgeRouter Infinity ER-8-XG data; the remaining CP0 fields come from a Juniper SRX300 CN70xx/CN71xx dump. Signed-off-by: Kirill A. Korinsky --- target/mips/cpu-defs.c.inc | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index cc1916232f..ee638568db 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -1023,6 +1023,48 @@ const mips_def_t mips_defs[] =3D .insn_flags =3D CPU_MIPS64R2 | INSN_OCTEON, .mmu_type =3D MMU_TYPE_R4000, }, + { + /* + * Minimal Octeon CN73xx model for EBB7304 firmware. PRId, FPU FIR, + * and L2 cache geometry match a Ubiquiti EdgeRouter Infinity + * ER-8-XG; the remaining CP0 values are a placeholder Octeon III + * model derived from a Juniper SRX300 CN70xx/CN71xx dump. + */ + .name =3D "OcteonCN73XX", + .CP0_PRid =3D 0x000D9702, + .CP0_Config0 =3D MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_= AT) | + (MMU_TYPE_R4000 << CP0C0_MT) | (1 << CP0C0_VI), + .CP0_Config1 =3D MIPS_CONFIG1 | (1 << CP0C1_FP) | + (0x3F << CP0C1_MMU) | + (0 << CP0C1_IS) | (6 << CP0C1_IL) | (7 << CP0C1_IA)= | + (0 << CP0C1_DS) | (0 << CP0C1_DL) | (7 << CP0C1_DA)= | + (1 << CP0C1_C2) | (1 << CP0C1_PC) | + (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 =3D MIPS_CONFIG2 | (5 << CP0C2_SS) | (6 << CP0C2_SL) | + (15 << CP0C2_SA), + .CP0_Config3 =3D MIPS_CONFIG3 | (1U << CP0C3_M) | + (1 << CP0C3_BP) | (1 << CP0C3_BI) | + (1 << CP0C3_VZ) | (1 << CP0C3_ULRI) | + (1 << CP0C3_RXI) | (1 << CP0C3_LPA), + .CP0_Config4 =3D MIPS_CONFIG4 | (1U << CP0C4_M) | + (0x3c << CP0C4_KScrExist), + .CP0_Config7 =3D 1 << CP0C7_WII, + .CP0_LLAddr_rw_bitmask =3D 0, + .CP0_LLAddr_shift =3D 4, + .CP0_PageGrain =3D (1 << CP0PG_ELPA), + .SYNCI_Step =3D 32, + .CCRes =3D 1, + .CP0_Status_rw_bitmask =3D 0x36F8FFFF, + .CP1_fcr0 =3D (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | + (0x97 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 =3D 0, + .CP1_fcr31_rw_bitmask =3D 0xFF83FFFF, + .SEGBITS =3D 42, + .PABITS =3D 49, + .insn_flags =3D CPU_MIPS64R2 | INSN_OCTEON, + .mmu_type =3D MMU_TYPE_R4000, + }, =20 #endif }; --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941686411944.8084577942769; Mon, 13 Jul 2026 04:21:26 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiC-0000Ek-Ol; Mon, 13 Jul 2026 07:20:36 -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 1wjEi4-0008UJ-8k for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:28 -0400 Received: from mx1.catap.net ([162.55.82.72]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEi1-00080r-Vf for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:27 -0400 Received: by mx1.catap.net (OpenSMTPD) with ESMTPSA id 04e8c581 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:16 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id d2362b45; Mon, 13 Jul 2026 13:20:14 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=RuiDCDN XvJ7ewXGm8VFy+l4r6gHjMLne1t005d4DC1VRLQ8jzmNTR/eeC1u0yDnTCUeW+M+ia/Ygr EsoTnVKi0SduGYboZnoi5WS6fUkQWnldIfNOzUe86mk77wQCOq1ba/0Q8Pr09D91uM0s2Q bu3e4MGQZSZ/NAqrdI7Za5rdRavKKetfYkSOBSQ3WDJmefgXEznOFoCOrLYewMvhWWS/nN eLnRiaS2KAx9SVt/ctn3YMj9lLKQKZ/AjBK3JFKW8uMFyQdRBPEjmOTuLdD47fyBjMfalp 34aQC8Cd6clu5MVs9UrOwrQ3YFeadOk0aJBEnExhyMPd96Hoq484SPg== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=8 EKocf+kGlaTuXdnno/LPGcAjHf3KPZivbjcNNvd8k4=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=HI+su3HKeUF6hz+zNLtksq55mtsOsQj7yZSOYzzc2c0Y2KKpTQg7gEW8gWynq4P0SdYR 8dSWJn/G4PcFtcBL2c8xazDVSwuRYCbGzkv11rQi2sMT3hw9E7cXuBjugmbmvgYtW1jlDc 0lAEMU4+ZM/l+NJpzWKqcOnfHMJjUNn0qyRHouSoWWwB67jyoJlRrmT0vG711Vpc9cTy/a bzarRjlFjY5KYFsR6DJbZjzHKvD6jWyGIr4iNQrDUacRiZlARzkyYHSo+dHV1nf7xiPeQT rxtuBSDG6t+Y9BKH/qmKzwF1N2VJj7iHS55riKsygLefy/bTLoxIfha9SsBwvbdg== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=GjVJFMk5; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=ffhMTbkc; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=8EKocf+k GlaTuXdnno/LPGcAjHf3KPZivbjcNNvd8k4=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=GjVJFMk5UiP5gvM5sMOS4WlzFXZOUEzfX3F PSTXe6o6JsxoK07LIrKCyH9mjU+jY+l49UNckCEAavMs1w5eNkQUT0f+OYgEJfr9QBqQaU bhF3LBBmyPgGvzX/swcyido7Mm65NIzBk/to3yllX1b3ScQQQFMNBcEXsEMe548k0uQ9dE QwJlF9IYYVPjUs49qsG6mfxcwOBspgHjmFmtJyduAbGqehVXURKtrihKFpOhrky88j7G+K S+trksvZ1rQ1eQNnZOMsubwVk7UoXu6AOBgCeIqvivSdWU9pp88ouFmTCFasAlLl7EJ6Dt 9xxn4S386iYRHOOG9TFtxbhFtcg== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=8EKocf+kGlaTuXdnno/LPGcAjHf3KPZivbjcNNvd8k4=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=ffhMTbkcUpp8gjVZTC NTC99IQcWtqIndexhAot1eedv1p5UGeWCwTezs4wGIrPipj/dqii+rKSL+UsOcruqZCA== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 7/9] target/mips: accept Octeon PageMask encodings Date: Mon, 13 Jul 2026 13:16:35 +0200 Message-ID: <20260713112004.4956-8-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=162.55.82.72; envelope-from=kirill@korins.ky; helo=mx1.catap.net X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941688214158500 Content-Type: text/plain; charset="utf-8" Octeon firmware programs contiguous PageMask values that do not follow the generic R4K even maskbits rule. Keep the generic validation rule for other MIPS CPUs, while allowing Octeon CPUs to use the contiguous masks accepted by hardware. Signed-off-by: Kirill A. Korinsky --- target/mips/tcg/system/cp0_helper.c | 15 ++++++++++----- target/mips/tcg/system/tlb_helper.c | 2 +- target/mips/tcg/tcg-internal.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/c= p0_helper.c index 1c23d2a525..40a41805fa 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -882,14 +882,19 @@ void helper_mtc0_memorymapid(CPUMIPSState *env, targe= t_ulong arg1) } } =20 -uint32_t compute_pagemask(uint32_t val) +uint32_t compute_pagemask(CPUMIPSState *env, uint32_t val) { /* Don't care MASKX as we don't support 1KB page */ - uint32_t mask =3D extract32(val, CP0PM_MASK, 16); + uint32_t mask =3D val >> CP0PM_MASK; int maskbits =3D cto32(mask); =20 - /* Ensure no more set bit after first zero, and maskbits even. */ - if ((mask >> maskbits) =3D=3D 0 && maskbits % 2 =3D=3D 0) { + /* + * Generic R4K PageMask values use two mask bits per size step. + * Octeon accepts contiguous byte masks such as 0x003fffff for + * two 2 MiB pages. + */ + if ((mask & (mask + 1)) =3D=3D 0 && + ((env->insn_flags & INSN_OCTEON) || maskbits % 2 =3D=3D 0)) { return mask << CP0PM_MASK; } else { /* When invalid, set to default target page size. */ @@ -899,7 +904,7 @@ uint32_t compute_pagemask(uint32_t val) =20 void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) { - env->CP0_PageMask =3D compute_pagemask(arg1); + env->CP0_PageMask =3D compute_pagemask(env, arg1); } =20 void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/t= lb_helper.c index 4c0a25e622..ae805bff28 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -877,7 +877,7 @@ refill: } } pw_pagemask =3D m >> TARGET_PAGE_BITS; - pw_pagemask =3D compute_pagemask(pw_pagemask << CP0PM_MASK); + pw_pagemask =3D compute_pagemask(env, pw_pagemask << CP0PM_MASK); pw_entryhi =3D (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); { target_ulong tmp_entryhi =3D env->CP0_EntryHi; diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index 950e6afc3f..66ae39551e 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -47,7 +47,7 @@ bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); =20 void mmu_init(CPUMIPSState *env, const mips_def_t *def); =20 -uint32_t compute_pagemask(uint32_t val); +uint32_t compute_pagemask(CPUMIPSState *env, uint32_t val); =20 void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra); uint32_t cpu_mips_get_random(CPUMIPSState *env); --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941682412985.4372249147199; Mon, 13 Jul 2026 04:21:22 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiA-0000Cf-7W; Mon, 13 Jul 2026 07:20:34 -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 1wjEi5-0008Uh-Dl for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:30 -0400 Received: from mx1.catap.net ([162.55.82.72]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEi3-00080G-KM for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:29 -0400 Received: by mx1.catap.net (OpenSMTPD) with ESMTPSA id 655b3f44 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:17 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id 9fabe162; Mon, 13 Jul 2026 13:20:14 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=Fjlc31f 1QAY8DVNww2O6PDsOaYsaRRayc8OQcf0AIMhL6rJQrU+whpj9gxfkg8cGQjN5Xu0i2nRsS KLbDSZ38Q7laYLzA9u+9rXUXc9YY+rGT+5K3FCcusnzeRvX8xF/j1WknPOEzj0ZaAp6YwI j/B3K8NYDxTtQU98VGzm/VpAtus8EMYezDs2QtelcXygXTE7GugPPIfV3qtiUIqZ8AqVEm 1rHoqBFsD7L4bkKy/IKEGt8rJDL+ZmKN3RyVDICpuHBYfnsXqeKzO0C+YBoYEwXrpTMVdw 2EoPezSnBlKiB3rBJDQdZI/h1Xs6aylmvUHsHAkvkYfOiQ8jhnYxHwA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=o Lwt71prGK6I83sbfOW6rxv5mtmxp7hjhNlN3IYrmsM=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=XS0ywIBDS5Wau57egXQsz51xcg8q9GWj4MEsyZH5nKTloW8i4Cd5lxoIpXjF8B35UFYM prHnoS6F2xcCh+3ayU/T4JzhEh2mddYqXXHO8pQZCddqelkNnnSfW57cOMzXM/D12tek1M YNX5DucWPDPiUhjVZu/NhNBv4Dv8PhlaHCw9zuLYa337BiCmQoQ8tYUWDZuBKQMYPM+M47 gP/v8W7hnNRR3ROqng0QqwHkqpchNR+pcW7pJ79uHspCmLgUOd62HqJ9heEp/kvl4MDjz/ PcX37cDSWAiSkJ/1pfZ+00OVmYX0y/X2trB31X522CLwSwNjCKmeoOClMXp2s6DQ== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=Vwo/iDP3; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=/R/dGq+O; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=oLwt71pr GK6I83sbfOW6rxv5mtmxp7hjhNlN3IYrmsM=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=Vwo/iDP3IAABSyS5Oeg7kQB2Ee4EVDkgxYF GCSBqNTZd1xkWkmjYqbmEfKk764Z9h/JikLIJBdZ5tfcI3jm+TuMGTZyXEf6XxYi0KlM2B sJGPD+eN+ZXwJcQTc5P2uGUwQlDCGvchKaXCHqPPUgk/zAJTm112YjYkJAaC5GAljBj4KC X7uJFWcTA1YaHzT/qL7nyUZ16TZpZfquBZB7661EFs9gAWgEBHYw7GpIdfWh9MG8qPZs/m b9/U12ROQdjPemJ49wjAft/HDs3rmw88C107WCvT30GIGtzzGCQ/iXwGq3eqH22jKyVkq+ fxntXRDmUiZeQmGxJnmnKKTszRg== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=oLwt71prGK6I83sbfOW6rxv5mtmxp7hjhNlN3IYrmsM=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=/R/dGq+OVmH8+5+bxB P+AciCjQs2lGBfbx295hGqbf32v7LCEmN+uqYGEQ79fHVohjGs/LOwdHwEIyAlpPv9Cw== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 8/9] target/mips: allow Octeon unaligned accesses Date: Mon, 13 Jul 2026 13:16:36 +0200 Message-ID: <20260713112004.4956-9-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=162.55.82.72; envelope-from=kirill@korins.ky; helo=mx1.catap.net X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941684336158500 Content-Type: text/plain; charset="utf-8" Octeon firmware performs unaligned memory accesses during early DDR setup. Use QEMU's unaligned memory operation mode for Octeon translations, matching the existing treatment for MIPS R6 and Loongson3A. Signed-off-by: Kirill A. Korinsky --- target/mips/tcg/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 123ba6aecd..d83f76e514 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -15193,6 +15193,7 @@ static void mips_tr_init_disas_context(DisasContext= Base *dcbase, CPUState *cs) #endif ctx->default_tcg_memop_mask =3D (!(ctx->insn_flags & ISA_NANOMIPS32) && (ctx->insn_flags & (ISA_MIPS_R6 | + INSN_OCTEON | INSN_LOONGSON3A))) ? MO_UNALN : MO_ALIGN; if (tb_flags & TB_FLAG_MIPS_FIXADE) { ctx->default_tcg_memop_mask =3D MO_UNALN; --=20 2.54.0 From nobody Sat Jul 25 21:24:00 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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; arc=fail (Bad Signature); dmarc=fail(p=none dis=none) header.from=korins.ky Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783941687306922.2425166583475; Mon, 13 Jul 2026 04:21:27 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wjEiI-0000JS-8Y; Mon, 13 Jul 2026 07:20:42 -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 1wjEi5-0008Ui-FE for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:30 -0400 Received: from mx2.catap.net ([65.21.79.163]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wjEhz-00080U-P4 for qemu-devel@nongnu.org; Mon, 13 Jul 2026 07:20:29 -0400 Received: by mx2.catap.net (OpenSMTPD) with ESMTPSA id 4af89f04 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Mon, 13 Jul 2026 13:20:17 +0200 (CEST) Received: from localhost (matebook.home.arpa [local]) by matebook.home.arpa (OpenSMTPD) with ESMTPA id f9a57fc4; Mon, 13 Jul 2026 13:20:14 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=20240125rsa; cv=none; d=mx.catap.net; b=LW3DaKw lPEhfpS/Hnn5Y3ld2BxSVXAu/NbJfx62FoqGqBhvanDwGxUzwpbBpj6NyyA5sBYEsGevgC 1G4Bxs4tb61cETamB8qH+p/A3l9IuaR0nmpTyhQQzobAxomb16A++StReN+TDGAkAbeoD0 N5DcSql0udhlWU6o6rmccKUdGwP7m1orhct72Uh/NIx9d7dWZVc5yb9l2bITqpcrBn34P7 LaTfFncXhRUiYqNX84DCZIY5KX8owWPgCqWsmoEV5EmCPNgZ0dBwYfs1f/5yRZw8r2HQ8m VB9PL6Xd2IwBzN/E7zr5TmWrqk7hUZ+XHN7ENyeiwTvmKJ+ggB5+PGA== ARC-Message-Signature: i=1; a=rsa-sha256; s=20240125rsa; c=simple/simple; bh=V 9pOqKuvbF1n9DobcNfjJP3o9NK1jRfv6H+CBPFB8j8=; h=references:in-reply-to: date:subject:cc:to:from:dkim-signature:dkim-signature; d=mx.catap.net; b=JBIW4L8fzOqplpub8C0kTXr16wKcH+JUFxNQdIfkti2cDLfS+0v4TYoicwJi2WGt5hV0 etOgq4c6jy1zQDxCQRzN6Hu1EC8t5Gf+NawsijAPUC6gqgObTEyP5KBrwjzEAIMPst88BA ND9XSWIQE4FtQ/FVZvldeqh0YvL077N84mcqRLrorhP2whM27/SKUbSUWB2Zmj0y/JXMNG pp4fBwx6B/FgCQ1Z0o9FhJKTT7xcv9pTEEf3/DugfHDQt9+COWWoNpuVIU8y32Ep7/TzbM l5PrdzfgLFlshvfGYdacSgrvyuJm16Uz3rkLCSZuHov0fx/WRrTFHrvPFQWoXm3Q== ARC-Authentication-Results: i=1; mx.catap.net; dkim=pass header.s=20240125rsa header.d=korins.ky header.a=rsa-sha256 header.b=o+PejjUm; dkim=pass header.s=20240125ed25519 header.d=korins.ky header.a=ed25519-sha256 header.b=VyEMx+pk; arc=none; iprev=pass; spf=none smtp.helo=postmaster@matebook.home.arpa; spf=softfail smtp.mailfrom=kirill@korins.ky DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=20240125rsa; bh=V9pOqKuv bF1n9DobcNfjJP3o9NK1jRfv6H+CBPFB8j8=; h=references:in-reply-to:date: subject:cc:to:from; d=korins.ky; b=o+PejjUmfVJgcC7/AknK57JI2koGMvQlt93 Y1EdNUn07C5gMiuTE+CnlkerPt3gbMSway95PdtYSa8oThddM3icjkZ8yDDqLIHVWpLTkS R7hI8mktE5YBWWLrE5zT7T6fjbCH2r3y4tC+VMKhWmpXh3hsvvBMpcPD7jK4zpVnoxrVFr lfRYvx9DLHVAw9Qi6XiOAqvaY7kgLKEm+JVWCKhyhGIxrZ9Z/7OK9RNkiNFZ7L2TgxZ6ZB 8iM2CBtDxfZoNsniq7NaSFcCpQHddO9EWRIsnudcFdbpfwkLRlQbRvQuFv1gm02OGN4ZlH ufKMqfIVl6esKbs1I9V64r8Ia7Q== DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=20240125ed25519; bh=V9pOqKuvbF1n9DobcNfjJP3o9NK1jRfv6H+CBPFB8j8=; h=references: in-reply-to:date:subject:cc:to:from; d=korins.ky; b=VyEMx+pk2AplUxPLv1 LI65kkXKNEdmzQO2RfTMQOqrpuk/VwelwY41NKNA4+tR2S9jujvbwmlfnNIo9a6/yRCQ== From: "Kirill A. Korinsky" To: qemu-devel@nongnu.org Cc: "Kirill A. Korinsky" Subject: [PATCH 9/9] hw/mips: add Octeon III EBB7304 machine Date: Mon, 13 Jul 2026 13:16:37 +0200 Message-ID: <20260713112004.4956-10-kirill@korins.ky> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713112004.4956-1-kirill@korins.ky> References: <20260713112004.4956-1-kirill@korins.ky> 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=65.21.79.163; envelope-from=kirill@korins.ky; helo=mx2.catap.net X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 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, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=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: fail (found 2 invalid signatures) X-ZM-MESSAGEID: 1783941688451158500 Content-Type: text/plain; charset="utf-8" Add a minimal Cavium/Marvell Octeon III machine model for the EBB7304 evaluation board. This work is based on Amir Mehmood's original Octeon host.patch; the basic machine approach and several hardware model ideas come from that work. The model targets modern U-Boot's EBB7304 firmware and OpenBSD/octeon. The same machine also boots the U-Boot image shipped in the ER-8-XG firmware upgrade package. It provides the RAM layout, boot flash aliasing, CIU/CIU3 interrupt state, UART, TWSI/SPD, LMC state, RNG, POW, DWC3 USB, and minimal PCIe support needed to boot with USB storage and an attached PCI e1000. Add a functional test that boots the OpenBSD/octeon ramdisk through U-Boot using QEMU's user mode TFTP server. Signed-off-by: Kirill A. Korinsky --- hw/mips/Kconfig | 10 + hw/mips/meson.build | 1 + hw/mips/octeon.c | 3829 +++++++++++++++++++++++ tests/functional/mips64/meson.build | 2 + tests/functional/mips64/test_octeon3.py | 54 + 5 files changed, 3896 insertions(+) create mode 100644 hw/mips/octeon.c create mode 100644 tests/functional/mips64/test_octeon3.py diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index b59cb2f111..8636003f53 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -67,6 +67,16 @@ config LOONGSON3V select FW_CFG_MIPS select UNIMP =20 +config MIPS_OCTEON + bool + default y + depends on MIPS64 && TARGET_BIG_ENDIAN + imply E1000_PCI + imply PCI_DEVICES + select PFLASH_CFI02 + select SERIAL_MM + select USB_DWC3 + config MIPS_CPS bool select MIPS_ITU if TCG diff --git a/hw/mips/meson.build b/hw/mips/meson.build index 390f0fd7f9..f11dcdf794 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -10,6 +10,7 @@ if 'CONFIG_TCG' in config_all_accel mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c')) +mips_ss.add(when: 'CONFIG_MIPS_OCTEON', if_true: files('octeon.c')) endif =20 hw_arch +=3D {'mips': mips_ss} diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c new file mode 100644 index 0000000000..20b4c16525 --- /dev/null +++ b/hw/mips/octeon.c @@ -0,0 +1,3829 @@ +/* + * QEMU Cavium Octeon board model. + * + * Copyright (c) 2026 Kirill A. Korinsky + * + * This work is based on Amir Mehmood's original Octeon host.patch. + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qemu/bswap.h" +#include "qemu/host-utils.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "hw/char/serial-mm.h" +#include "hw/core/clock.h" +#include "hw/core/irq.h" +#include "hw/core/boards.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev-properties-system.h" +#include "hw/core/sysbus.h" +#include "hw/block/flash.h" +#include "hw/mips/mips.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/usb/hcd-dwc3.h" +#include "system/blockdev.h" +#include "system/cpus.h" +#include "system/memory.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" +#include "exec/cpu-interrupt.h" +#include "qemu/guest-random.h" +#include "qemu/error-report.h" +#include "qemu/atomic.h" +#include "target/mips/cpu.h" + +#define TYPE_OCTEON_MACHINE MACHINE_TYPE_NAME("octeon3") +OBJECT_DECLARE_SIMPLE_TYPE(OcteonMachineState, OCTEON_MACHINE) + +#define TYPE_OCTEON_PCI_HOST "octeon-pci-host" +OBJECT_DECLARE_SIMPLE_TYPE(OcteonPCIHostState, OCTEON_PCI_HOST) + +#define OCTEON_CSR_32BIT_SIZE 4 +#define OCTEON_CSR_64BIT_SIZE 8 +#define OCTEON_CSR_HI32_OFFSET 0 +#define OCTEON_CSR_LO32_OFFSET 4 + +#define OCTEON_MAX_CPUS 16 +/* + * Clock defaults follow a Ubiquiti E1000 EBB7304 U-Boot banner. + * DDR follows the upstream EBB7304 U-Boot board default. + */ +#define OCTEON_DEFAULT_CPU_HZ 1800000000 +#define OCTEON_DEFAULT_REF_HZ 50000000 +#define OCTEON_DEFAULT_IO_HZ 1000000000 +#define OCTEON_DEFAULT_DDR_HZ 800000000 + +/* + * The physical layout follows U-Boot's generic Octeon III + * OCTEON_MAX_PHY_MEM_SIZE value. The synthesized DDR4 SPD below accepts + * power-of-two RAM sizes from 256 MiB through 32 GiB. Keep 1 GiB as the + * tested default. + */ +#define OCTEON_DEFAULT_RAM_SIZE (1 * GiB) +#define OCTEON_MAX_PHY_MEM_SIZE (512 * GiB) + +/* + * The EBB7304 FDT exposes DRAM below and above the + * 0x10000000..0x1fffffff boot-bus/EJTAG hole. + */ +#define OCTEON_DR0_BASE 0x000000000ULL +#define OCTEON_DR0_SIZE (256 * MiB) +#define OCTEON_DR1_BASE 0x020000000ULL +#define OCTEON_DR1_SIZE (OCTEON_MAX_PHY_MEM_SIZE - OCTEON_DR0_= SIZE) + +#define OCTEON_CIU_BASE 0x1070000000000ULL +#define OCTEON_CIU_LEGACY_PAGE_SIZE (4 * KiB) +#define OCTEON_CIU_SIZE (1 * MiB + OCTEON_CIU_LEGACY_PAGE_SIZE) +#define OCTEON_CIU_FUSE 0x728 +#define OCTEON_CIU_SUM_IPI 0x0000000100000000ULL +#define OCTEON_CIU_SUM_UART 0x0000000800000000ULL +#define OCTEON_CIU_SUM_PCI 0x0000001000000000ULL +#define OCTEON_CIU_ENABLE0 0x88006f1800000000ULL +#define OCTEON_CIU_IPI_SUM_BASE 0x008 +#define OCTEON_CIU_IPI_SUM_STRIDE 0x10 +#define OCTEON_CIU_IPI_EN_BASE 0x210 +#define OCTEON_CIU_IPI_EN_STRIDE 0x20 +#define OCTEON_CIU_MBOX_SET_BASE 0x600 +#define OCTEON_CIU_MBOX_CLR_BASE 0x680 +#define OCTEON_CIU_MBOX_SET_STRIDE 8 +#define OCTEON_CIU_GPIO_RX_DAT 0x880 +#define OCTEON_CIU_GPIO_TX_SET 0x888 +#define OCTEON_CIU_GPIO_TX_CLR 0x890 +#define OCTEON_CIU_GPIO_BIT_CFG_STRIDE OCTEON_CSR_64BIT_SIZE +#define OCTEON_CIU_GPIO_BIT_CFGX(x) \ + (0x900 + (x) * OCTEON_CIU_GPIO_BIT_CFG_STRIDE) +#define OCTEON_CIU_GPIO_COUNT 32 +#define OCTEON_CIU_GPIO_INPUTS ((1ULL << OCTEON_CIU_GPIO_COUNT) - 1) +#define OCTEON_CIU_GPIO_BIT_CFG_TX_OE (1ULL << 0) + +#define OCTEON_CIU3_BASE 0x1010000000000ULL +#define OCTEON_CIU3_SIZE 0xb0000000ULL +#define OCTEON_CIU3_PP_RST 0x100 +#define OCTEON_CIU3_PP_RST_PENDING 0x110 +#define OCTEON_CIU3_NMI 0x160 +#define OCTEON_CIU3_FUSE 0x1a0 +#define OCTEON_CIU3_IDT_CTL(idt) ((idt) * 8 + 0x110000U) +#define OCTEON_CIU3_IDT_PP(idt) ((idt) * 32 + 0x120000U) +#define OCTEON_CIU3_IDT_IO(idt) ((idt) * 8 + 0x130000U) +#define OCTEON_CIU3_DEST_PP_INT(cpu) ((cpu) * 8 + 0x200000U) +#define OCTEON_CIU3_DEST_PP_INT_INTSN_SHIFT 32 +#define OCTEON_CIU3_DEST_PP_INT_INTR 0x0000000000000001ULL +#define OCTEON_CIU3_IDT_CTL_IP_NUM 0x7 +#define OCTEON_CIU3_CP0_IRQ_BASE 2 +#define OCTEON_CIU3_CP0_IRQ_COUNT 4 +#define OCTEON_CIU3_ISC_CTL_BASE 0x80000000U +#define OCTEON_CIU3_ISC_W1C_BASE 0x90000000U +#define OCTEON_CIU3_ISC_W1S_BASE 0xa0000000U +#define OCTEON_CIU3_ISC_CTL_IDT 0x0000000000ff0000ULL +#define OCTEON_CIU3_ISC_CTL_IDT_SHIFT 16 +#define OCTEON_CIU3_ISC_CTL_IMP 0x0000000000008000ULL +#define OCTEON_CIU3_ISC_CTL_EN 0x0000000000000002ULL +#define OCTEON_CIU3_ISC_CTL_RAW 0x0000000000000001ULL +#define OCTEON_CIU3_SRC_LEVEL 0x00000001U +#define OCTEON_CIU3_SRC_RAW 0x00000002U +#define OCTEON_CIU3_NINTSN (1U << 20) +#define OCTEON_CIU3_ISC_SIZE (OCTEON_CIU3_NINTSN * 8) +#define OCTEON_CIU3_IDT_COUNT (OCTEON_MAX_CPUS * 4) +#define OCTEON_CIU3_PEM_INTSN_INTA(port) (((0xc0 + (port)) << 12) + 60) +#define OCTEON_CIU3_MBOX_INTSN(cpu) ((cpu) + 0x4000U) +#define OCTEON_CIU3_UART0_INTSN 0x8000 +#define OCTEON_CSR_BASE 0x1180000000000ULL +#define OCTEON_CSR_SIZE 0x100000000ULL +#define OCTEON_PEXP_BASE 0x11f0000000000ULL +#define OCTEON_PEXP_SIZE 0x40000 +#define OCTEON_DPI_BASE 0x1df0000000000ULL +#define OCTEON_DPI_SIZE 0x10000 +#define OCTEON_MIO_BOOT_REG_CFGX(x) \ + (0x0000000 + (x) * OCTEON_CSR_64BIT_SIZE) +#define OCTEON_MIO_BOOT_REG_CFG_INDEX(reg) \ + ((reg) / OCTEON_CSR_64BIT_SIZE) +#define OCTEON_MIO_BOOT_REG_CFG_COUNT 8 +#define OCTEON_MIO_BOOT_REG_CFG_BASE 0x000000000000ffffULL +#define OCTEON_MIO_BOOT_LOC_CFGX(x) \ + (0x0000080 + ((x) & 1) * OCTEON_CSR_64BIT_SIZE) +#define OCTEON_MIO_BOOT_LOC_CFG_INDEX(reg) \ + (((reg) - OCTEON_MIO_BOOT_LOC_CFGX(0)) / OCTEON_CSR_64BIT_SIZE) +#define OCTEON_MIO_BOOT_LOC_ADR 0x0000090 +#define OCTEON_MIO_BOOT_LOC_DAT 0x0000098 +#define OCTEON_MIO_BOOT_LOC_SIZE 0x100 +#define OCTEON_MIO_BOOT_LOC_CFG_BASE 0x000000000ffffff8ULL +#define OCTEON_MIO_BOOT_LOC_CFG_BASE_SHIFT 3 +#define OCTEON_MIO_BOOT_LOC_CFG_EN 0x0000000080000000ULL +#define OCTEON_MIO_BOOT_LOC_ADR_MASK 0xf8 +/* Fuses are left unprogrammed; firmware only needs reads to complete. */ +#define OCTEON_MIO_FUS_DAT2 0x0001410 +#define OCTEON_MIO_FUS_RCMD 0x0001500 +#define OCTEON_MIO_FUS_RCMD_PEND 0x0000000000001000ULL +#define OCTEON_MIO_FUS_RCMD_DAT 0x0000000000ff0000ULL +#define OCTEON_MIO_EMM_DMA_FIFO_CFG 0x0000160 +#define OCTEON_MIO_EMM_DMA_FIFO_ADR 0x0000170 +#define OCTEON_MIO_EMM_DMA_FIFO_CMD 0x0000178 +#define OCTEON_MIO_EMM_DMA_CFG 0x0000180 +#define OCTEON_MIO_EMM_DMA_ADR 0x0000188 +#define OCTEON_MIO_EMM_DMA_INT 0x0000190 +#define OCTEON_MIO_EMM_CFG 0x0002000 +#define OCTEON_MIO_EMM_MODEX(x) \ + (0x0002008 + (x) * OCTEON_CSR_64BIT_SIZE) +#define OCTEON_MIO_EMM_MODE_COUNT 4 +#define OCTEON_MIO_EMM_INT_EN_OLD 0x0002040 +#define OCTEON_MIO_EMM_SWITCH 0x0002048 +#define OCTEON_MIO_EMM_DMA 0x0002050 +#define OCTEON_MIO_EMM_CMD 0x0002058 +#define OCTEON_MIO_EMM_RSP_STS 0x0002060 +#define OCTEON_MIO_EMM_RSP_LO 0x0002068 +#define OCTEON_MIO_EMM_RSP_HI 0x0002070 +#define OCTEON_MIO_EMM_INT 0x0002078 +#define OCTEON_MIO_EMM_WDOG 0x0002088 +#define OCTEON_MIO_EMM_SAMPLE 0x0002090 +#define OCTEON_MIO_EMM_STS_MASK 0x0002098 +#define OCTEON_MIO_EMM_RCA 0x00020a0 +#define OCTEON_MIO_EMM_BUF_IDX 0x00020e0 +#define OCTEON_MIO_EMM_BUF_DAT 0x00020e8 +#define OCTEON_MIO_EMM_DEBUG 0x00020f8 +#define OCTEON_MIO_EMM_NODE_OFFSET 0x0002000 +#define OCTEON_MIO_EMM_CMD_VAL (1ULL << 59) +#define OCTEON_MIO_EMM_DMA_VAL (1ULL << 59) +#define OCTEON_MIO_EMM_DMA_BLOCK_CNT_SHIFT 32 +#define OCTEON_MIO_EMM_DMA_BLOCK_CNT_MASK \ + (0xffffULL << OCTEON_MIO_EMM_DMA_BLOCK_CNT_SHIFT) +#define OCTEON_MIO_EMM_SWITCH_EXE (1ULL << 59) +#define OCTEON_MIO_EMM_CMD_IDX_SHIFT 32 +#define OCTEON_MIO_EMM_CMD_IDX_BITS 6 +#define OCTEON_MIO_EMM_CMD_IDX_MASK \ + ((1U << OCTEON_MIO_EMM_CMD_IDX_BITS) - 1) +#define OCTEON_MIO_EMM_CMD_BUS_ID_SHIFT 60 +#define OCTEON_MIO_EMM_BUS_ID_MASK (OCTEON_MIO_EMM_MODE_COUNT - 1) +#define OCTEON_MIO_EMM_RSP_STS_CMD_DONE (1ULL << 0) +#define OCTEON_MIO_EMM_RSP_STS_CMD_IDX_SHIFT 1 +#define OCTEON_MIO_EMM_RSP_STS_RSP_TIMEOUT (1ULL << 13) +#define OCTEON_MIO_EMM_RSP_STS_DMA_VAL (1ULL << 57) +#define OCTEON_MIO_EMM_RSP_STS_DMA_PEND (1ULL << 56) +#define OCTEON_MIO_EMM_RSP_STS_BUS_ID_SHIFT 60 +#define OCTEON_MIO_EMM_DMA_INT_DONE (1ULL << 0) +#define OCTEON_MIO_EMM_INT_CMD_DONE (1ULL << 1) +#define OCTEON_MIO_EMM_INT_CMD_ERR (1ULL << 3) +#define OCTEON_MIO_EMM_INT_DMA_DONE (1ULL << 2) +#define OCTEON_MIO_EMM_INT_DMA_ERR (1ULL << 4) +#define OCTEON_MIO_EMM_INT_SWITCH_DONE (1ULL << 5) +#define OCTEON_MIO_EMM_INT_SWITCH_ERR (1ULL << 6) +#define OCTEON_MIO_EMM_SWITCH_MODE_MASK ((1ULL << 49) - 1) +#define OCTEON_MIO_EMM_CFG_BUS_EN_MASK \ + ((1ULL << OCTEON_MIO_EMM_MODE_COUNT) - 1) +#define OCTEON_MIO_EMM_LOW_SPEED_HZ 400000 +#define OCTEON_MIO_EMM_MODE_CLK_LO_MASK 0x000000000000ffffULL +#define OCTEON_MIO_EMM_MODE_CLK_HI_MASK 0x00000000ffff0000ULL +#define OCTEON_MIO_EMM_MODE_CLK_HI_SHIFT 16 +#define OCTEON_MIO_EMM_BUF_IDX_INC (1ULL << 16) +#define OCTEON_MIO_EMM_BUF_WORD_SIZE OCTEON_CSR_64BIT_SIZE +#define OCTEON_MIO_EMM_BUF_SIZE 512 +#define OCTEON_MIO_EMM_BUF_ENTRY_COUNT \ + (OCTEON_MIO_EMM_BUF_SIZE / OCTEON_MIO_EMM_BUF_WORD_SIZE) +#define OCTEON_MIO_EMM_BUF_IDX_OFF_MASK \ + (OCTEON_MIO_EMM_BUF_ENTRY_COUNT - 1) +#define OCTEON_MMC_CMD_GO_IDLE_STATE 0 +#define OCTEON_MMC_CMD_BUSTEST_R 14 +#define OCTEON_MMC_CMD_BUSTEST_W 19 +/* SD/MMC CMD19 request patterns and CMD14 response patterns. */ +#define OCTEON_MMC_BUSTEST_8_REQ0 0x55 +#define OCTEON_MMC_BUSTEST_8_REQ1 0xaa +#define OCTEON_MMC_BUSTEST_8_RESP0 0xaa +#define OCTEON_MMC_BUSTEST_8_RESP1 0x55 +#define OCTEON_MMC_BUSTEST_4_REQ0 0x5a +#define OCTEON_MMC_BUSTEST_4_REQ4 0x99 +#define OCTEON_MMC_BUSTEST_4_REQ5 0x50 +#define OCTEON_MMC_BUSTEST_4_REQ6 0x0f +#define OCTEON_MMC_BUSTEST_4_RESP0 0xa5 +#define OCTEON_MMC_BUSTEST_1_REQ0 0x80 +#define OCTEON_MMC_BUSTEST_1_REQ1 0x70 +#define OCTEON_MMC_BUSTEST_1_REQ2 0x78 +#define OCTEON_MMC_BUSTEST_1_REQ3 0x01 +#define OCTEON_MMC_BUSTEST_1_RESP0 0x40 +#define OCTEON_RST_BOOT 0x6001600 +#define OCTEON_RST_SOFT_RST 0x6001680 +/* U-Boot uses the reset controller to park and release secondary cores. */ +#define OCTEON_RST_PP_POWER 0x6001700 +#define OCTEON_LMC_BASE 0x88000000 +#define OCTEON_LMC_SEQ_CTL 0x48 +#define OCTEON_LMC_SEQ_COMPLETE (1ULL << 5) +#define OCTEON_LMC_MPR_DATA0 0x70 +#define OCTEON_LMC_MPR_DATA1 0x78 +#define OCTEON_LMC_DCLK_CNT 0x1e0 +#define OCTEON_LMC_PHY_CTL 0x210 +#define OCTEON_LMC_PHY_DSK_COMPLETE (1ULL << 49) +#define OCTEON_LMC_RLEVEL_RANK 0x280 +#define OCTEON_LMC_RLEVEL_STATUS (3ULL << 54) +#define OCTEON_LMC_RLEVEL_DBG 0x2a8 +#define OCTEON_LMC_RLEVEL_MASK 0x3f0 +#define OCTEON_LMC_WLEVEL_RANK 0x2c0 +#define OCTEON_LMC_RANK_COUNT 4 +#define OCTEON_LMC_WLEVEL_STATUS (3ULL << 45) +#define OCTEON_LMC_WLEVEL_DBG 0x308 +#define OCTEON_LMC_WLEVEL_MASK (0x0fULL << 4) +#define OCTEON_LMC_COUNT 4 +#define OCTEON_LMC_STRIDE 0x1000000 +#define OCTEON_RST_BOOT_C_MUL_SHIFT 30 +#define OCTEON_RST_BOOT_PNR_MUL_SHIFT 24 + +#define OCTEON_FPA_BASE 0x1280000000000ULL +#define OCTEON_FPA_SIZE 0x1000 +#define OCTEON_FPA_CLK_COUNT 0xf0 + +#define OCTEON_IPD_BASE 0x14f0000000000ULL +#define OCTEON_IPD_SIZE 0x1000 +#define OCTEON_IPD_CLK_COUNT 0x338 + +#define OCTEON_PKO_BASE 0x1540000000000ULL +#define OCTEON_PKO_SIZE 0x1000000 + +#define OCTEON_RNG_BASE 0x1400000000000ULL +#define OCTEON_RNG_SIZE 0x8 + +#define OCTEON_POW_BASE 0x1670000000000ULL +#define OCTEON_POW_SIZE 0x2000 + +#define OCTEON_USB_COUNT 2 +#define OCTEON_UCTL_SIZE 0x100 +#define OCTEON_USB0_UCTL_BASE 0x1180068000000ULL +#define OCTEON_USB1_UCTL_BASE 0x1180069000000ULL +#define OCTEON_USB0_DWC3_BASE 0x1680000000000ULL +#define OCTEON_USB1_DWC3_BASE 0x1690000000000ULL +#define OCTEON_UCTL_SHIM_CFG 0xe8 +#define OCTEON_UCTL_SHIM_CFG_CSR_BYTE_SWAP 0x3 +#define OCTEON_UCTL_SHIM_CFG_CSR_NATIVE 0x3 +#define OCTEON_CIU3_USB0_INTSN 0x68080 +#define OCTEON_CIU3_USB1_INTSN 0x69080 + +#define OCTEON_TWSI_COUNT 2 +#define OCTEON_TWSI_SIZE 0x200 +#define OCTEON_TWSI0_BASE 0x1180000001000ULL +#define OCTEON_TWSI1_BASE 0x1180000001200ULL +#define OCTEON_TWSI_SW_TWSI 0x00 +#define OCTEON_TWSI_INT 0x10 +#define OCTEON_TWSI_SW_DATA_MASK 0xffffffffULL +#define OCTEON_TWSI_SW_EOP_IA_SHIFT 32 +#define OCTEON_TWSI_SW_R (1ULL << 56) +#define OCTEON_TWSI_SW_OP_SHIFT 57 +#define OCTEON_TWSI_SW_V (1ULL << 63) +#define OCTEON_TWSI_OP_EOP_IA 6 +#define OCTEON_TWSI_EOP_DATA 1 +#define OCTEON_TWSI_EOP_CTL 2 +#define OCTEON_TWSI_EOP_STAT 3 +#define OCTEON_TWSI_CTL_AAK 0x04 +#define OCTEON_TWSI_CTL_IFLG 0x08 +#define OCTEON_TWSI_CTL_STP 0x10 +#define OCTEON_TWSI_CTL_STA 0x20 +#define OCTEON_TWSI_STAT_START 0x08 +#define OCTEON_TWSI_STAT_TXADDR_ACK 0x18 +#define OCTEON_TWSI_STAT_TXADDR_NAK 0x20 +#define OCTEON_TWSI_STAT_TXDATA_ACK 0x28 +#define OCTEON_TWSI_STAT_TXDATA_NAK 0x30 +#define OCTEON_TWSI_STAT_RXADDR_ACK 0x40 +#define OCTEON_TWSI_STAT_RXADDR_NAK 0x48 +#define OCTEON_TWSI_STAT_RXDATA_ACK 0x50 +#define OCTEON_TWSI_STAT_RXDATA_NAK 0x58 +#define OCTEON_TWSI_STAT_IDLE 0xf8 +#define OCTEON_SPD_EEPROM_SIZE 139 +#define OCTEON_SPD_EEPROM_COUNT 1 +#define OCTEON_SPD_BUS 1 +#define OCTEON_SPD_ADDR 0x50 +#define OCTEON_SPD_MIN_ROW_BITS 12 +#define OCTEON_SPD_MAX_ROW_BITS 18 +#define OCTEON_SPD_MIN_COL_BITS 9 +#define OCTEON_SPD_MAX_COL_BITS 10 +#define OCTEON_SPD_BANK_BITS 4 +#define OCTEON_SPD_MIN_RAM_SIZE \ + (1ULL << (OCTEON_SPD_MIN_ROW_BITS + OCTEON_SPD_MIN_COL_BITS + \ + OCTEON_SPD_BANK_BITS + 3)) +#define OCTEON_SPD_MAX_RAM_SIZE \ + (1ULL << (OCTEON_SPD_MAX_ROW_BITS + OCTEON_SPD_MAX_COL_BITS + \ + OCTEON_SPD_BANK_BITS + 3)) +#define OCTEON_SPD_DDR4_TCKMIN 18 +#define OCTEON_SPD_DDR4_FINE_TCKMIN 125 +#define OCTEON_SPD_DDR4_MTB_PS 125 +#define OCTEON_SPD_DDR4_FTB_PS 1 + +#define OCTEON_UART0_BASE 0x1180000000800ULL +#define OCTEON_UART0_ALIAS_BASE 0x1180000000c00ULL +#define OCTEON_UART_TX_REG 0x40 +#define OCTEON_UART_TX_HI32_OFFSET OCTEON_CSR_HI32_OFFSET +#define OCTEON_UART_TX_SIZE OCTEON_CSR_64BIT_SIZE + +/* + * QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM; + * the guest sees the virtual CVMSEG window, not this physical address. + */ +#define OCTEON_CVMSEG_BASE 0x10000000000ULL +#define OCTEON_CVMSEG_SIZE 0x4000 +#define OCTEON_CVMSEG_TOTAL_SIZE (OCTEON_MAX_CPUS * OCTEON_CVMSEG_SIZE) + +#define OCTEON_PCIE_CFG_BASE 0x1190400000000ULL +#define OCTEON_PCIE_CFG_SIZE 0x20000000ULL +#define OCTEON_PCIE_SLI_CFG_BASE 0x1190c00000000ULL +#define OCTEON_PCIE_SLI_CFG_PORTS 4 +#define OCTEON_PCIE_SLI_CFG_PORT_SIZE (1ULL << 32) +#define OCTEON_PCIE_SLI_CFG_SIZE \ + (OCTEON_PCIE_SLI_CFG_PORTS * OCTEON_PCIE_SLI_CFG_PORT_SIZE) +#define OCTEON_PCIE0_IO_BASE 0x11a0400000000ULL +#define OCTEON_PCIE0_IO_SIZE (1ULL << 32) +#define OCTEON_PCIE0_MEM_BASE 0x11b0000000000ULL +#define OCTEON_PCIE0_MEM_SIZE (1ULL << 32) +#define OCTEON_SLI_MEM_ACCESS_SUBID_BASE 0x000000e0 +#define OCTEON_SLI_MEM_ACCESS_SUBID_STRIDE 16 +#define OCTEON_SLI_MEM_ACCESS_SUBID_ESR_M 0x0000003000000000ULL +#define OCTEON_SLI_MEM_ACCESS_SUBID_ESR_S 36 +#define OCTEON_SLI_MEM_ACCESS_SUBID_ESW_M 0x0000000c00000000ULL +#define OCTEON_SLI_MEM_ACCESS_SUBID_ESW_S 34 +#define OCTEON_SLI_MEM_ACCESS_SWAP 3 +#define OCTEON_PCIE0_ENDPOINT_DEVFN PCI_DEVFN(0x0b, 0) + +#define OCTEON_GSERX_CFG0 0x90000080 +#define OCTEON_GSERX_QLM_STAT0 0x900000a0 +#define OCTEON_RST_CTLX0 0x6001640 +#define OCTEON_RST_SOFT_PRSTX0 0x60016c0 +#define OCTEON_PEMX_CFG_RD0 0xc0000030 +#define OCTEON_PEMX_CFG_WR0 0xc0000028 +#define OCTEON_PEMX_BIST_STATUS0 0xc0000440 +#define OCTEON_PEMX_ON0 0xc0000420 +#define OCTEON_PEMX_STRAP0 0xc0000408 + +#define OCTEON_RST_CTL_HOST_MODE (1ULL << 6) +#define OCTEON_RST_CTL_RST_DONE (1ULL << 8) +#define OCTEON_PCIE_CFG006 0x018 +#define OCTEON_PCIE_CFG031 0x07c +#define OCTEON_PCIE_CFG032 0x080 +#define OCTEON_PCIE_CFG068 0x110 +#define OCTEON_PCIE_CFG_STORE_BASE (1ULL << 63) + +#define OCTEON_FLASH_BASE 0x1f400000ULL +#define OCTEON_FLASH_RESET_BASE 0x1fc00000ULL +#define OCTEON_FLASH_RESET_SIZE (4 * MiB) +#define OCTEON_FLASH_MAIN_SECTORS 127 +#define OCTEON_FLASH_MAIN_SECTOR_SIZE (64 * KiB) +#define OCTEON_FLASH_ENV_SECTORS 8 +#define OCTEON_FLASH_ENV_SECTOR_SIZE 0x2000 +#define OCTEON_FLASH_SIZE \ + (OCTEON_FLASH_MAIN_SECTORS * OCTEON_FLASH_MAIN_SECTOR_SIZE + \ + OCTEON_FLASH_ENV_SECTORS * OCTEON_FLASH_ENV_SECTOR_SIZE) +#define OCTEON_FLASH_ENV_OFFSET \ + (OCTEON_FLASH_SIZE - OCTEON_FLASH_ENV_SECTOR_SIZE) +#define OCTEON_FLASH_WIDTH 2 +#define OCTEON_MIO_BOOT_REG_CFG_RESET_BASE (OCTEON_FLASH_RESET_BASE >> 16) +#define OCTEON_BOOTBUS_LED_CS 4 +#define OCTEON_BOOTBUS_LED_SIZE (64 * KiB) + +typedef enum OcteonIRQ { + OCTEON_IRQ_UART, + OCTEON_IRQ_PCI, + OCTEON_IRQ_USB0, + OCTEON_IRQ_USB1, + OCTEON_IRQ_COUNT, +} OcteonIRQ; + +typedef enum OcteonCiu3Source { + OCTEON_CIU3_SRC_PCI_INTA, + OCTEON_CIU3_SRC_UART0, + OCTEON_CIU3_SRC_USB0, + OCTEON_CIU3_SRC_USB1, + OCTEON_CIU3_SRC_MBOX0, + OCTEON_CIU3_SRC_COUNT =3D OCTEON_CIU3_SRC_MBOX0 + OCTEON_MAX_CPUS, +} OcteonCiu3Source; + +typedef struct OcteonState OcteonState; + +struct OcteonMachineState { + MachineState parent_obj; + uint64_t cpu_hz; + uint64_t ref_hz; + uint64_t io_hz; + uint64_t ddr_hz; +}; + +struct OcteonPCIHostState { + PCIHostState parent_obj; + MemoryRegion mem; + MemoryRegion io; +}; + +typedef struct OcteonCPUState { + OcteonState *board; + MIPSCPU *cpu; + bool boot_cpu; +} OcteonCPUState; + +typedef struct OcteonSpdEepromState { + unsigned int bus; + uint8_t addr; + uint16_t offset; + uint8_t data[OCTEON_SPD_EEPROM_SIZE]; +} OcteonSpdEepromState; + +typedef struct OcteonLmcState { + GHashTable *regs; +} OcteonLmcState; + +typedef struct OcteonTWSIState { + OcteonState *board; + MemoryRegion mmio; + unsigned int bus; + uint64_t sw_twsi; + uint64_t int_reg; + uint8_t ctl; + uint8_t stat; + uint8_t data; + uint8_t slave_addr; + uint8_t write_buf[2]; + unsigned int write_len; + bool have_slave; + bool read_transfer; + bool addr_phase; +} OcteonTWSIState; + +typedef struct OcteonUsbState { + OcteonState *board; + USBDWC3 *dwc3; + unsigned int index; + MemoryRegion uctl; + MemoryRegion dwc3_window; + AddressSpace dwc3_as; + GHashTable *regs; +} OcteonUsbState; + +struct OcteonState { + MachineState *machine; + Clock *cpuclk; + uint64_t cpu_hz; + uint64_t ref_hz; + uint64_t io_hz; + uint64_t ddr_hz; + OcteonCPUState cpu[OCTEON_MAX_CPUS]; + unsigned int cpu_count; + uint64_t firmware_entry; + + MemoryRegion dr0; + MemoryRegion dr1; + MemoryRegion *flash; + MemoryRegion boot_flash; + MemoryRegion boot_flash_alias; + MemoryRegion bootbus_led; + MemoryRegion mio_boot_loc; + MemoryRegion cvmseg; + MemoryRegion ciu; + MemoryRegion ciu3; + MemoryRegion csr; + MemoryRegion pexp; + MemoryRegion dpi; + MemoryRegion fpa; + MemoryRegion ipd; + MemoryRegion pko; + MemoryRegion rng; + MemoryRegion pow; + OcteonLmcState lmc[OCTEON_LMC_COUNT]; + OcteonTWSIState twsi[OCTEON_TWSI_COUNT]; + OcteonUsbState usb[OCTEON_USB_COUNT]; + OcteonSpdEepromState spd[OCTEON_SPD_EEPROM_COUNT]; + SerialMM *uart; + MemoryRegion uart_alias; + MemoryRegion uart_tx; + MemoryRegion uart_alias_tx; + MemoryRegion pcie_cfg; + MemoryRegion pcie_sli_cfg; + MemoryRegion pcie0_io; + MemoryRegion pcie0_mem; + AddressSpace pcie0_mem_as; + + uint64_t mio_boot_reg_cfg[OCTEON_MIO_BOOT_REG_CFG_COUNT]; + uint64_t mio_boot_loc_cfg[2]; + uint64_t mio_boot_loc_adr; + uint64_t mio_fus_rcmd; + uint64_t mio_emm_rsp_sts; + uint64_t mio_emm_rsp_lo; + uint64_t mio_emm_rsp_hi; + uint64_t mio_emm_int; + uint64_t mio_emm_dma_int; + uint16_t mio_emm_buf_idx; + bool mio_emm_buf_inc; + uint8_t mio_emm_buf[OCTEON_MIO_EMM_BUF_SIZE]; + uint64_t rst_pp_power; + hwaddr mio_boot_loc_base; + uint8_t mio_boot_loc_data[OCTEON_MIO_BOOT_LOC_SIZE]; + bool mio_boot_loc_mapped; + hwaddr bootbus_led_base; + bool bootbus_led_mapped; + uint32_t pcie_cfg_rd_addr; + uint32_t ciu_mbox[OCTEON_MAX_CPUS]; + uint64_t ciu_ipi_en[OCTEON_MAX_CPUS]; + uint64_t ciu_gpio_rx; + uint64_t ciu_gpio_tx; + uint64_t ciu_gpio_bit_cfg[OCTEON_CIU_GPIO_COUNT]; + bool uart_pending; + bool pci_pending; + uint64_t ciu3_pp_rst; + uint32_t ciu3_src_state[OCTEON_CIU3_SRC_COUNT]; + uint64_t ciu3_src_ctl[OCTEON_CIU3_SRC_COUNT]; + uint64_t ciu3_idt_ctl[OCTEON_CIU3_IDT_COUNT]; + uint64_t ciu3_idt_pp[OCTEON_CIU3_IDT_COUNT]; + uint64_t ciu3_idt_io[OCTEON_CIU3_IDT_COUNT]; + uint16_t ciu3_src_cursor[OCTEON_MAX_CPUS][OCTEON_CIU3_CP0_IRQ_COUNT]; + uint8_t ciu3_irq_line[OCTEON_MAX_CPUS][OCTEON_CIU3_CP0_IRQ_COUNT]; + + PCIBus *pci_bus; + GHashTable *csr_values; +}; + +static const uint8_t octeon_spd_eeprom_template[OCTEON_SPD_EEPROM_SIZE] = =3D { + [0] =3D 0x23, [1] =3D 0x11, [2] =3D 0x0c, [3] =3D 0x02, + [4] =3D 0x82, [5] =3D 0x19, [11] =3D 0x03, [12] =3D 0x01, + [13] =3D 0x03, [18] =3D 0x0a, [19] =3D 0x20, [20] =3D 0x78, + [24] =3D 0x6e, [25] =3D 0x6e, [26] =3D 0x6e, [27] =3D 0x11, + [29] =3D 0x6e, [31] =3D 0x05, [32] =3D 0x70, [33] =3D 0x03, + [34] =3D 0xd0, [35] =3D 0x02, [37] =3D 0xa8, [38] =3D 0x20, + [39] =3D 0x27, [40] =3D 0x28, [126] =3D 0x16, [127] =3D 0x2f, +}; + +static guint octeon_uint64_hash(gconstpointer v) +{ + uint64_t value =3D *(const uint64_t *)v; + + return value ^ (value >> 32); +} + +static gboolean octeon_uint64_equal(gconstpointer a, gconstpointer b) +{ + return *(const uint64_t *)a =3D=3D *(const uint64_t *)b; +} + +static uint64_t octeon_read64(uint64_t value, hwaddr addr, unsigned size) +{ + if (size =3D=3D 4) { + if (addr & 4) { + return value & 0xffffffffU; + } + return value >> 32; + } + + return value; +} + +static uint64_t octeon_write64(uint64_t old, hwaddr addr, + uint64_t value, unsigned size) +{ + if (size =3D=3D 4) { + if (addr & 4) { + return (old & 0xffffffff00000000ULL) | (value & 0xffffffffU); + } + return (old & 0xffffffffULL) | ((value & 0xffffffffU) << 32); + } + + return value; +} + +static uint64_t octeon_atomic_write64(uint64_t *ptr, hwaddr addr, + uint64_t value, unsigned size, + uint64_t and_mask, uint64_t or_mask, + uint64_t *oldp) +{ + uint64_t old; + uint64_t new; + uint64_t cmp =3D qatomic_read(ptr); + + do { + old =3D cmp; + new =3D (octeon_write64(old, addr, value, size) & and_mask) | or_m= ask; + cmp =3D qatomic_cmpxchg(ptr, old, new); + } while (old !=3D cmp); + + if (oldp) { + *oldp =3D old; + } + + return new; +} + +static uint64_t octeon_clock_count(uint64_t hz) +{ + uint64_t now =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + return muldiv64(now, hz, NANOSECONDS_PER_SECOND); +} + +static uint16_t octeon_spd_crc16(const uint8_t *ptr, int count) +{ + int crc =3D 0; + int i; + + while (--count >=3D 0) { + crc ^=3D *ptr++ << 8; + for (i =3D 0; i < 8; i++) { + if (crc & 0x8000) { + crc =3D (crc << 1) ^ 0x1021; + } else { + crc <<=3D 1; + } + } + } + + return crc & 0xffff; +} + +static bool octeon_spd_geometry(uint64_t ram_size, int *row_bits, + int *col_bits, int *density_code) +{ + uint64_t ram_mb; + int pbank_lsb; + int row_col_bits; + int col; + int row; + int density; + + if (ram_size < OCTEON_SPD_MIN_RAM_SIZE || + ram_size > OCTEON_SPD_MAX_RAM_SIZE || + !is_power_of_2(ram_size)) { + return false; + } + + ram_mb =3D ram_size / MiB; + pbank_lsb =3D ctz64(ram_size); + row_col_bits =3D pbank_lsb - 3 - OCTEON_SPD_BANK_BITS; + + col =3D MIN(OCTEON_SPD_MAX_COL_BITS, + row_col_bits - OCTEON_SPD_MIN_ROW_BITS); + col =3D MAX(OCTEON_SPD_MIN_COL_BITS, col); + row =3D row_col_bits - col; + density =3D ctz64(ram_mb) - 8; + + if (row < OCTEON_SPD_MIN_ROW_BITS || + row > OCTEON_SPD_MAX_ROW_BITS || + density < 0 || density > 7) { + return false; + } + + if (row_bits) { + *row_bits =3D row; + } + if (col_bits) { + *col_bits =3D col; + } + if (density_code) { + *density_code =3D density; + } + return true; +} + +static void octeon_validate_ram_size(uint64_t ram_size) +{ + if (!octeon_spd_geometry(ram_size, NULL, NULL, NULL)) { + error_report("octeon3 RAM size must be a power of two from " + "%" PRIu64 " MB to %" PRIu64 " GB", + (uint64_t)OCTEON_SPD_MIN_RAM_SIZE / MiB, + (uint64_t)OCTEON_SPD_MAX_RAM_SIZE / GiB); + exit(1); + } +} + +static void octeon_validate_clocks(OcteonMachineState *oms) +{ + if (!oms->cpu_hz || !oms->ref_hz || !oms->io_hz || !oms->ddr_hz) { + error_report("octeon3 clock frequencies must be non-zero"); + exit(1); + } + + if (oms->cpu_hz % oms->ref_hz || oms->io_hz % oms->ref_hz) { + error_report("octeon3 CPU and IO clocks must be multiples of " + "ref-clock-hz"); + exit(1); + } +} + +static void octeon_spd_set_tckmin(OcteonState *s, uint8_t *spd) +{ + uint64_t tck_ps =3D (1000000000000ULL + s->ddr_hz - 1) / s->ddr_hz; + uint64_t mtb =3D tck_ps / OCTEON_SPD_DDR4_MTB_PS; + uint64_t fine =3D tck_ps % OCTEON_SPD_DDR4_MTB_PS; + + if (mtb =3D=3D 0 || mtb > UINT8_MAX || fine > INT8_MAX) { + error_report("octeon3 DDR clock cannot be represented in DDR4 SPD"= ); + exit(1); + } + + spd[OCTEON_SPD_DDR4_TCKMIN] =3D mtb; + spd[OCTEON_SPD_DDR4_FINE_TCKMIN] =3D fine / OCTEON_SPD_DDR4_FTB_PS; +} + +static void octeon_init_spd(OcteonState *s) +{ + OcteonSpdEepromState *eeprom =3D &s->spd[0]; + uint64_t ram_size =3D s->machine->ram_size; + int col_bits; + int row_bits; + int density_code; + uint16_t crc; + + eeprom->bus =3D OCTEON_SPD_BUS; + eeprom->addr =3D OCTEON_SPD_ADDR; + memcpy(eeprom->data, octeon_spd_eeprom_template, + sizeof(eeprom->data)); + + g_assert(octeon_spd_geometry(ram_size, &row_bits, &col_bits, + &density_code)); + + eeprom->data[4] =3D 0x80 | density_code; + eeprom->data[5] =3D ((row_bits - OCTEON_SPD_MIN_ROW_BITS) << 3) | + (col_bits - OCTEON_SPD_MIN_COL_BITS); + octeon_spd_set_tckmin(s, eeprom->data); + + crc =3D octeon_spd_crc16(eeprom->data, + (eeprom->data[0] & 0x80) ? 117 : 126); + eeprom->data[126] =3D crc & 0xff; + eeprom->data[127] =3D crc >> 8; +} + +static bool octeon_reg_lookup(GHashTable *regs, uint64_t reg, uint64_t *va= lue) +{ + uint64_t *stored =3D g_hash_table_lookup(regs, ®); + + if (!stored) { + return false; + } + + *value =3D *stored; + return true; +} + +static void octeon_reg_store(GHashTable *regs, uint64_t reg, uint64_t valu= e) +{ + uint64_t *key =3D g_new(uint64_t, 1); + uint64_t *stored =3D g_new(uint64_t, 1); + + *key =3D reg; + *stored =3D value; + g_hash_table_replace(regs, key, stored); +} + +static bool octeon_csr_lookup(OcteonState *s, uint64_t reg, uint64_t *valu= e) +{ + return octeon_reg_lookup(s->csr_values, reg, value); +} + +static void octeon_csr_store(OcteonState *s, uint64_t reg, uint64_t value) +{ + octeon_reg_store(s->csr_values, reg, value); +} + +static hwaddr octeon_mio_boot_loc_base(uint64_t cfg) +{ + uint64_t base =3D (cfg & OCTEON_MIO_BOOT_LOC_CFG_BASE) >> + OCTEON_MIO_BOOT_LOC_CFG_BASE_SHIFT; + + return base << 7; +} + +static void octeon_mio_boot_loc_update(OcteonState *s) +{ + MemoryRegion *sysmem =3D get_system_memory(); + bool enabled =3D s->mio_boot_loc_cfg[0] & OCTEON_MIO_BOOT_LOC_CFG_EN; + hwaddr base =3D octeon_mio_boot_loc_base(s->mio_boot_loc_cfg[0]); + + if (s->mio_boot_loc_mapped) { + if (enabled && base =3D=3D s->mio_boot_loc_base) { + return; + } + memory_region_del_subregion(sysmem, &s->mio_boot_loc); + s->mio_boot_loc_mapped =3D false; + } + + if (enabled) { + memory_region_add_subregion_overlap(sysmem, base, + &s->mio_boot_loc, 1); + s->mio_boot_loc_base =3D base; + s->mio_boot_loc_mapped =3D true; + } +} + +static hwaddr octeon_mio_boot_reg_cfg_base(uint64_t cfg) +{ + return (cfg & OCTEON_MIO_BOOT_REG_CFG_BASE) << 16; +} + +static void octeon_bootbus_led_update(OcteonState *s) +{ + MemoryRegion *sysmem =3D get_system_memory(); + hwaddr base =3D octeon_mio_boot_reg_cfg_base( + s->mio_boot_reg_cfg[OCTEON_BOOTBUS_LED_CS]); + + if (s->bootbus_led_mapped) { + if (base =3D=3D s->bootbus_led_base) { + return; + } + memory_region_del_subregion(sysmem, &s->bootbus_led); + s->bootbus_led_mapped =3D false; + } + + if (base) { + memory_region_add_subregion(sysmem, base, &s->bootbus_led); + s->bootbus_led_base =3D base; + s->bootbus_led_mapped =3D true; + } +} + +static uint64_t octeon_mio_boot_loc_dat_read(OcteonState *s) +{ + unsigned int offset =3D s->mio_boot_loc_adr & OCTEON_MIO_BOOT_LOC_ADR_= MASK; + uint64_t value =3D ldq_be_p(s->mio_boot_loc_data + offset); + + s->mio_boot_loc_adr =3D + (s->mio_boot_loc_adr + OCTEON_CSR_64BIT_SIZE) & + OCTEON_MIO_BOOT_LOC_ADR_MASK; + return value; +} + +static void octeon_mio_boot_loc_dat_write(OcteonState *s, hwaddr addr, + uint64_t value, unsigned size) +{ + unsigned int offset =3D s->mio_boot_loc_adr & OCTEON_MIO_BOOT_LOC_ADR_= MASK; + uint64_t old =3D ldq_be_p(s->mio_boot_loc_data + offset); + + value =3D octeon_write64(old, addr, value, size); + stq_be_p(s->mio_boot_loc_data + offset, value); + memory_region_set_dirty(&s->mio_boot_loc, offset, sizeof(value)); + s->mio_boot_loc_adr =3D + (s->mio_boot_loc_adr + OCTEON_CSR_64BIT_SIZE) & + OCTEON_MIO_BOOT_LOC_ADR_MASK; +} + +static bool octeon_mio_emm_valid_reg(hwaddr reg) +{ + if (reg >=3D OCTEON_MIO_EMM_MODEX(0) && + reg < OCTEON_MIO_EMM_MODEX(OCTEON_MIO_EMM_MODE_COUNT) && + (reg & (OCTEON_CSR_64BIT_SIZE - 1)) =3D=3D 0) { + return true; + } + + switch (reg) { + case OCTEON_MIO_EMM_DMA_INT: + case OCTEON_MIO_EMM_CFG: + case OCTEON_MIO_EMM_SWITCH: + case OCTEON_MIO_EMM_DMA: + case OCTEON_MIO_EMM_CMD: + case OCTEON_MIO_EMM_RSP_STS: + case OCTEON_MIO_EMM_INT: + case OCTEON_MIO_EMM_DMA_FIFO_CFG: + case OCTEON_MIO_EMM_DMA_FIFO_ADR: + case OCTEON_MIO_EMM_DMA_FIFO_CMD: + case OCTEON_MIO_EMM_DMA_CFG: + case OCTEON_MIO_EMM_DMA_ADR: + case OCTEON_MIO_EMM_INT_EN_OLD: + case OCTEON_MIO_EMM_RSP_LO: + case OCTEON_MIO_EMM_RSP_HI: + case OCTEON_MIO_EMM_WDOG: + case OCTEON_MIO_EMM_SAMPLE: + case OCTEON_MIO_EMM_STS_MASK: + case OCTEON_MIO_EMM_RCA: + case OCTEON_MIO_EMM_BUF_IDX: + case OCTEON_MIO_EMM_BUF_DAT: + case OCTEON_MIO_EMM_DEBUG: + return true; + default: + return false; + } +} + +static bool octeon_mio_emm_decode(hwaddr reg, hwaddr *ereg) +{ + if (octeon_mio_emm_valid_reg(reg)) { + *ereg =3D reg; + return true; + } + + reg -=3D OCTEON_MIO_EMM_NODE_OFFSET; + if (octeon_mio_emm_valid_reg(reg)) { + *ereg =3D reg; + return true; + } + + return false; +} + +static bool octeon_mio_emm_execute_write(hwaddr addr, unsigned size) +{ + return size =3D=3D OCTEON_CSR_64BIT_SIZE || + (size =3D=3D OCTEON_CSR_32BIT_SIZE && + (addr & OCTEON_CSR_LO32_OFFSET)); +} + +static void octeon_mio_emm_set_low_speed_mode(OcteonState *s, + unsigned int bus_id) +{ + uint64_t period =3D DIV_ROUND_UP(s->io_hz, OCTEON_MIO_EMM_LOW_SPEED_HZ= ); + uint64_t value; + + period =3D MIN(period, UINT16_MAX); + if (!octeon_csr_lookup(s, OCTEON_MIO_EMM_MODEX(bus_id), &value)) { + value =3D 0; + } + + value &=3D ~(OCTEON_MIO_EMM_MODE_CLK_LO_MASK | + OCTEON_MIO_EMM_MODE_CLK_HI_MASK); + value |=3D period | (period << OCTEON_MIO_EMM_MODE_CLK_HI_SHIFT); + octeon_csr_store(s, OCTEON_MIO_EMM_MODEX(bus_id), value); +} + +static void octeon_mio_emm_set_bustest(OcteonState *s) +{ + const uint8_t *buf =3D s->mio_emm_buf; + bool bus8 =3D buf[0] =3D=3D OCTEON_MMC_BUSTEST_8_REQ0 && + buf[1] =3D=3D OCTEON_MMC_BUSTEST_8_REQ1; + bool bus4 =3D buf[0] =3D=3D OCTEON_MMC_BUSTEST_4_REQ0 && + buf[4] =3D=3D OCTEON_MMC_BUSTEST_4_REQ4 && + buf[5] =3D=3D OCTEON_MMC_BUSTEST_4_REQ5 && + buf[6] =3D=3D OCTEON_MMC_BUSTEST_4_REQ6; + bool bus1 =3D buf[0] =3D=3D OCTEON_MMC_BUSTEST_1_REQ0 && + buf[1] =3D=3D OCTEON_MMC_BUSTEST_1_REQ1 && + buf[2] =3D=3D OCTEON_MMC_BUSTEST_1_REQ2 && + buf[3] =3D=3D OCTEON_MMC_BUSTEST_1_REQ3; + + memset(s->mio_emm_buf, 0, sizeof(s->mio_emm_buf)); + if (bus8) { + s->mio_emm_buf[0] =3D OCTEON_MMC_BUSTEST_8_RESP0; + s->mio_emm_buf[1] =3D OCTEON_MMC_BUSTEST_8_RESP1; + } else if (bus4) { + s->mio_emm_buf[0] =3D OCTEON_MMC_BUSTEST_4_RESP0; + } else if (bus1) { + s->mio_emm_buf[0] =3D OCTEON_MMC_BUSTEST_1_RESP0; + } +} + +static void octeon_mio_emm_complete_dma(OcteonState *s, uint64_t dma) +{ + dma &=3D ~(OCTEON_MIO_EMM_DMA_VAL | OCTEON_MIO_EMM_DMA_BLOCK_CNT_MASK); + octeon_csr_store(s, OCTEON_MIO_EMM_DMA, dma); + s->mio_emm_rsp_sts &=3D ~(OCTEON_MIO_EMM_RSP_STS_DMA_VAL | + OCTEON_MIO_EMM_RSP_STS_DMA_PEND); + s->mio_emm_dma_int |=3D OCTEON_MIO_EMM_DMA_INT_DONE; + s->mio_emm_int |=3D OCTEON_MIO_EMM_INT_DMA_DONE; +} + +static void octeon_mio_emm_command(OcteonState *s, uint64_t cmd) +{ + unsigned int cmd_idx =3D (cmd >> OCTEON_MIO_EMM_CMD_IDX_SHIFT) & + OCTEON_MIO_EMM_CMD_IDX_MASK; + unsigned int bus_id =3D (cmd >> OCTEON_MIO_EMM_CMD_BUS_ID_SHIFT) & + OCTEON_MIO_EMM_BUS_ID_MASK; + + s->mio_emm_rsp_lo =3D 0; + s->mio_emm_rsp_hi =3D 0; + s->mio_emm_rsp_sts =3D + OCTEON_MIO_EMM_RSP_STS_CMD_DONE | + ((uint64_t)cmd_idx << OCTEON_MIO_EMM_RSP_STS_CMD_IDX_SHIFT) | + ((uint64_t)bus_id << OCTEON_MIO_EMM_RSP_STS_BUS_ID_SHIFT); + + switch (cmd_idx) { + case OCTEON_MMC_CMD_GO_IDLE_STATE: + case OCTEON_MMC_CMD_BUSTEST_W: + break; + case OCTEON_MMC_CMD_BUSTEST_R: + octeon_mio_emm_set_bustest(s); + break; + default: + s->mio_emm_rsp_sts |=3D OCTEON_MIO_EMM_RSP_STS_RSP_TIMEOUT; + break; + } + + s->mio_emm_int |=3D OCTEON_MIO_EMM_INT_CMD_DONE; + if (s->mio_emm_rsp_sts & OCTEON_MIO_EMM_RSP_STS_RSP_TIMEOUT) { + s->mio_emm_int |=3D OCTEON_MIO_EMM_INT_CMD_ERR; + } +} + +static uint64_t octeon_mio_emm_read(OcteonState *s, hwaddr reg, + hwaddr addr, unsigned size) +{ + uint64_t value; + + switch (reg) { + case OCTEON_MIO_EMM_RSP_STS: + value =3D s->mio_emm_rsp_sts; + break; + case OCTEON_MIO_EMM_RSP_LO: + value =3D s->mio_emm_rsp_lo; + break; + case OCTEON_MIO_EMM_RSP_HI: + value =3D s->mio_emm_rsp_hi; + break; + case OCTEON_MIO_EMM_BUF_DAT: + value =3D ldq_be_p(s->mio_emm_buf + s->mio_emm_buf_idx); + if (s->mio_emm_buf_inc) { + s->mio_emm_buf_idx =3D + (s->mio_emm_buf_idx + OCTEON_MIO_EMM_BUF_WORD_SIZE) % + OCTEON_MIO_EMM_BUF_SIZE; + } + break; + case OCTEON_MIO_EMM_INT: + value =3D s->mio_emm_int; + break; + case OCTEON_MIO_EMM_DMA_INT: + value =3D s->mio_emm_dma_int; + break; + case OCTEON_MIO_EMM_CMD: + case OCTEON_MIO_EMM_SWITCH: + case OCTEON_MIO_EMM_DMA: + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D 0; + } + break; + case OCTEON_MIO_EMM_MODEX(0): + case OCTEON_MIO_EMM_MODEX(1): + case OCTEON_MIO_EMM_MODEX(2): + case OCTEON_MIO_EMM_MODEX(3): + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D 0; + } + break; + default: + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D 0; + } + break; + } + + return octeon_read64(value, addr, size); +} + +static void octeon_mio_emm_write(OcteonState *s, hwaddr reg, + hwaddr addr, uint64_t value, unsigned siz= e) +{ + uint64_t old; + uint64_t clear; + + switch (reg) { + case OCTEON_MIO_EMM_CMD: + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + if (!(value & OCTEON_MIO_EMM_CMD_VAL)) { + return; + } + if (!octeon_mio_emm_execute_write(addr, size)) { + return; + } + + octeon_csr_store(s, reg, value & ~OCTEON_MIO_EMM_CMD_VAL); + octeon_mio_emm_command(s, value); + return; + case OCTEON_MIO_EMM_SWITCH: + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + if (!(value & OCTEON_MIO_EMM_SWITCH_EXE)) { + return; + } + if (!octeon_mio_emm_execute_write(addr, size)) { + return; + } + octeon_csr_store(s, reg, value & ~OCTEON_MIO_EMM_SWITCH_EXE); + octeon_csr_store(s, OCTEON_MIO_EMM_MODEX( + (value >> OCTEON_MIO_EMM_CMD_BUS_ID_SHIFT) & + OCTEON_MIO_EMM_BUS_ID_MASK), + value & OCTEON_MIO_EMM_SWITCH_MODE_MASK); + s->mio_emm_int |=3D OCTEON_MIO_EMM_INT_SWITCH_DONE; + return; + case OCTEON_MIO_EMM_DMA: + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + if (!(value & OCTEON_MIO_EMM_DMA_VAL)) { + return; + } + if (!octeon_mio_emm_execute_write(addr, size)) { + return; + } + octeon_mio_emm_complete_dma(s, value); + return; + case OCTEON_MIO_EMM_INT: + clear =3D octeon_write64(0, addr, value, size); + s->mio_emm_int &=3D ~clear; + return; + case OCTEON_MIO_EMM_DMA_INT: + clear =3D octeon_write64(0, addr, value, size); + s->mio_emm_dma_int &=3D ~clear; + return; + case OCTEON_MIO_EMM_MODEX(0): + case OCTEON_MIO_EMM_MODEX(1): + case OCTEON_MIO_EMM_MODEX(2): + case OCTEON_MIO_EMM_MODEX(3): + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + return; + case OCTEON_MIO_EMM_CFG: + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + if (octeon_mio_emm_execute_write(addr, size)) { + uint64_t enabled =3D (value & ~old) & OCTEON_MIO_EMM_CFG_BUS_E= N_MASK; + unsigned int i; + + for (i =3D 0; i < OCTEON_MIO_EMM_MODE_COUNT; i++) { + if (enabled & (1U << i)) { + octeon_mio_emm_set_low_speed_mode(s, i); + } + } + } + return; + case OCTEON_MIO_EMM_BUF_IDX: + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + s->mio_emm_buf_idx =3D (value & OCTEON_MIO_EMM_BUF_IDX_OFF_MASK) * + OCTEON_MIO_EMM_BUF_WORD_SIZE; + s->mio_emm_buf_inc =3D value & OCTEON_MIO_EMM_BUF_IDX_INC; + return; + case OCTEON_MIO_EMM_BUF_DAT: + old =3D ldq_be_p(s->mio_emm_buf + s->mio_emm_buf_idx); + value =3D octeon_write64(old, addr, value, size); + stq_be_p(s->mio_emm_buf + s->mio_emm_buf_idx, value); + if (s->mio_emm_buf_inc) { + s->mio_emm_buf_idx =3D + (s->mio_emm_buf_idx + OCTEON_MIO_EMM_BUF_WORD_SIZE) % + OCTEON_MIO_EMM_BUF_SIZE; + } + return; + default: + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); + return; + } +} + +static uint32_t octeon_pcie_root_cfg_default(uint32_t reg) +{ + switch (reg) { + case OCTEON_PCIE_CFG006: + return 0x00010101; + case OCTEON_PCIE_CFG031: + return 0x00000011; + case OCTEON_PCIE_CFG032: + return 0x20110000; + default: + return 0; + } +} + +static uint32_t octeon_pcie_root_cfg_read(OcteonState *s, uint32_t reg) +{ + uint64_t value; + + reg &=3D ~3U; + switch (reg) { + case OCTEON_PCIE_CFG006: + case OCTEON_PCIE_CFG031: + case OCTEON_PCIE_CFG032: + return octeon_pcie_root_cfg_default(reg); + default: + break; + } + + if (octeon_csr_lookup(s, OCTEON_PCIE_CFG_STORE_BASE | reg, &value)) { + return value; + } + + return octeon_pcie_root_cfg_default(reg); +} + +static void octeon_pcie_root_cfg_write(OcteonState *s, uint32_t reg, + uint32_t value) +{ + reg &=3D ~3U; + if (reg =3D=3D OCTEON_PCIE_CFG068) { + value =3D 0; + } + + octeon_csr_store(s, OCTEON_PCIE_CFG_STORE_BASE | reg, value); +} + +static void octeon_init_lmcs(OcteonState *s) +{ + unsigned int i; + + for (i =3D 0; i < OCTEON_LMC_COUNT; i++) { + s->lmc[i].regs =3D g_hash_table_new_full(octeon_uint64_hash, + octeon_uint64_equal, + g_free, g_free); + } +} + +static bool octeon_lmc_decode(hwaddr reg, OcteonLmcState **lmc, hwaddr *lr= eg, + OcteonState *s) +{ + hwaddr offset; + unsigned int index; + + if (reg < OCTEON_LMC_BASE) { + return false; + } + + offset =3D reg - OCTEON_LMC_BASE; + index =3D offset / OCTEON_LMC_STRIDE; + if (index >=3D OCTEON_LMC_COUNT) { + return false; + } + + *lmc =3D &s->lmc[index]; + *lreg =3D offset % OCTEON_LMC_STRIDE; + return true; +} + +static uint64_t octeon_lmc_get(OcteonLmcState *lmc, hwaddr reg) +{ + uint64_t value; + + if (!octeon_reg_lookup(lmc->regs, reg, &value)) { + return 0; + } + return value; +} + +static bool octeon_lmc_rank_reg(hwaddr reg, hwaddr base) +{ + return reg >=3D base && + reg < base + OCTEON_LMC_RANK_COUNT * 8 && + ((reg - base) & 7) =3D=3D 0; +} + +static uint64_t octeon_lmc_read(OcteonState *s, OcteonLmcState *lmc, + hwaddr reg, hwaddr addr, unsigned size) +{ + uint64_t value; + + switch (reg) { + case OCTEON_LMC_SEQ_CTL: + value =3D octeon_lmc_get(lmc, reg) | OCTEON_LMC_SEQ_COMPLETE; + break; + case OCTEON_LMC_MPR_DATA0: + value =3D ~0ULL; + break; + case OCTEON_LMC_MPR_DATA1: + value =3D 0xff; + break; + case OCTEON_LMC_DCLK_CNT: + value =3D octeon_clock_count(s->ddr_hz); + break; + case OCTEON_LMC_PHY_CTL: + value =3D octeon_lmc_get(lmc, reg) | OCTEON_LMC_PHY_DSK_COMPLETE; + break; + case OCTEON_LMC_RLEVEL_DBG: + value =3D OCTEON_LMC_RLEVEL_MASK; + break; + case OCTEON_LMC_WLEVEL_DBG: + value =3D OCTEON_LMC_WLEVEL_MASK; + break; + default: + if (octeon_lmc_rank_reg(reg, OCTEON_LMC_RLEVEL_RANK)) { + value =3D octeon_lmc_get(lmc, reg) | OCTEON_LMC_RLEVEL_STATUS; + } else if (octeon_lmc_rank_reg(reg, OCTEON_LMC_WLEVEL_RANK)) { + value =3D octeon_lmc_get(lmc, reg) | OCTEON_LMC_WLEVEL_STATUS; + } else { + value =3D octeon_lmc_get(lmc, reg); + } + break; + } + + return octeon_read64(value, addr, size); +} + +static void octeon_lmc_write(OcteonLmcState *lmc, hwaddr reg, + hwaddr addr, uint64_t value, unsigned size) +{ + uint64_t old =3D octeon_lmc_get(lmc, reg); + + value =3D octeon_write64(old, addr, value, size); + octeon_reg_store(lmc->regs, reg, value); +} + +static bool octeon_ciu3_source_from_intsn(uint32_t intsn, + unsigned int *source) +{ + if (intsn =3D=3D OCTEON_CIU3_PEM_INTSN_INTA(0)) { + *source =3D OCTEON_CIU3_SRC_PCI_INTA; + return true; + } + if (intsn =3D=3D OCTEON_CIU3_UART0_INTSN) { + *source =3D OCTEON_CIU3_SRC_UART0; + return true; + } + if (intsn =3D=3D OCTEON_CIU3_USB0_INTSN) { + *source =3D OCTEON_CIU3_SRC_USB0; + return true; + } + if (intsn =3D=3D OCTEON_CIU3_USB1_INTSN) { + *source =3D OCTEON_CIU3_SRC_USB1; + return true; + } + + if (intsn >=3D OCTEON_CIU3_MBOX_INTSN(0) && + intsn < OCTEON_CIU3_MBOX_INTSN(OCTEON_MAX_CPUS)) { + *source =3D OCTEON_CIU3_SRC_MBOX0 + + intsn - OCTEON_CIU3_MBOX_INTSN(0); + return true; + } + + return false; +} + +static uint32_t octeon_ciu3_source_intsn(unsigned int source) +{ + switch (source) { + case OCTEON_CIU3_SRC_PCI_INTA: + return OCTEON_CIU3_PEM_INTSN_INTA(0); + case OCTEON_CIU3_SRC_UART0: + return OCTEON_CIU3_UART0_INTSN; + case OCTEON_CIU3_SRC_USB0: + return OCTEON_CIU3_USB0_INTSN; + case OCTEON_CIU3_SRC_USB1: + return OCTEON_CIU3_USB1_INTSN; + } + + if (source >=3D OCTEON_CIU3_SRC_MBOX0 && + source < OCTEON_CIU3_SRC_COUNT) { + return OCTEON_CIU3_MBOX_INTSN(source - OCTEON_CIU3_SRC_MBOX0); + } + + g_assert_not_reached(); +} + +static bool octeon_ciu3_source_is_level(unsigned int source) +{ + switch (source) { + case OCTEON_CIU3_SRC_PCI_INTA: + case OCTEON_CIU3_SRC_UART0: + case OCTEON_CIU3_SRC_USB0: + case OCTEON_CIU3_SRC_USB1: + return true; + } + + return false; +} + +static bool octeon_ciu3_set_level(OcteonState *s, unsigned int source, + int level) +{ + uint32_t old; + uint32_t new; + uint32_t cmp =3D qatomic_read(&s->ciu3_src_state[source]); + + do { + old =3D cmp; + if (level) { + new =3D old | OCTEON_CIU3_SRC_LEVEL | OCTEON_CIU3_SRC_RAW; + } else { + new =3D old & ~(OCTEON_CIU3_SRC_LEVEL | OCTEON_CIU3_SRC_RAW); + } + if (new =3D=3D old) { + return false; + } + cmp =3D qatomic_cmpxchg(&s->ciu3_src_state[source], old, new); + } while (old !=3D cmp); + + return true; +} + +static void octeon_ciu3_clear_raw(OcteonState *s, unsigned int source) +{ + uint32_t old; + uint32_t new; + uint32_t cmp =3D qatomic_read(&s->ciu3_src_state[source]); + + do { + old =3D cmp; + if (octeon_ciu3_source_is_level(source) && + (old & OCTEON_CIU3_SRC_LEVEL)) { + new =3D old | OCTEON_CIU3_SRC_RAW; + } else { + new =3D old & ~OCTEON_CIU3_SRC_RAW; + } + if (new =3D=3D old) { + return; + } + cmp =3D qatomic_cmpxchg(&s->ciu3_src_state[source], old, new); + } while (old !=3D cmp); +} + +static bool octeon_ciu3_set_mbox(OcteonState *s, unsigned int cpu) +{ + return octeon_ciu3_set_level(s, OCTEON_CIU3_SRC_MBOX0 + cpu, + qatomic_read(&s->ciu_mbox[cpu]) !=3D 0); +} + +static bool octeon_ciu3_isc_decode(hwaddr reg, uint32_t base, + uint32_t *intsn) +{ + if (reg < base || reg >=3D base + OCTEON_CIU3_ISC_SIZE) { + return false; + } + + *intsn =3D (reg - base) >> 3; + return true; +} + +static uint64_t octeon_ciu3_isc_read(OcteonState *s, uint32_t intsn) +{ + unsigned int src; + uint64_t value; + + if (!octeon_ciu3_source_from_intsn(intsn, &src)) { + return 0; + } + + value =3D qatomic_read(&s->ciu3_src_ctl[src]) | OCTEON_CIU3_ISC_CTL_IM= P; + if (qatomic_read(&s->ciu3_src_state[src]) & OCTEON_CIU3_SRC_RAW) { + value |=3D OCTEON_CIU3_ISC_CTL_RAW; + } + return value; +} + +static bool octeon_ciu3_source_pending(OcteonState *s, unsigned int cpu_in= dex, + unsigned int irq, unsigned int src) +{ + uint64_t ctl =3D qatomic_read(&s->ciu3_src_ctl[src]); + unsigned int idt; + unsigned int output; + + if (!(qatomic_read(&s->ciu3_src_state[src]) & OCTEON_CIU3_SRC_RAW) || + !(ctl & OCTEON_CIU3_ISC_CTL_EN)) { + return false; + } + + idt =3D (ctl & OCTEON_CIU3_ISC_CTL_IDT) >> + OCTEON_CIU3_ISC_CTL_IDT_SHIFT; + if (idt >=3D OCTEON_CIU3_IDT_COUNT) { + return false; + } + + output =3D qatomic_read(&s->ciu3_idt_ctl[idt]) & + OCTEON_CIU3_IDT_CTL_IP_NUM; + if (output >=3D OCTEON_CIU3_CP0_IRQ_COUNT || + irq !=3D OCTEON_CIU3_CP0_IRQ_BASE + output) { + return false; + } + + return qatomic_read(&s->ciu3_idt_pp[idt]) & (1ULL << cpu_index); +} + +static bool octeon_ciu3_pending_intsn(OcteonState *s, unsigned int cpu_ind= ex, + unsigned int irq, uint32_t *intsn) +{ + unsigned int src; + + for (src =3D 0; src < OCTEON_CIU3_SRC_COUNT; src++) { + if (octeon_ciu3_source_pending(s, cpu_index, irq, src)) { + *intsn =3D octeon_ciu3_source_intsn(src); + return true; + } + } + + return false; +} + +static bool octeon_ciu3_pending_intsn_from(OcteonState *s, + unsigned int cpu_index, + unsigned int irq, + unsigned int first_src, + uint32_t *intsn, + unsigned int *source) +{ + unsigned int i; + unsigned int src; + + for (i =3D 0; i < OCTEON_CIU3_SRC_COUNT; i++) { + src =3D (first_src + i) % OCTEON_CIU3_SRC_COUNT; + if (octeon_ciu3_source_pending(s, cpu_index, irq, src)) { + *intsn =3D octeon_ciu3_source_intsn(src); + *source =3D src; + return true; + } + } + + return false; +} + +static bool octeon_ciu3_pending_dest_intsn(OcteonState *s, + unsigned int cpu_index, + uint32_t *intsn) +{ + unsigned int irq; + unsigned int irq_index; + unsigned int src; + + for (irq =3D OCTEON_CIU3_CP0_IRQ_BASE; + irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT; + irq++) { + irq_index =3D irq - OCTEON_CIU3_CP0_IRQ_BASE; + if (octeon_ciu3_pending_intsn_from(s, cpu_index, irq, + qatomic_read(&s->ciu3_src_cursor[cpu_index][irq_index]), + intsn, &src)) { + qatomic_set(&s->ciu3_src_cursor[cpu_index][irq_index], + (src + 1) % OCTEON_CIU3_SRC_COUNT); + return true; + } + } + + return false; +} + +static void octeon_ciu3_set_cpu_irq(OcteonState *s, unsigned int cpu_index, + unsigned int irq, bool level) +{ + CPUMIPSState *env =3D &s->cpu[cpu_index].cpu->env; + unsigned int irq_index =3D irq - OCTEON_CIU3_CP0_IRQ_BASE; + uint8_t new_level =3D level; + uint8_t old_level; + + old_level =3D qatomic_xchg(&s->ciu3_irq_line[cpu_index][irq_index], + new_level); + if (old_level =3D=3D new_level) { + return; + } + + qemu_set_irq(env->irq[irq], level); +} + +static void octeon_intc_update_cpu(OcteonState *s, unsigned int cpu_index) +{ + uint32_t intsn; + unsigned int irq; + bool level; + + if (cpu_index >=3D s->cpu_count || !s->cpu[cpu_index].cpu) { + return; + } + + if (qatomic_read(&s->ciu3_pp_rst) & (1ULL << cpu_index)) { + for (irq =3D OCTEON_CIU3_CP0_IRQ_BASE; + irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT; + irq++) { + octeon_ciu3_set_cpu_irq(s, cpu_index, irq, false); + } + return; + } + + for (irq =3D OCTEON_CIU3_CP0_IRQ_BASE; + irq < OCTEON_CIU3_CP0_IRQ_BASE + OCTEON_CIU3_CP0_IRQ_COUNT; + irq++) { + level =3D false; + if (irq =3D=3D 3) { + level =3D qatomic_read(&s->ciu_mbox[cpu_index]) && + (qatomic_read(&s->ciu_ipi_en[cpu_index]) & + OCTEON_CIU_SUM_IPI); + } + + if (octeon_ciu3_pending_intsn(s, cpu_index, irq, &intsn)) { + level =3D true; + } + + octeon_ciu3_set_cpu_irq(s, cpu_index, irq, level); + } +} + +static void octeon_intc_request_cpu_update(OcteonState *s, + unsigned int cpu_index) +{ + if (cpu_index >=3D s->cpu_count || !s->cpu[cpu_index].cpu) { + return; + } + + octeon_intc_update_cpu(s, cpu_index); +} + +static void octeon_intc_update_cpu_mask(OcteonState *s, uint64_t cpu_mask) +{ + unsigned int cpu; + + for (cpu =3D 0; cpu < s->cpu_count; cpu++) { + if (cpu_mask & (1ULL << cpu)) { + octeon_intc_request_cpu_update(s, cpu); + } + } +} + +static void octeon_intc_update_all_cpus(OcteonState *s) +{ + unsigned int cpu; + + for (cpu =3D 0; cpu < s->cpu_count; cpu++) { + octeon_intc_request_cpu_update(s, cpu); + } +} + +static uint64_t octeon_present_cpu_mask(OcteonState *s) +{ + return (1ULL << s->cpu_count) - 1; +} + +static uint64_t octeon_secondary_cpu_mask(OcteonState *s) +{ + return octeon_present_cpu_mask(s) & ~1ULL; +} + +static uint64_t octeon_ciu3_ctl_cpu_mask(OcteonState *s, uint64_t ctl) +{ + unsigned int idt =3D (ctl & OCTEON_CIU3_ISC_CTL_IDT) >> + OCTEON_CIU3_ISC_CTL_IDT_SHIFT; + + if (idt >=3D OCTEON_CIU3_IDT_COUNT) { + return 0; + } + + return qatomic_read(&s->ciu3_idt_pp[idt]); +} + +static uint64_t octeon_ciu3_source_cpu_mask(OcteonState *s, unsigned int s= rc) +{ + return octeon_ciu3_ctl_cpu_mask(s, qatomic_read(&s->ciu3_src_ctl[src])= ); +} + +static uint64_t octeon_ciu_gpio_rx_value(OcteonState *s) +{ + uint64_t value =3D 0; + unsigned int i; + + for (i =3D 0; i < OCTEON_CIU_GPIO_COUNT; i++) { + uint64_t mask =3D 1ULL << i; + + if (qatomic_read(&s->ciu_gpio_bit_cfg[i]) & + OCTEON_CIU_GPIO_BIT_CFG_TX_OE) { + if (qatomic_read(&s->ciu_gpio_tx) & mask) { + value |=3D mask; + } + } else if (qatomic_read(&s->ciu_gpio_rx) & mask) { + value |=3D mask; + } + } + + return value; +} + +static uint64_t octeon_ciu_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonState *s =3D opaque; + uint64_t value =3D 0; + hwaddr reg =3D addr & ~7ULL; + unsigned int cpu; + + if (reg =3D=3D 0) { + if (qatomic_read(&s->uart_pending)) { + value |=3D OCTEON_CIU_SUM_UART; + } + if (qatomic_read(&s->pci_pending)) { + value |=3D OCTEON_CIU_SUM_PCI; + } + octeon_intc_request_cpu_update(s, 0); + return octeon_read64(value, addr, size); + } + + if (reg =3D=3D OCTEON_CIU_IPI_EN_BASE) { + return octeon_read64(OCTEON_CIU_ENABLE0, addr, size); + } + + if (reg =3D=3D OCTEON_CIU_FUSE) { + value =3D octeon_present_cpu_mask(s); + return octeon_read64(value, addr, size); + } + + if (reg =3D=3D OCTEON_CIU_GPIO_RX_DAT) { + return octeon_read64(octeon_ciu_gpio_rx_value(s), addr, size); + } + + if (reg =3D=3D OCTEON_CIU_GPIO_TX_SET || + reg =3D=3D OCTEON_CIU_GPIO_TX_CLR) { + return octeon_read64(qatomic_read(&s->ciu_gpio_tx), addr, size); + } + + if (reg >=3D OCTEON_CIU_GPIO_BIT_CFGX(0) && + reg < OCTEON_CIU_GPIO_BIT_CFGX(OCTEON_CIU_GPIO_COUNT)) { + unsigned int index =3D (reg - OCTEON_CIU_GPIO_BIT_CFGX(0)) / + OCTEON_CIU_GPIO_BIT_CFG_STRIDE; + + return octeon_read64(qatomic_read(&s->ciu_gpio_bit_cfg[index]), + addr, size); + } + + if (reg >=3D OCTEON_CIU_IPI_SUM_BASE && + reg < OCTEON_CIU_IPI_SUM_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_IPI_SUM_STRIDE && + ((reg - OCTEON_CIU_IPI_SUM_BASE) & + (OCTEON_CIU_IPI_SUM_STRIDE - 1)) =3D=3D 0) { + cpu =3D (reg - OCTEON_CIU_IPI_SUM_BASE) / OCTEON_CIU_IPI_SUM_STRID= E; + if (cpu < s->cpu_count && qatomic_read(&s->ciu_mbox[cpu])) { + return octeon_read64(OCTEON_CIU_SUM_IPI, addr, size); + } + } + + if (reg >=3D OCTEON_CIU_IPI_EN_BASE && + reg < OCTEON_CIU_IPI_EN_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_IPI_EN_STRIDE && + ((reg - OCTEON_CIU_IPI_EN_BASE) & + (OCTEON_CIU_IPI_EN_STRIDE - 1)) =3D=3D 0) { + cpu =3D (reg - OCTEON_CIU_IPI_EN_BASE) / OCTEON_CIU_IPI_EN_STRIDE; + if (cpu < s->cpu_count) { + return octeon_read64(qatomic_read(&s->ciu_ipi_en[cpu]), + addr, size); + } + } + + if (reg >=3D OCTEON_CIU_MBOX_CLR_BASE && + reg < OCTEON_CIU_MBOX_CLR_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_MBOX_SET_STRIDE && + ((reg - OCTEON_CIU_MBOX_CLR_BASE) & + (OCTEON_CIU_MBOX_SET_STRIDE - 1)) =3D=3D 0) { + cpu =3D (reg - OCTEON_CIU_MBOX_CLR_BASE) / OCTEON_CIU_MBOX_SET_STR= IDE; + if (cpu < s->cpu_count) { + return octeon_read64(qatomic_read(&s->ciu_mbox[cpu]), addr, si= ze); + } + } + + return 0; +} + +static void octeon_ciu_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + hwaddr reg =3D addr & ~7ULL; + uint64_t old; + unsigned int cpu; + + if (reg >=3D OCTEON_CIU_IPI_EN_BASE && + reg < OCTEON_CIU_IPI_EN_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_IPI_EN_STRIDE && + ((reg - OCTEON_CIU_IPI_EN_BASE) & + (OCTEON_CIU_IPI_EN_STRIDE - 1)) =3D=3D 0) { + cpu =3D (reg - OCTEON_CIU_IPI_EN_BASE) / OCTEON_CIU_IPI_EN_STRIDE; + if (cpu < s->cpu_count) { + value =3D octeon_atomic_write64(&s->ciu_ipi_en[cpu], addr, val= ue, + size, UINT64_MAX, 0, &old); + if (old !=3D value) { + octeon_intc_request_cpu_update(s, cpu); + } + } + return; + } + + if (reg >=3D OCTEON_CIU_MBOX_SET_BASE && + reg < OCTEON_CIU_MBOX_SET_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_MBOX_SET_STRIDE && + ((reg - OCTEON_CIU_MBOX_SET_BASE) & + (OCTEON_CIU_MBOX_SET_STRIDE - 1)) =3D=3D 0) { + cpu =3D (reg - OCTEON_CIU_MBOX_SET_BASE) / OCTEON_CIU_MBOX_SET_STR= IDE; + if (cpu < s->cpu_count) { + old =3D qatomic_fetch_or(&s->ciu_mbox[cpu], value & UINT32_MAX= ); + if ((old | (value & UINT32_MAX)) !=3D old && + octeon_ciu3_set_mbox(s, cpu)) { + octeon_intc_request_cpu_update(s, cpu); + } + } + return; + } + + if (reg >=3D OCTEON_CIU_MBOX_CLR_BASE && + reg < OCTEON_CIU_MBOX_CLR_BASE + + OCTEON_MAX_CPUS * OCTEON_CIU_MBOX_SET_STRIDE && + ((reg - OCTEON_CIU_MBOX_CLR_BASE) & + (OCTEON_CIU_MBOX_SET_STRIDE - 1)) =3D=3D 0) { + cpu =3D (reg - OCTEON_CIU_MBOX_CLR_BASE) / OCTEON_CIU_MBOX_SET_STR= IDE; + if (cpu < s->cpu_count) { + old =3D qatomic_fetch_and(&s->ciu_mbox[cpu], + ~(value & UINT32_MAX)); + if ((old & (value & UINT32_MAX)) && + octeon_ciu3_set_mbox(s, cpu)) { + octeon_intc_request_cpu_update(s, cpu); + } + } + return; + } + + if (reg =3D=3D OCTEON_CIU_GPIO_TX_SET) { + value =3D octeon_write64(0, addr, value, size) & + OCTEON_CIU_GPIO_INPUTS; + qatomic_or(&s->ciu_gpio_tx, value); + return; + } + + if (reg =3D=3D OCTEON_CIU_GPIO_TX_CLR) { + value =3D octeon_write64(0, addr, value, size) & + OCTEON_CIU_GPIO_INPUTS; + qatomic_and(&s->ciu_gpio_tx, ~value); + return; + } + + if (reg >=3D OCTEON_CIU_GPIO_BIT_CFGX(0) && + reg < OCTEON_CIU_GPIO_BIT_CFGX(OCTEON_CIU_GPIO_COUNT)) { + unsigned int index =3D (reg - OCTEON_CIU_GPIO_BIT_CFGX(0)) / + OCTEON_CIU_GPIO_BIT_CFG_STRIDE; + + octeon_atomic_write64(&s->ciu_gpio_bit_cfg[index], addr, value, + size, UINT64_MAX, 0, NULL); + return; + } +} + +static void octeon_cpu_park_now(MIPSCPU *cpu) +{ + CPUMIPSState *env =3D &cpu->env; + CPUState *cs =3D CPU(cpu); + + env->active_tc.CP0_TCHalt =3D 1; + env->tcs[0].CP0_TCHalt =3D 1; + cs->halted =3D 1; + cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); +} + +static void octeon_cpu_park_work(CPUState *cs, run_on_cpu_data data) +{ + octeon_cpu_park_now(MIPS_CPU(cs)); +} + +static void octeon_cpu_start_work(CPUState *cs, run_on_cpu_data data) +{ + MIPSCPU *cpu =3D MIPS_CPU(cs); + CPUMIPSState *env =3D &cpu->env; + + cpu->mvp->CP0_MVPControl |=3D (1 << CP0MVPCo_EVP); + env->CP0_VPEConf0 |=3D (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); + env->active_tc.CP0_TCHalt =3D 0; + env->tcs[0].CP0_TCHalt =3D 0; + env->active_tc.CP0_TCStatus =3D (1 << CP0TCSt_A); + env->tcs[0].CP0_TCStatus =3D (1 << CP0TCSt_A); + env->active_tc.PC =3D cpu_mips_phys_to_kseg1(NULL, OCTEON_FLASH_RESET_= BASE); + cs->halted =3D 0; + cpu_interrupt(cs, CPU_INTERRUPT_WAKE | CPU_INTERRUPT_EXITTB); +} + +static void octeon_start_cpu(OcteonState *s, unsigned int cpu_index) +{ + MIPSCPU *cpu; + + if (cpu_index >=3D s->cpu_count || !s->cpu[cpu_index].cpu) { + return; + } + + cpu =3D s->cpu[cpu_index].cpu; + async_run_on_cpu(CPU(cpu), octeon_cpu_start_work, RUN_ON_CPU_NULL); +} + +static void octeon_ciu3_start_cpu(OcteonState *s, unsigned int cpu_index) +{ + if (qatomic_read(&s->ciu3_pp_rst) & (1ULL << cpu_index)) { + return; + } + + octeon_start_cpu(s, cpu_index); +} + +static void octeon_ciu3_nmi(OcteonState *s, uint64_t mask) +{ + unsigned int cpu; + + for (cpu =3D 1; cpu < s->cpu_count; cpu++) { + if (mask & (1ULL << cpu)) { + octeon_ciu3_start_cpu(s, cpu); + } + } +} + +static void octeon_rst_pp_power_write(OcteonState *s, hwaddr addr, + uint64_t value, unsigned size) +{ + uint64_t old =3D s->rst_pp_power; + uint64_t changed; + unsigned int cpu; + + value =3D octeon_write64(old, addr, value, size) & + octeon_present_cpu_mask(s); + s->rst_pp_power =3D value; + changed =3D old ^ value; + + for (cpu =3D 1; cpu < s->cpu_count; cpu++) { + uint64_t mask =3D 1ULL << cpu; + + if (!(changed & mask)) { + continue; + } + if (value & mask) { + async_run_on_cpu(CPU(s->cpu[cpu].cpu), octeon_cpu_park_work, + RUN_ON_CPU_NULL); + } else { + octeon_start_cpu(s, cpu); + } + } +} + +static const MemoryRegionOps octeon_ciu_ops =3D { + .read =3D octeon_ciu_read, + .write =3D octeon_ciu_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_ciu3_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonState *s =3D opaque; + hwaddr reg =3D addr & ~7ULL; + uint64_t value; + uint32_t intsn; + unsigned int cpu; + unsigned int idt; + + if (reg =3D=3D OCTEON_CIU3_PP_RST) { + return octeon_read64(qatomic_read(&s->ciu3_pp_rst), addr, size); + } + + if (reg =3D=3D OCTEON_CIU3_PP_RST_PENDING) { + return 0; + } + + if (reg =3D=3D OCTEON_CIU3_NMI) { + return 0; + } + + if (reg =3D=3D OCTEON_CIU3_FUSE) { + value =3D octeon_present_cpu_mask(s); + return octeon_read64(value, addr, size); + } + + if (reg >=3D OCTEON_CIU3_IDT_CTL(0) && + reg < OCTEON_CIU3_IDT_CTL(OCTEON_CIU3_IDT_COUNT)) { + idt =3D (reg - OCTEON_CIU3_IDT_CTL(0)) >> 3; + return octeon_read64(qatomic_read(&s->ciu3_idt_ctl[idt]), addr, si= ze); + } + + if (reg >=3D OCTEON_CIU3_IDT_PP(0) && + reg < OCTEON_CIU3_IDT_PP(OCTEON_CIU3_IDT_COUNT)) { + idt =3D (reg - OCTEON_CIU3_IDT_PP(0)) >> 5; + return octeon_read64(qatomic_read(&s->ciu3_idt_pp[idt]), addr, siz= e); + } + + if (reg >=3D OCTEON_CIU3_IDT_IO(0) && + reg < OCTEON_CIU3_IDT_IO(OCTEON_CIU3_IDT_COUNT)) { + idt =3D (reg - OCTEON_CIU3_IDT_IO(0)) >> 3; + return octeon_read64(qatomic_read(&s->ciu3_idt_io[idt]), addr, siz= e); + } + + if (reg >=3D OCTEON_CIU3_DEST_PP_INT(0) && + reg < OCTEON_CIU3_DEST_PP_INT(OCTEON_MAX_CPUS)) { + cpu =3D (reg - OCTEON_CIU3_DEST_PP_INT(0)) >> 3; + value =3D 0; + if (cpu < s->cpu_count) { + if (!(qatomic_read(&s->ciu3_pp_rst) & (1ULL << cpu)) && + octeon_ciu3_pending_dest_intsn(s, cpu, &intsn)) { + value =3D OCTEON_CIU3_DEST_PP_INT_INTR | + ((uint64_t)intsn << + OCTEON_CIU3_DEST_PP_INT_INTSN_SHIFT); + } + } + return octeon_read64(value, addr, size); + } + + if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_CTL_BASE, &intsn)) { + return octeon_read64(octeon_ciu3_isc_read(s, intsn), addr, size); + } + + if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1C_BASE, &intsn) || + octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1S_BASE, &intsn)) { + return octeon_read64(octeon_ciu3_isc_read(s, intsn), addr, size); + } + + return 0; +} + +static void octeon_ciu3_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + hwaddr reg =3D addr & ~7ULL; + uint64_t old; + uint32_t intsn; + unsigned int cpu; + unsigned int idt; + unsigned int src; + + if (reg =3D=3D OCTEON_CIU3_PP_RST) { + value =3D octeon_atomic_write64(&s->ciu3_pp_rst, addr, value, size, + octeon_present_cpu_mask(s), 0, &old); + + for (cpu =3D 1; cpu < s->cpu_count; cpu++) { + uint64_t mask =3D 1ULL << cpu; + + if (!((old ^ value) & mask)) { + continue; + } + if (value & mask) { + async_run_on_cpu(CPU(s->cpu[cpu].cpu), octeon_cpu_park_wor= k, + RUN_ON_CPU_NULL); + } else { + octeon_ciu3_start_cpu(s, cpu); + } + } + octeon_intc_update_all_cpus(s); + return; + } + + if (reg =3D=3D OCTEON_CIU3_NMI) { + octeon_ciu3_nmi(s, octeon_write64(0, addr, value, size)); + octeon_intc_update_all_cpus(s); + return; + } + + if (reg >=3D OCTEON_CIU3_IDT_CTL(0) && + reg < OCTEON_CIU3_IDT_CTL(OCTEON_CIU3_IDT_COUNT)) { + idt =3D (reg - OCTEON_CIU3_IDT_CTL(0)) >> 3; + octeon_atomic_write64(&s->ciu3_idt_ctl[idt], addr, value, size, + UINT64_MAX, 0, NULL); + octeon_intc_update_cpu_mask(s, qatomic_read(&s->ciu3_idt_pp[idt])); + return; + } + + if (reg >=3D OCTEON_CIU3_IDT_PP(0) && + reg < OCTEON_CIU3_IDT_PP(OCTEON_CIU3_IDT_COUNT)) { + idt =3D (reg - OCTEON_CIU3_IDT_PP(0)) >> 5; + value =3D octeon_atomic_write64(&s->ciu3_idt_pp[idt], addr, value, + size, UINT64_MAX, 0, &old); + octeon_intc_update_cpu_mask(s, old | value); + return; + } + + if (reg >=3D OCTEON_CIU3_IDT_IO(0) && + reg < OCTEON_CIU3_IDT_IO(OCTEON_CIU3_IDT_COUNT)) { + idt =3D (reg - OCTEON_CIU3_IDT_IO(0)) >> 3; + octeon_atomic_write64(&s->ciu3_idt_io[idt], addr, value, size, + UINT64_MAX, 0, NULL); + octeon_intc_update_cpu_mask(s, qatomic_read(&s->ciu3_idt_pp[idt])); + return; + } + + if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_CTL_BASE, &intsn)) { + if (octeon_ciu3_source_from_intsn(intsn, &src)) { + value =3D octeon_atomic_write64(&s->ciu3_src_ctl[src], addr, v= alue, + size, ~OCTEON_CIU3_ISC_CTL_RAW, + 0, &old); + octeon_intc_update_cpu_mask(s, + octeon_ciu3_ctl_cpu_mask(s, old) | + octeon_ciu3_ctl_cpu_mask(s, value)= ); + } + return; + } + + if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1C_BASE, &intsn)) { + if (octeon_ciu3_source_from_intsn(intsn, &src)) { + old =3D qatomic_read(&s->ciu3_src_ctl[src]); + if (value & OCTEON_CIU3_ISC_CTL_EN) { + qatomic_and(&s->ciu3_src_ctl[src], ~OCTEON_CIU3_ISC_CTL_EN= ); + } + if (value & OCTEON_CIU3_ISC_CTL_RAW) { + octeon_ciu3_clear_raw(s, src); + } + octeon_intc_update_cpu_mask(s, + octeon_ciu3_ctl_cpu_mask(s, old) | + octeon_ciu3_source_cpu_mask(s, src= )); + } + return; + } + + if (octeon_ciu3_isc_decode(reg, OCTEON_CIU3_ISC_W1S_BASE, &intsn)) { + if (octeon_ciu3_source_from_intsn(intsn, &src)) { + old =3D qatomic_read(&s->ciu3_src_ctl[src]); + if (value & OCTEON_CIU3_ISC_CTL_EN) { + qatomic_or(&s->ciu3_src_ctl[src], OCTEON_CIU3_ISC_CTL_EN); + } + if (value & OCTEON_CIU3_ISC_CTL_RAW) { + qatomic_or(&s->ciu3_src_state[src], OCTEON_CIU3_SRC_RAW); + } + octeon_intc_update_cpu_mask(s, + octeon_ciu3_ctl_cpu_mask(s, old) | + octeon_ciu3_source_cpu_mask(s, src= )); + } + } +} + +static const MemoryRegionOps octeon_ciu3_ops =3D { + .read =3D octeon_ciu3_read, + .write =3D octeon_ciu3_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_csr_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonState *s =3D opaque; + OcteonLmcState *lmc; + hwaddr reg =3D addr & ~7ULL; + hwaddr lreg; + hwaddr ereg; + uint64_t value; + + if (reg >=3D OCTEON_MIO_BOOT_REG_CFGX(0) && + reg < OCTEON_MIO_BOOT_REG_CFGX(OCTEON_MIO_BOOT_REG_CFG_COUNT)) { + unsigned int index =3D OCTEON_MIO_BOOT_REG_CFG_INDEX(reg); + + return octeon_read64(s->mio_boot_reg_cfg[index], addr, size); + } + + switch (reg) { + case OCTEON_MIO_BOOT_LOC_CFGX(0): + case OCTEON_MIO_BOOT_LOC_CFGX(1): { + unsigned int index =3D OCTEON_MIO_BOOT_LOC_CFG_INDEX(reg); + + return octeon_read64(s->mio_boot_loc_cfg[index], addr, size); + } + case OCTEON_MIO_BOOT_LOC_ADR: + return octeon_read64(s->mio_boot_loc_adr, addr, size); + case OCTEON_MIO_BOOT_LOC_DAT: + return octeon_read64(octeon_mio_boot_loc_dat_read(s), addr, size); + case OCTEON_MIO_FUS_DAT2: + return 0; + case OCTEON_MIO_FUS_RCMD: + return octeon_read64(s->mio_fus_rcmd, addr, size); + case OCTEON_RST_BOOT: + value =3D ((uint64_t)(s->cpu_hz / s->ref_hz) << + OCTEON_RST_BOOT_C_MUL_SHIFT) | + ((uint64_t)(s->io_hz / s->ref_hz) << + OCTEON_RST_BOOT_PNR_MUL_SHIFT); + return octeon_read64(value, addr, size); + case OCTEON_RST_PP_POWER: + return octeon_read64(s->rst_pp_power, addr, size); + case OCTEON_GSERX_CFG0: + return octeon_read64(1, addr, size); + case OCTEON_GSERX_QLM_STAT0: + return octeon_read64(3, addr, size); + case OCTEON_RST_CTLX0: + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D OCTEON_RST_CTL_HOST_MODE | OCTEON_RST_CTL_RST_DONE; + } + return octeon_read64(value, addr, size); + case OCTEON_RST_SOFT_PRSTX0: + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D 1; + } + return octeon_read64(value, addr, size); + case OCTEON_PEMX_CFG_RD0: + value =3D ((uint64_t)octeon_pcie_root_cfg_read(s, + s->pcie_cfg_rd_addr) = << + 32) | s->pcie_cfg_rd_addr; + return octeon_read64(value, addr, size); + case OCTEON_PEMX_BIST_STATUS0: + return 0; + case OCTEON_PEMX_ON0: + return octeon_read64(3, addr, size); + case OCTEON_PEMX_STRAP0: + return octeon_read64(3, addr, size); + default: + break; + } + + if (octeon_lmc_decode(reg, &lmc, &lreg, s)) { + return octeon_lmc_read(s, lmc, lreg, addr, size); + } + + if (octeon_mio_emm_decode(reg, &ereg)) { + return octeon_mio_emm_read(s, ereg, addr, size); + } + + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D 0; + } + return octeon_read64(value, addr, size); +} + +static void octeon_csr_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + OcteonLmcState *lmc; + hwaddr reg =3D addr & ~7ULL; + hwaddr lreg; + hwaddr ereg; + uint64_t old; + + if (octeon_lmc_decode(reg, &lmc, &lreg, s)) { + octeon_lmc_write(lmc, lreg, addr, value, size); + return; + } + + if (octeon_mio_emm_decode(reg, &ereg)) { + octeon_mio_emm_write(s, ereg, addr, value, size); + return; + } + + if (reg =3D=3D OCTEON_RST_SOFT_RST && value) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + return; + } + + if (reg =3D=3D OCTEON_MIO_BOOT_LOC_CFGX(0) || + reg =3D=3D OCTEON_MIO_BOOT_LOC_CFGX(1)) { + unsigned int index =3D OCTEON_MIO_BOOT_LOC_CFG_INDEX(reg); + + old =3D s->mio_boot_loc_cfg[index]; + s->mio_boot_loc_cfg[index] =3D octeon_write64(old, addr, value, si= ze); + if (index =3D=3D 0) { + octeon_mio_boot_loc_update(s); + } + return; + } + + if (reg =3D=3D OCTEON_MIO_BOOT_LOC_ADR) { + old =3D s->mio_boot_loc_adr; + value =3D octeon_write64(old, addr, value, size); + s->mio_boot_loc_adr =3D value & OCTEON_MIO_BOOT_LOC_ADR_MASK; + return; + } + + if (reg =3D=3D OCTEON_MIO_BOOT_LOC_DAT) { + octeon_mio_boot_loc_dat_write(s, addr, value, size); + return; + } + + if (reg =3D=3D OCTEON_MIO_FUS_RCMD) { + value =3D octeon_write64(s->mio_fus_rcmd, addr, value, size); + s->mio_fus_rcmd =3D value & ~(OCTEON_MIO_FUS_RCMD_PEND | + OCTEON_MIO_FUS_RCMD_DAT); + return; + } + + if (reg =3D=3D OCTEON_RST_PP_POWER) { + octeon_rst_pp_power_write(s, addr, value, size); + return; + } + + if (reg =3D=3D OCTEON_PEMX_CFG_RD0) { + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + s->pcie_cfg_rd_addr =3D value; + return; + } + + if (reg =3D=3D OCTEON_PEMX_CFG_WR0) { + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_pcie_root_cfg_write(s, value, value >> 32); + octeon_csr_store(s, reg, value); + return; + } + + if (reg >=3D OCTEON_MIO_BOOT_REG_CFGX(0) && + reg < OCTEON_MIO_BOOT_REG_CFGX(OCTEON_MIO_BOOT_REG_CFG_COUNT)) { + unsigned int index =3D OCTEON_MIO_BOOT_REG_CFG_INDEX(reg); + + old =3D s->mio_boot_reg_cfg[index]; + s->mio_boot_reg_cfg[index] =3D octeon_write64(old, addr, value, si= ze); + if (index =3D=3D OCTEON_BOOTBUS_LED_CS) { + octeon_bootbus_led_update(s); + } + return; + } + + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + value =3D octeon_write64(old, addr, value, size); + octeon_csr_store(s, reg, value); +} + +static const MemoryRegionOps octeon_csr_ops =3D { + .read =3D octeon_csr_read, + .write =3D octeon_csr_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_stateful_io_read(OcteonState *s, uint64_t base, + hwaddr addr, unsigned size) +{ + uint64_t value; + uint64_t reg =3D base + (addr & ~7ULL); + + if (!octeon_csr_lookup(s, reg, &value)) { + value =3D 0; + } + + return octeon_read64(value, addr, size); +} + +static void octeon_stateful_io_write(OcteonState *s, uint64_t base, + hwaddr addr, uint64_t value, + unsigned size) +{ + uint64_t reg =3D base + (addr & ~7ULL); + uint64_t old; + + if (!octeon_csr_lookup(s, reg, &old)) { + old =3D 0; + } + + octeon_csr_store(s, reg, octeon_write64(old, addr, value, size)); +} + +static uint64_t octeon_pexp_read(void *opaque, hwaddr addr, unsigned size) +{ + return octeon_stateful_io_read(opaque, OCTEON_PEXP_BASE, addr, size); +} + +static void octeon_pexp_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + octeon_stateful_io_write(opaque, OCTEON_PEXP_BASE, addr, value, size); +} + +static const MemoryRegionOps octeon_pexp_ops =3D { + .read =3D octeon_pexp_read, + .write =3D octeon_pexp_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_dpi_read(void *opaque, hwaddr addr, unsigned size) +{ + return octeon_stateful_io_read(opaque, OCTEON_DPI_BASE, addr, size); +} + +static void octeon_dpi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + octeon_stateful_io_write(opaque, OCTEON_DPI_BASE, addr, value, size); +} + +static const MemoryRegionOps octeon_dpi_ops =3D { + .read =3D octeon_dpi_read, + .write =3D octeon_dpi_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_fpa_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonState *s =3D opaque; + hwaddr reg =3D addr & ~7ULL; + + if (reg =3D=3D OCTEON_FPA_CLK_COUNT) { + return octeon_read64(octeon_clock_count(s->io_hz), addr, size); + } + + return 0; +} + +static void octeon_fpa_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ +} + +static const MemoryRegionOps octeon_fpa_ops =3D { + .read =3D octeon_fpa_read, + .write =3D octeon_fpa_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_ipd_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonState *s =3D opaque; + hwaddr reg =3D addr & ~7ULL; + + if (reg =3D=3D OCTEON_IPD_CLK_COUNT) { + return octeon_read64(octeon_clock_count(s->io_hz), addr, size); + } + + return 0; +} + +static void octeon_ipd_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ +} + +static const MemoryRegionOps octeon_ipd_ops =3D { + .read =3D octeon_ipd_read, + .write =3D octeon_ipd_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_pko_read(void *opaque, hwaddr addr, unsigned size) +{ + return octeon_stateful_io_read(opaque, OCTEON_PKO_BASE, addr, size); +} + +static void octeon_pko_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + octeon_stateful_io_write(opaque, OCTEON_PKO_BASE, addr, value, size); +} + +static const MemoryRegionOps octeon_pko_ops =3D { + .read =3D octeon_pko_read, + .write =3D octeon_pko_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_rng_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t value; + + qemu_guest_getrandom_nofail(&value, sizeof(value)); + return octeon_read64(value, addr, size); +} + +static void octeon_rng_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ +} + +static const MemoryRegionOps octeon_rng_ops =3D { + .read =3D octeon_rng_read, + .write =3D octeon_rng_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_pow_read(void *opaque, hwaddr addr, unsigned size) +{ + return octeon_stateful_io_read(opaque, OCTEON_POW_BASE, addr, size); +} + +static void octeon_pow_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + octeon_stateful_io_write(opaque, OCTEON_POW_BASE, addr, value, size); +} + +static const MemoryRegionOps octeon_pow_ops =3D { + .read =3D octeon_pow_read, + .write =3D octeon_pow_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static uint64_t octeon_uctl_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonUsbState *usb =3D opaque; + hwaddr reg =3D addr & ~7ULL; + uint64_t value; + + if (!octeon_reg_lookup(usb->regs, reg, &value)) { + value =3D 0; + } + + return octeon_read64(value, addr, size); +} + +static void octeon_uctl_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonUsbState *usb =3D opaque; + hwaddr reg =3D addr & ~7ULL; + uint64_t old; + + if (!octeon_reg_lookup(usb->regs, reg, &old)) { + old =3D 0; + } + + octeon_reg_store(usb->regs, reg, octeon_write64(old, addr, value, size= )); +} + +static const MemoryRegionOps octeon_uctl_ops =3D { + .read =3D octeon_uctl_read, + .write =3D octeon_uctl_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static bool octeon_usb_csr_needs_swap(OcteonUsbState *usb) +{ +#if TARGET_BIG_ENDIAN + uint64_t value; + + if (!octeon_reg_lookup(usb->regs, OCTEON_UCTL_SHIM_CFG, &value)) { + return true; + } + + return (value & OCTEON_UCTL_SHIM_CFG_CSR_BYTE_SWAP) !=3D + OCTEON_UCTL_SHIM_CFG_CSR_NATIVE; +#else + return false; +#endif +} + +static uint64_t octeon_usb_swap(uint64_t value, unsigned size) +{ + switch (size) { + case 2: + return bswap16(value); + case 4: + return bswap32(value); + case 8: + return bswap64(value); + default: + return value; + } +} + +static uint64_t octeon_dwc3_window_read(void *opaque, hwaddr addr, + unsigned size) +{ + OcteonUsbState *usb =3D opaque; + uint64_t value; + + switch (size) { + case 1: + value =3D address_space_ldub(&usb->dwc3_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 2: + value =3D address_space_lduw_le(&usb->dwc3_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 4: + value =3D address_space_ldl_le(&usb->dwc3_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 8: + value =3D address_space_ldq_le(&usb->dwc3_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + default: + g_assert_not_reached(); + } + + if (octeon_usb_csr_needs_swap(usb)) { + value =3D octeon_usb_swap(value, size); + } + + return value; +} + +static void octeon_dwc3_window_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonUsbState *usb =3D opaque; + + if (octeon_usb_csr_needs_swap(usb)) { + value =3D octeon_usb_swap(value, size); + } + + switch (size) { + case 1: + address_space_stb(&usb->dwc3_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 2: + address_space_stw_le(&usb->dwc3_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 4: + address_space_stl_le(&usb->dwc3_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 8: + address_space_stq_le(&usb->dwc3_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + default: + g_assert_not_reached(); + } +} + +static const MemoryRegionOps octeon_dwc3_window_ops =3D { + .read =3D octeon_dwc3_window_read, + .write =3D octeon_dwc3_window_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + }, +}; + +static OcteonSpdEepromState *octeon_twsi_find_spd(OcteonTWSIState *t) +{ + OcteonState *s =3D t->board; + unsigned int i; + + for (i =3D 0; i < OCTEON_SPD_EEPROM_COUNT; i++) { + if (s->spd[i].bus =3D=3D t->bus && s->spd[i].addr =3D=3D t->slave_= addr) { + return &s->spd[i]; + } + } + + return NULL; +} + +static uint8_t octeon_spd_eeprom_read(OcteonSpdEepromState *eeprom) +{ + uint8_t value =3D 0xff; + + if (eeprom->offset < sizeof(eeprom->data)) { + value =3D eeprom->data[eeprom->offset]; + } + eeprom->offset++; + return value; +} + +static void octeon_twsi_write_ctl(OcteonTWSIState *t, uint8_t value) +{ + OcteonSpdEepromState *eeprom; + + t->ctl =3D value | OCTEON_TWSI_CTL_IFLG; + + if (value & OCTEON_TWSI_CTL_STA) { + t->have_slave =3D false; + t->addr_phase =3D false; + t->write_len =3D 0; + t->stat =3D OCTEON_TWSI_STAT_START; + } else if (value & OCTEON_TWSI_CTL_STP) { + t->have_slave =3D false; + t->addr_phase =3D false; + t->stat =3D OCTEON_TWSI_STAT_IDLE; + } else if (t->addr_phase) { + eeprom =3D octeon_twsi_find_spd(t); + t->addr_phase =3D false; + if (eeprom) { + t->stat =3D t->read_transfer ? OCTEON_TWSI_STAT_RXADDR_ACK : + OCTEON_TWSI_STAT_TXADDR_ACK; + } else { + t->stat =3D t->read_transfer ? OCTEON_TWSI_STAT_RXADDR_NAK : + OCTEON_TWSI_STAT_TXADDR_NAK; + } + } else if (t->read_transfer) { + t->stat =3D (value & OCTEON_TWSI_CTL_AAK) ? + OCTEON_TWSI_STAT_RXDATA_ACK : OCTEON_TWSI_STAT_RXDATA_NA= K; + } else { + t->stat =3D octeon_twsi_find_spd(t) ? OCTEON_TWSI_STAT_TXDATA_ACK : + OCTEON_TWSI_STAT_TXDATA_NAK; + } +} + +static void octeon_twsi_write_data(OcteonTWSIState *t, uint8_t value) +{ + OcteonSpdEepromState *eeprom; + + t->data =3D value; + + if (!t->have_slave) { + t->slave_addr =3D value >> 1; + t->read_transfer =3D value & 1; + t->have_slave =3D true; + t->addr_phase =3D true; + t->write_len =3D 0; + return; + } + + if (!t->read_transfer && t->write_len < ARRAY_SIZE(t->write_buf)) { + t->write_buf[t->write_len++] =3D value; + eeprom =3D octeon_twsi_find_spd(t); + if (eeprom) { + if (t->write_len =3D=3D 1) { + eeprom->offset =3D value; + } else if (t->write_len =3D=3D ARRAY_SIZE(t->write_buf)) { + eeprom->offset =3D (t->write_buf[0] << 8) | t->write_buf[1= ]; + } + } + } +} + +static uint8_t octeon_twsi_read_reg(OcteonTWSIState *t, unsigned int reg) +{ + switch (reg) { + case OCTEON_TWSI_EOP_DATA: + if (t->read_transfer) { + OcteonSpdEepromState *eeprom =3D octeon_twsi_find_spd(t); + + t->data =3D eeprom ? octeon_spd_eeprom_read(eeprom) : 0xff; + } + return t->data; + case OCTEON_TWSI_EOP_CTL: + return t->ctl; + case OCTEON_TWSI_EOP_STAT: + return t->stat; + default: + return 0; + } +} + +static void octeon_twsi_write_reg(OcteonTWSIState *t, unsigned int reg, + uint8_t value) +{ + switch (reg) { + case OCTEON_TWSI_EOP_DATA: + octeon_twsi_write_data(t, value); + break; + case OCTEON_TWSI_EOP_CTL: + octeon_twsi_write_ctl(t, value); + break; + default: + break; + } +} + +static uint64_t octeon_twsi_exec(OcteonTWSIState *t, uint64_t value) +{ + unsigned int op =3D (value >> OCTEON_TWSI_SW_OP_SHIFT) & 0xf; + unsigned int reg =3D (value >> OCTEON_TWSI_SW_EOP_IA_SHIFT) & 0x7; + + if (!(value & OCTEON_TWSI_SW_V)) { + return value; + } + + if (op =3D=3D OCTEON_TWSI_OP_EOP_IA) { + if (value & OCTEON_TWSI_SW_R) { + value &=3D ~OCTEON_TWSI_SW_DATA_MASK; + value |=3D octeon_twsi_read_reg(t, reg); + } else { + octeon_twsi_write_reg(t, reg, value & 0xff); + } + } + + return value & ~OCTEON_TWSI_SW_V; +} + +static uint64_t octeon_twsi_read(void *opaque, hwaddr addr, unsigned size) +{ + OcteonTWSIState *t =3D opaque; + hwaddr reg =3D addr & ~7ULL; + + switch (reg) { + case OCTEON_TWSI_SW_TWSI: + return octeon_read64(t->sw_twsi, addr, size); + case OCTEON_TWSI_INT: + return octeon_read64(t->int_reg, addr, size); + default: + return 0; + } +} + +static void octeon_twsi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonTWSIState *t =3D opaque; + hwaddr reg =3D addr & ~7ULL; + uint64_t old =3D 0; + + switch (reg) { + case OCTEON_TWSI_SW_TWSI: + old =3D t->sw_twsi; + value =3D octeon_write64(old, addr, value, size); + if (size =3D=3D 4 && !(addr & 4)) { + t->sw_twsi =3D value; + } else { + t->sw_twsi =3D octeon_twsi_exec(t, value); + } + break; + case OCTEON_TWSI_INT: + old =3D t->int_reg; + t->int_reg =3D octeon_write64(old, addr, value, size); + break; + default: + break; + } +} + +static const MemoryRegionOps octeon_twsi_ops =3D { + .read =3D octeon_twsi_read, + .write =3D octeon_twsi_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 8, + }, +}; + +static void octeon_irq_set(void *opaque, int irq, int level) +{ + OcteonState *s =3D opaque; + unsigned int src; + + switch (irq) { + case OCTEON_IRQ_UART: + src =3D OCTEON_CIU3_SRC_UART0; + if (!octeon_ciu3_set_level(s, src, level)) { + return; + } + qatomic_set(&s->uart_pending, level); + break; + case OCTEON_IRQ_PCI: + src =3D OCTEON_CIU3_SRC_PCI_INTA; + if (!octeon_ciu3_set_level(s, src, level)) { + return; + } + qatomic_set(&s->pci_pending, level); + break; + case OCTEON_IRQ_USB0: + src =3D OCTEON_CIU3_SRC_USB0; + if (!octeon_ciu3_set_level(s, src, level)) { + return; + } + break; + case OCTEON_IRQ_USB1: + src =3D OCTEON_CIU3_SRC_USB1; + if (!octeon_ciu3_set_level(s, src, level)) { + return; + } + break; + default: + return; + } + + octeon_intc_update_cpu_mask(s, octeon_ciu3_source_cpu_mask(s, src)); +} + +static uint64_t octeon_pci_absent(unsigned size) +{ + return size =3D=3D 1 ? 0xff : size =3D=3D 2 ? 0xffff : 0xffffffffU; +} + +static bool octeon_pcie_config_addr(hwaddr addr, uint32_t *pci_addr, + bool sli_cfg) +{ + unsigned int bus =3D (addr >> 20) & 0xff; + unsigned int dev =3D (addr >> 15) & 0x1f; + unsigned int fn =3D (addr >> 12) & 0x7; + unsigned int reg =3D addr & 0xfff; + + if (sli_cfg) { + if (addr >=3D OCTEON_PCIE_SLI_CFG_PORT_SIZE || + bus !=3D 0 || dev !=3D 0 || fn !=3D 0 || + reg >=3D PCI_CONFIG_SPACE_SIZE) { + return false; + } + } else if (bus !=3D 1 || dev !=3D 0 || fn !=3D 0 || + reg >=3D PCI_CONFIG_SPACE_SIZE) { + return false; + } + + *pci_addr =3D 0x80000000U | (OCTEON_PCIE0_ENDPOINT_DEVFN << 8) | reg; + return true; +} + +static uint64_t octeon_pcie_cfg_read(void *opaque, hwaddr addr, unsigned s= ize) +{ + OcteonState *s =3D opaque; + uint32_t pci_addr; + + if (!octeon_pcie_config_addr(addr, &pci_addr, false)) { + return octeon_pci_absent(size); + } + + return pci_data_read(s->pci_bus, pci_addr, size); +} + +static void octeon_pcie_cfg_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + uint32_t pci_addr; + + if (octeon_pcie_config_addr(addr, &pci_addr, false)) { + pci_data_write(s->pci_bus, pci_addr, value, size); + } +} + +static const MemoryRegionOps octeon_pcie_cfg_ops =3D { + .read =3D octeon_pcie_cfg_read, + .write =3D octeon_pcie_cfg_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 4, + }, +}; + +static uint64_t octeon_pcie_sli_cfg_read(void *opaque, hwaddr addr, + unsigned size) +{ + OcteonState *s =3D opaque; + uint32_t pci_addr; + + if (!octeon_pcie_config_addr(addr, &pci_addr, true)) { + return octeon_pci_absent(size); + } + + return pci_data_read(s->pci_bus, pci_addr, size); +} + +static void octeon_pcie_sli_cfg_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + uint32_t pci_addr; + + if (octeon_pcie_config_addr(addr, &pci_addr, true)) { + pci_data_write(s->pci_bus, pci_addr, value, size); + } +} + +static const MemoryRegionOps octeon_pcie_sli_cfg_ops =3D { + .read =3D octeon_pcie_sli_cfg_read, + .write =3D octeon_pcie_sli_cfg_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 4, + }, +}; + +static uint64_t octeon_pcie_bswap(uint64_t value, unsigned size) +{ + switch (size) { + case 1: + return value; + case 2: + return bswap16(value); + case 4: + return bswap32(value); + case 8: + return bswap64(value); + default: + g_assert_not_reached(); + } +} + +static uint64_t octeon_pcie_mem_subid_reg(hwaddr addr) +{ + unsigned int subid =3D (addr >> 28) & 3; + + return OCTEON_PEXP_BASE + OCTEON_SLI_MEM_ACCESS_SUBID_BASE + + subid * OCTEON_SLI_MEM_ACCESS_SUBID_STRIDE; +} + +static bool octeon_pcie_mem_swap_enabled(OcteonState *s, hwaddr addr, + bool write) +{ + uint64_t value; + uint64_t mask =3D write ? OCTEON_SLI_MEM_ACCESS_SUBID_ESW_M : + OCTEON_SLI_MEM_ACCESS_SUBID_ESR_M; + unsigned int shift =3D write ? OCTEON_SLI_MEM_ACCESS_SUBID_ESW_S : + OCTEON_SLI_MEM_ACCESS_SUBID_ESR_S; + + if (!octeon_csr_lookup(s, octeon_pcie_mem_subid_reg(addr), &value)) { + return false; + } + + return ((value & mask) >> shift) =3D=3D OCTEON_SLI_MEM_ACCESS_SWAP; +} + +static uint64_t octeon_pcie0_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + OcteonState *s =3D opaque; + uint64_t value; + + switch (size) { + case 1: + value =3D address_space_ldub(&s->pcie0_mem_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 2: + value =3D address_space_lduw_le(&s->pcie0_mem_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 4: + value =3D address_space_ldl_le(&s->pcie0_mem_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 8: + value =3D address_space_ldq_le(&s->pcie0_mem_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + default: + g_assert_not_reached(); + } + + if (TARGET_BIG_ENDIAN && !octeon_pcie_mem_swap_enabled(s, addr, false)= ) { + value =3D octeon_pcie_bswap(value, size); + } + + return value; +} + +static void octeon_pcie0_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + + if (TARGET_BIG_ENDIAN && !octeon_pcie_mem_swap_enabled(s, addr, true))= { + value =3D octeon_pcie_bswap(value, size); + } + + switch (size) { + case 1: + address_space_stb(&s->pcie0_mem_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 2: + address_space_stw_le(&s->pcie0_mem_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 4: + address_space_stl_le(&s->pcie0_mem_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + case 8: + address_space_stq_le(&s->pcie0_mem_as, addr, value, + MEMTXATTRS_UNSPECIFIED, NULL); + break; + default: + g_assert_not_reached(); + } +} + +static const MemoryRegionOps octeon_pcie0_mem_ops =3D { + .read =3D octeon_pcie0_mem_read, + .write =3D octeon_pcie0_mem_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + }, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + }, +}; + +static int octeon_pci_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return 0; +} + +static void octeon_pci_set_irq(void *opaque, int irq_num, int level) +{ + octeon_irq_set(opaque, OCTEON_IRQ_PCI, level); +} + +static void octeon_pci_host_class_init(ObjectClass *klass, const void *dat= a) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->fw_name =3D "pci"; + dc->user_creatable =3D false; +} + +static const TypeInfo octeon_pci_host_info =3D { + .name =3D TYPE_OCTEON_PCI_HOST, + .parent =3D TYPE_PCI_HOST_BRIDGE, + .instance_size =3D sizeof(OcteonPCIHostState), + .class_init =3D octeon_pci_host_class_init, +}; + +static void octeon_pci_host_register_types(void) +{ + type_register_static(&octeon_pci_host_info); +} + +type_init(octeon_pci_host_register_types) + +static void octeon_cpu_reset(void *opaque) +{ + OcteonCPUState *cs =3D opaque; + CPUMIPSState *env =3D &cs->cpu->env; + CPUState *cpu =3D CPU(cs->cpu); + + cpu_reset(cpu); + + env->CP0_CvmMemCtl =3D 0x100; + env->CP0_CvmCtl =3D 0; + + if (!cs->boot_cpu) { + /* Secondary cores are released by the Octeon reset controller. */ + octeon_cpu_park_now(cs->cpu); + return; + } + + if (cs->board->firmware_entry) { + env->active_tc.PC =3D cs->board->firmware_entry; + } +} + +static void octeon_create_cpus(OcteonState *s) +{ + unsigned int i; + + s->cpuclk =3D clock_new(OBJECT(s->machine), "cpu-refclk"); + clock_set_hz(s->cpuclk, s->cpu_hz); + s->cpu_count =3D s->machine->smp.cpus; + qatomic_set(&s->ciu3_pp_rst, octeon_secondary_cpu_mask(s)); + + for (i =3D 0; i < s->cpu_count; i++) { + DeviceState *dev =3D qdev_new(s->machine->cpu_type); + + s->cpu[i].board =3D s; + s->cpu[i].boot_cpu =3D i =3D=3D 0; + object_property_set_bool(OBJECT(dev), "big-endian", TARGET_BIG_END= IAN, + &error_abort); + object_property_set_bool(OBJECT(dev), "start-powered-off", i !=3D = 0, + &error_abort); + qdev_connect_clock_in(dev, "clk-in", s->cpuclk); + qdev_realize(dev, NULL, &error_abort); + s->cpu[i].cpu =3D MIPS_CPU(dev); + + cpu_mips_irq_init_cpu(s->cpu[i].cpu); + cpu_mips_clock_init(s->cpu[i].cpu); + qemu_register_reset(octeon_cpu_reset, &s->cpu[i]); + } +} + +static uint64_t octeon_map_ram_alias(OcteonState *s, MemoryRegion *alias, + const char *name, hwaddr base, + uint64_t offset, uint64_t size) +{ + MemoryRegion *sysmem =3D get_system_memory(); + + if (!size) { + return offset; + } + + memory_region_init_alias(alias, NULL, name, s->machine->ram, offset, s= ize); + memory_region_add_subregion(sysmem, base, alias); + return offset + size; +} + +static void octeon_map_ram(OcteonState *s) +{ + MemoryRegion *sysmem =3D get_system_memory(); + uint64_t offset =3D 0; + uint64_t remaining =3D s->machine->ram_size; + uint64_t size; + + size =3D MIN(remaining, (uint64_t)OCTEON_DR0_SIZE); + offset =3D octeon_map_ram_alias(s, &s->dr0, "octeon.dr0", + OCTEON_DR0_BASE, offset, size); + remaining -=3D size; + + size =3D MIN(remaining, (uint64_t)OCTEON_DR1_SIZE); + octeon_map_ram_alias(s, &s->dr1, "octeon.dr1", + OCTEON_DR1_BASE, offset, size); + + memory_region_init_ram(&s->cvmseg, NULL, "octeon.cvmseg", + OCTEON_CVMSEG_TOTAL_SIZE, &error_fatal); + memory_region_add_subregion(sysmem, OCTEON_CVMSEG_BASE, &s->cvmseg); +} + +static void octeon_load_firmware(OcteonState *s) +{ + g_autofree char *filename =3D NULL; + g_autofree gchar *contents =3D NULL; + g_autoptr(GError) err =3D NULL; + gsize len; + uint8_t *flash; + + if (!s->machine->firmware) { + return; + } + if (!s->flash) { + error_report("Octeon boot flash is not initialized"); + exit(1); + } + + filename =3D qemu_find_file(QEMU_FILE_TYPE_BIOS, s->machine->firmware); + if (!filename) { + error_report("Could not find Octeon firmware '%s'", + s->machine->firmware); + exit(1); + } + + if (!g_file_get_contents(filename, &contents, &len, &err)) { + error_report("Could not load Octeon firmware '%s': %s", + filename, err->message); + exit(1); + } + + if (len =3D=3D 0 || len > OCTEON_FLASH_ENV_OFFSET) { + error_report("Octeon firmware '%s' has invalid size %" G_GSIZE_FOR= MAT, + filename, len); + exit(1); + } + + flash =3D memory_region_get_ram_ptr(s->flash); + memcpy(flash, contents, len); + s->firmware_entry =3D cpu_mips_phys_to_kseg1(NULL, OCTEON_FLASH_RESET_= BASE); +} + +static MemTxResult octeon_boot_flash_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + OcteonState *s =3D opaque; + uint8_t buf[8]; + MemTxResult result =3D MEMTX_OK; + unsigned int i; + + for (i =3D 0; i < size; i++) { + uint64_t value; + + result |=3D memory_region_dispatch_read(s->flash, addr + i, &value, + MO_8, attrs); + buf[i] =3D value; + } + *data =3D ldn_be_p(buf, size); + return result; +} + +static MemTxResult octeon_boot_flash_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + OcteonState *s =3D opaque; + unsigned access_size =3D MIN(size, 4); + + if (size > access_size) { + access_size =3D OCTEON_FLASH_WIDTH; + } + return memory_region_dispatch_write(s->flash, addr, value, + size_memop(access_size) | MO_BE, + attrs); +} + +static const MemoryRegionOps octeon_boot_flash_ops =3D { + .read_with_attrs =3D octeon_boot_flash_read, + .write_with_attrs =3D octeon_boot_flash_write, + .endianness =3D DEVICE_BIG_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + }, +}; + +static void octeon_init_flash(OcteonState *s) +{ + DriveInfo *dinfo =3D drive_get(IF_PFLASH, 0, 0); + DeviceState *dev; + uint8_t *flash; + + dev =3D qdev_new(TYPE_PFLASH_CFI02); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); + } + qdev_prop_set_uint32(dev, "num-blocks0", OCTEON_FLASH_MAIN_SECTORS); + qdev_prop_set_uint32(dev, "sector-length0", + OCTEON_FLASH_MAIN_SECTOR_SIZE); + qdev_prop_set_uint32(dev, "num-blocks1", OCTEON_FLASH_ENV_SECTORS); + qdev_prop_set_uint32(dev, "sector-length1", + OCTEON_FLASH_ENV_SECTOR_SIZE); + qdev_prop_set_uint8(dev, "width", OCTEON_FLASH_WIDTH); + qdev_prop_set_uint8(dev, "mappings", 1); + qdev_prop_set_uint8(dev, "big-endian", TARGET_BIG_ENDIAN); + qdev_prop_set_uint16(dev, "id0", 0x01); + qdev_prop_set_uint16(dev, "id1", 0x7e); + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x00); + qdev_prop_set_uint16(dev, "unlock-addr0", 0x555); + qdev_prop_set_uint16(dev, "unlock-addr1", 0x2aa); + qdev_prop_set_string(dev, "name", "octeon.bootbus-flash"); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + s->flash =3D sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + if (!dinfo) { + flash =3D memory_region_get_ram_ptr(s->flash); + memset(flash, 0xff, OCTEON_FLASH_SIZE); + } + memory_region_init_io(&s->boot_flash, NULL, &octeon_boot_flash_ops, s, + "octeon.bootbus-flash-window", OCTEON_FLASH_SIZE= ); + memory_region_add_subregion(get_system_memory(), OCTEON_FLASH_BASE, + &s->boot_flash); + + memory_region_init_alias(&s->boot_flash_alias, NULL, + "octeon.bootbus-flash-reset-alias", + &s->boot_flash, 0, OCTEON_FLASH_RESET_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_FLASH_RESET_BA= SE, + &s->boot_flash_alias); +} + +static void octeon_init_mio_boot_loc(OcteonState *s) +{ + memory_region_init_ram(&s->bootbus_led, NULL, "octeon.bootbus-led", + OCTEON_BOOTBUS_LED_SIZE, &error_fatal); + memory_region_init_ram_ptr(&s->mio_boot_loc, NULL, + "octeon.mio-boot-loc", + OCTEON_MIO_BOOT_LOC_SIZE, + s->mio_boot_loc_data); +} + +static void octeon_init_pci(OcteonState *s) +{ + DeviceState *dev; + OcteonPCIHostState *host; + PCIHostState *phb; + + dev =3D qdev_new(TYPE_OCTEON_PCI_HOST); + host =3D OCTEON_PCI_HOST(dev); + phb =3D PCI_HOST_BRIDGE(dev); + + memory_region_init(&host->mem, OBJECT(dev), "octeon.pci-mem", + OCTEON_PCIE0_MEM_SIZE); + memory_region_init(&host->io, OBJECT(dev), "octeon.pci-io", + OCTEON_PCIE0_IO_SIZE); + + phb->bus =3D pci_root_bus_new(dev, "pci", &host->mem, &host->io, + OCTEON_PCIE0_ENDPOINT_DEVFN, TYPE_PCI_BUS); + s->pci_bus =3D phb->bus; + pci_bus_irqs(s->pci_bus, octeon_pci_set_irq, s, 1); + pci_bus_map_irqs(s->pci_bus, octeon_pci_map_irq); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + memory_region_init_io(&s->pcie_cfg, NULL, &octeon_pcie_cfg_ops, s, + "octeon.pcie-config", OCTEON_PCIE_CFG_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_PCIE_CFG_BASE, + &s->pcie_cfg); + + memory_region_init_io(&s->pcie_sli_cfg, NULL, &octeon_pcie_sli_cfg_ops, + s, "octeon.pcie-sli-config", + OCTEON_PCIE_SLI_CFG_SIZE); + memory_region_add_subregion(get_system_memory(), + OCTEON_PCIE_SLI_CFG_BASE, + &s->pcie_sli_cfg); + + memory_region_init_alias(&s->pcie0_io, NULL, "octeon.pcie0-io", + &host->io, 0, + OCTEON_PCIE0_IO_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_PCIE0_IO_BASE, + &s->pcie0_io); + + address_space_init(&s->pcie0_mem_as, &host->mem, + "octeon.pcie0-mem-as"); + memory_region_init_io(&s->pcie0_mem, NULL, &octeon_pcie0_mem_ops, s, + "octeon.pcie0-mem", OCTEON_PCIE0_MEM_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_PCIE0_MEM_BASE, + &s->pcie0_mem); +} + +static uint64_t octeon_uart_tx_read(void *opaque, hwaddr addr, unsigned si= ze) +{ + return 0; +} + +static void octeon_uart_tx_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OcteonState *s =3D opaque; + + if (size =3D=3D OCTEON_CSR_32BIT_SIZE && + addr =3D=3D OCTEON_UART_TX_HI32_OFFSET) { + return; + } + + serial_io_ops.write(&s->uart->serial, 0, value & 0xff, 1); +} + +static const MemoryRegionOps octeon_uart_tx_ops =3D { + .read =3D octeon_uart_tx_read, + .write =3D octeon_uart_tx_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + }, +}; + +static void octeon_init_uart(OcteonState *s) +{ + SerialMM *uart; + qemu_irq uart_irq; + MemoryRegion *uart_mmio; + int baudbase =3D s->io_hz / 16; + + uart_irq =3D qemu_allocate_irq(octeon_irq_set, s, OCTEON_IRQ_UART); + uart =3D serial_mm_init(get_system_memory(), OCTEON_UART0_BASE, 3, uar= t_irq, + baudbase, serial_hd(0), DEVICE_NATIVE_ENDIAN); + s->uart =3D uart; + + uart_mmio =3D sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); + memory_region_init_alias(&s->uart_alias, NULL, "octeon.uart0-alias", + uart_mmio, 0, memory_region_size(uart_mmio)); + memory_region_add_subregion(get_system_memory(), OCTEON_UART0_ALIAS_BA= SE, + &s->uart_alias); + + memory_region_init_io(&s->uart_tx, NULL, &octeon_uart_tx_ops, s, + "octeon.uart0-tx", OCTEON_UART_TX_SIZE); + memory_region_add_subregion(get_system_memory(), + OCTEON_UART0_BASE + OCTEON_UART_TX_REG, + &s->uart_tx); + memory_region_init_io(&s->uart_alias_tx, NULL, &octeon_uart_tx_ops, s, + "octeon.uart0-alias-tx", OCTEON_UART_TX_SIZE); + memory_region_add_subregion(get_system_memory(), + OCTEON_UART0_ALIAS_BASE + OCTEON_UART_TX_R= EG, + &s->uart_alias_tx); +} + +static void octeon_init_twsi(OcteonState *s) +{ + static const uint64_t base[OCTEON_TWSI_COUNT] =3D { + OCTEON_TWSI0_BASE, + OCTEON_TWSI1_BASE, + }; + static const char *const name[OCTEON_TWSI_COUNT] =3D { + "octeon.twsi0", + "octeon.twsi1", + }; + unsigned int i; + + for (i =3D 0; i < OCTEON_TWSI_COUNT; i++) { + s->twsi[i].board =3D s; + s->twsi[i].bus =3D i; + s->twsi[i].stat =3D OCTEON_TWSI_STAT_IDLE; + + memory_region_init_io(&s->twsi[i].mmio, NULL, &octeon_twsi_ops, + &s->twsi[i], name[i], OCTEON_TWSI_SIZE); + memory_region_add_subregion(get_system_memory(), base[i], + &s->twsi[i].mmio); + } +} + +static void octeon_init_usb(OcteonState *s) +{ + static const uint64_t uctl_base[OCTEON_USB_COUNT] =3D { + OCTEON_USB0_UCTL_BASE, + OCTEON_USB1_UCTL_BASE, + }; + static const uint64_t dwc3_base[OCTEON_USB_COUNT] =3D { + OCTEON_USB0_DWC3_BASE, + OCTEON_USB1_DWC3_BASE, + }; + static const char * const uctl_name[OCTEON_USB_COUNT] =3D { + "octeon.usb0-uctl", + "octeon.usb1-uctl", + }; + static const char * const dwc3_window_name[OCTEON_USB_COUNT] =3D { + "octeon.usb0-dwc3-window", + "octeon.usb1-dwc3-window", + }; + static const char * const dwc3_as_name[OCTEON_USB_COUNT] =3D { + "octeon.usb0-dwc3-as", + "octeon.usb1-dwc3-as", + }; + static const OcteonIRQ irq[OCTEON_USB_COUNT] =3D { + OCTEON_IRQ_USB0, + OCTEON_IRQ_USB1, + }; + unsigned int i; + + for (i =3D 0; i < OCTEON_USB_COUNT; i++) { + OcteonUsbState *usb =3D &s->usb[i]; + DeviceState *dev =3D qdev_new(TYPE_USB_DWC3); + USBDWC3 *dwc3 =3D USB_DWC3(dev); + MemoryRegion *dwc3_mr; + + usb->board =3D s; + usb->dwc3 =3D dwc3; + usb->index =3D i; + usb->regs =3D g_hash_table_new_full(octeon_uint64_hash, + octeon_uint64_equal, + g_free, g_free); + memory_region_init_io(&usb->uctl, NULL, &octeon_uctl_ops, usb, + uctl_name[i], OCTEON_UCTL_SIZE); + memory_region_add_subregion(get_system_memory(), uctl_base[i], + &usb->uctl); + + object_property_set_link(OBJECT(&dwc3->sysbus_xhci), "dma", + OBJECT(get_system_memory()), &error_abort= ); + qdev_prop_set_uint32(DEVICE(&dwc3->sysbus_xhci), "intrs", 1); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + dwc3_mr =3D sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + address_space_init(&usb->dwc3_as, dwc3_mr, dwc3_as_name[i]); + memory_region_init_io(&usb->dwc3_window, NULL, + &octeon_dwc3_window_ops, usb, + dwc3_window_name[i], DWC3_SIZE); + memory_region_add_subregion(get_system_memory(), dwc3_base[i], + &usb->dwc3_window); + sysbus_connect_irq(SYS_BUS_DEVICE(&dwc3->sysbus_xhci), 0, + qemu_allocate_irq(octeon_irq_set, s, irq[i])); + } +} + +static void octeon_board_reset(void *opaque) +{ + OcteonState *s =3D opaque; + unsigned int i; + + if (s->bootbus_led_mapped) { + memory_region_del_subregion(get_system_memory(), &s->bootbus_led); + s->bootbus_led_mapped =3D false; + } + memset(s->mio_boot_reg_cfg, 0, sizeof(s->mio_boot_reg_cfg)); + s->mio_boot_reg_cfg[0] =3D OCTEON_MIO_BOOT_REG_CFG_RESET_BASE; + s->mio_boot_loc_cfg[0] =3D 0; + s->mio_boot_loc_cfg[1] =3D 0; + s->mio_boot_loc_adr =3D 0; + s->mio_fus_rcmd =3D 0; + s->mio_emm_rsp_sts =3D 0; + s->mio_emm_rsp_lo =3D 0; + s->mio_emm_rsp_hi =3D 0; + s->mio_emm_int =3D 0; + s->mio_emm_dma_int =3D 0; + s->mio_emm_buf_idx =3D 0; + s->mio_emm_buf_inc =3D false; + memset(s->mio_emm_buf, 0, sizeof(s->mio_emm_buf)); + s->rst_pp_power =3D octeon_secondary_cpu_mask(s); + memset(s->mio_boot_loc_data, 0, sizeof(s->mio_boot_loc_data)); + octeon_mio_boot_loc_update(s); + + s->pcie_cfg_rd_addr =3D 0; + memset(s->ciu_mbox, 0, sizeof(s->ciu_mbox)); + memset(s->ciu_ipi_en, 0, sizeof(s->ciu_ipi_en)); + qatomic_set(&s->ciu_gpio_rx, OCTEON_CIU_GPIO_INPUTS); + qatomic_set(&s->ciu_gpio_tx, 0); + memset(s->ciu_gpio_bit_cfg, 0, sizeof(s->ciu_gpio_bit_cfg)); + qatomic_set(&s->uart_pending, false); + qatomic_set(&s->pci_pending, false); + qatomic_set(&s->ciu3_pp_rst, octeon_secondary_cpu_mask(s)); + memset(s->ciu3_src_state, 0, sizeof(s->ciu3_src_state)); + memset(s->ciu3_src_ctl, 0, sizeof(s->ciu3_src_ctl)); + memset(s->ciu3_idt_ctl, 0, sizeof(s->ciu3_idt_ctl)); + memset(s->ciu3_idt_pp, 0, sizeof(s->ciu3_idt_pp)); + memset(s->ciu3_idt_io, 0, sizeof(s->ciu3_idt_io)); + memset(s->ciu3_src_cursor, 0, sizeof(s->ciu3_src_cursor)); + memset(s->ciu3_irq_line, 0, sizeof(s->ciu3_irq_line)); + + for (i =3D 0; i < OCTEON_LMC_COUNT; i++) { + g_hash_table_remove_all(s->lmc[i].regs); + } + for (i =3D 0; i < OCTEON_TWSI_COUNT; i++) { + OcteonTWSIState *t =3D &s->twsi[i]; + + t->sw_twsi =3D 0; + t->int_reg =3D 0; + t->ctl =3D 0; + t->stat =3D OCTEON_TWSI_STAT_IDLE; + t->data =3D 0; + t->slave_addr =3D 0; + t->write_len =3D 0; + t->have_slave =3D false; + t->read_transfer =3D false; + t->addr_phase =3D false; + memset(t->write_buf, 0, sizeof(t->write_buf)); + } + for (i =3D 0; i < OCTEON_USB_COUNT; i++) { + g_hash_table_remove_all(s->usb[i].regs); + } + for (i =3D 0; i < OCTEON_SPD_EEPROM_COUNT; i++) { + s->spd[i].offset =3D 0; + } + + g_hash_table_remove_all(s->csr_values); + octeon_intc_update_all_cpus(s); +} + +static void mips_octeon_init(MachineState *machine) +{ + OcteonMachineState *oms =3D OCTEON_MACHINE(machine); + OcteonState *s =3D g_new0(OcteonState, 1); + + octeon_validate_clocks(oms); + + s->machine =3D machine; + s->cpu_hz =3D oms->cpu_hz; + s->ref_hz =3D oms->ref_hz; + s->io_hz =3D oms->io_hz; + s->ddr_hz =3D oms->ddr_hz; + s->csr_values =3D g_hash_table_new_full(octeon_uint64_hash, + octeon_uint64_equal, + g_free, g_free); + + if (machine->kernel_filename) { + error_report("-kernel is not implemented for octeon3; " + "boot via -bios and U-Boot"); + exit(1); + } + + octeon_init_lmcs(s); + octeon_validate_ram_size(machine->ram_size); + octeon_init_spd(s); + octeon_map_ram(s); + octeon_init_flash(s); + octeon_init_mio_boot_loc(s); + octeon_load_firmware(s); + octeon_create_cpus(s); + + memory_region_init_io(&s->ciu, NULL, &octeon_ciu_ops, s, + "octeon.ciu", OCTEON_CIU_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_CIU_BASE, + &s->ciu); + + memory_region_init_io(&s->ciu3, NULL, &octeon_ciu3_ops, s, + "octeon.ciu3", OCTEON_CIU3_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_CIU3_BASE, + &s->ciu3); + + octeon_init_uart(s); + octeon_init_twsi(s); + + memory_region_init_io(&s->csr, NULL, &octeon_csr_ops, s, + "octeon.csr", OCTEON_CSR_SIZE); + memory_region_add_subregion_overlap(get_system_memory(), OCTEON_CSR_BA= SE, + &s->csr, -1); + + memory_region_init_io(&s->pexp, NULL, &octeon_pexp_ops, s, + "octeon.pexp", OCTEON_PEXP_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_PEXP_BASE, + &s->pexp); + + memory_region_init_io(&s->dpi, NULL, &octeon_dpi_ops, s, + "octeon.dpi", OCTEON_DPI_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_DPI_BASE, + &s->dpi); + + memory_region_init_io(&s->fpa, NULL, &octeon_fpa_ops, s, + "octeon.fpa", OCTEON_FPA_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_FPA_BASE, + &s->fpa); + + memory_region_init_io(&s->ipd, NULL, &octeon_ipd_ops, s, + "octeon.ipd", OCTEON_IPD_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_IPD_BASE, + &s->ipd); + + memory_region_init_io(&s->pko, NULL, &octeon_pko_ops, s, + "octeon.pko", OCTEON_PKO_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_PKO_BASE, + &s->pko); + + memory_region_init_io(&s->rng, NULL, &octeon_rng_ops, s, + "octeon.rng", OCTEON_RNG_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_RNG_BASE, + &s->rng); + + memory_region_init_io(&s->pow, NULL, &octeon_pow_ops, s, + "octeon.pow", OCTEON_POW_SIZE); + memory_region_add_subregion(get_system_memory(), OCTEON_POW_BASE, + &s->pow); + + octeon_init_usb(s); + octeon_init_pci(s); + qemu_register_reset(octeon_board_reset, s); +} + +static void octeon_machine_instance_init(Object *obj) +{ + OcteonMachineState *oms =3D OCTEON_MACHINE(obj); + + oms->cpu_hz =3D OCTEON_DEFAULT_CPU_HZ; + oms->ref_hz =3D OCTEON_DEFAULT_REF_HZ; + oms->io_hz =3D OCTEON_DEFAULT_IO_HZ; + oms->ddr_hz =3D OCTEON_DEFAULT_DDR_HZ; + + object_property_add_uint64_ptr(obj, "cpu-clock-hz", &oms->cpu_hz, + OBJ_PROP_FLAG_READWRITE); + object_property_set_description(obj, "cpu-clock-hz", + "CPU clock frequency in Hz"); + + object_property_add_uint64_ptr(obj, "ref-clock-hz", &oms->ref_hz, + OBJ_PROP_FLAG_READWRITE); + object_property_set_description(obj, "ref-clock-hz", + "Reference clock frequency in Hz"); + + object_property_add_uint64_ptr(obj, "io-clock-hz", &oms->io_hz, + OBJ_PROP_FLAG_READWRITE); + object_property_set_description(obj, "io-clock-hz", + "IO clock frequency in Hz"); + + object_property_add_uint64_ptr(obj, "ddr-clock-hz", &oms->ddr_hz, + OBJ_PROP_FLAG_READWRITE); + object_property_set_description(obj, "ddr-clock-hz", + "DDR clock frequency in Hz"); +} + +static void octeon_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc =3D MACHINE_CLASS(oc); + + mc->desc =3D "Cavium Octeon III / EBB7304 EVK"; + mc->init =3D mips_octeon_init; + mc->default_cpu_type =3D MIPS_CPU_TYPE_NAME("OcteonCN73XX"); + mc->default_ram_size =3D OCTEON_DEFAULT_RAM_SIZE; + mc->default_ram_id =3D "octeon.ram"; + mc->max_cpus =3D OCTEON_MAX_CPUS; + mc->pci_allow_0_address =3D true; +} + +static const TypeInfo octeon_machine_types[] =3D { + { + .name =3D TYPE_OCTEON_MACHINE, + .parent =3D TYPE_MACHINE, + .instance_size =3D sizeof(OcteonMachineState), + .instance_init =3D octeon_machine_instance_init, + .class_init =3D octeon_machine_class_init, + } +}; + +DEFINE_TYPES(octeon_machine_types) diff --git a/tests/functional/mips64/meson.build b/tests/functional/mips64/= meson.build index 3ff2118987..98b7c491a1 100644 --- a/tests/functional/mips64/meson.build +++ b/tests/functional/mips64/meson.build @@ -2,9 +2,11 @@ =20 test_mips64_timeouts =3D { 'malta' : 240, + 'octeon3' : 300, } =20 tests_mips64_system_thorough =3D [ 'malta', + 'octeon3', 'tuxrun', ] diff --git a/tests/functional/mips64/test_octeon3.py b/tests/functional/mip= s64/test_octeon3.py new file mode 100644 index 0000000000..9c2bc67ac4 --- /dev/null +++ b/tests/functional/mips64/test_octeon3.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# +# Functional test for the Cavium Octeon III / EBB7304 machine. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait + + +class Octeon3Machine(QemuSystemTest): + + ASSET_UBOOT =3D Asset( + 'https://kirill.korins.ky/pub/qemu-mips64-openbsd/u-boot.bin', + '7e519b2e52ba44b27bfafd16738c8f485e9eda5438b4899cf6a70520a6d34396') + + ASSET_BSD_RD =3D Asset( + 'https://kirill.korins.ky/pub/qemu-mips64-openbsd/bsd.rd', + '907f26f0ff67390dd21dc85e2c854fc0e65db295b15d47ae92e1927f98ac5872') + + def test_openbsd_ramdisk_tftp(self): + self.require_netdev('user') + self.require_device('e1000') + self.set_machine('octeon3') + + tftp_dir =3D self.scratch_file('tftp') + os.mkdir(tftp_dir) + shutil.copy(self.ASSET_BSD_RD.fetch(), + os.path.join(tftp_dir, 'bsd.rd')) + + self.vm.set_console() + self.vm.add_args('-bios', self.ASSET_UBOOT.fetch(), + '-m', '1G', + '-smp', '4', + '-no-reboot', + '-netdev', + f'user,id=3Dnet0,tftp=3D{tftp_dir},bootfile=3Dbsd= .rd', + '-device', 'e1000,netdev=3Dnet0') + self.vm.launch() + + wait_for_console_pattern(self, '=3D> ') + ec_and_wait(self, 'pci enum', '=3D> ') + ec_and_wait(self, 'dhcp', 'Bytes transferred') + wait_for_console_pattern(self, '=3D> ') + ec_and_wait(self, 'bootoctlinux ${loadaddr} numcores=3D4', + 'Welcome to the OpenBSD/octeon 7.9 installation progra= m.') + + +if __name__ =3D=3D '__main__': + QemuSystemTest.main() --=20 2.54.0