summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2026-07-02 10:44:50 +0200
committerStefan Hajnoczi <stefanha@redhat.com>2026-07-02 10:44:50 +0200
commita59157f98f0b69b0bbdb26bc15fbc4d6c8060799 (patch)
tree103c55abdbcbf075a661eb2138fcf4540544b808
parent8241394f7e749401e1fb84d412eab40a0e7a23bc (diff)
parent64ce9ac18757d79f3b5b337f7bcbdd0dabef3ce1 (diff)
downloadqemu-a59157f98f0b69b0bbdb26bc15fbc4d6c8060799.tar.gz
qemu-a59157f98f0b69b0bbdb26bc15fbc4d6c8060799.zip
Merge tag 'pull-riscv-to-apply-20260701' of https://github.com/alistair23/qemu into staging
RISC-V PR for 11.1 * Fix IMSIC CSR write and add tests * Parametrise debug trigger number * Add 'svbare' satp-mode * Fix RINTC PLIC context ID for KVM * Avoid abort when reading vtype before env->xl is set * Skip reset for KVM irqchip * Skip FP/Vector sync on KVM_PUT_RUNTIME_STATE * More FDT cleanups (PLIC) * Make FCTL.BE in IOMMU read only 0 * Check DC.TC reserved bits in IOMMU * Apply UXL WARL handling to vsstatus * Set cmd_ill IOFENCE.C if rsvp bits are set in IOMMU * Set RISCV_IOMMU_FQ_HDR_PV appropriately * Fix MSI MRIF IOMMU interrupt-pending offset * Report QEMU CPU archid as 42 * Check PMP before updating PTE * Add the Tenstorrent Atlantis machine # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmpE6SoACgkQr3yVEwxT # gBOxNQ//bI4BvnT65Kd2UNMgtAwwPPcehpsyPzC2S3BcflniXQL+fV6sQ7IreKta # 6dclp/v5v+yhbB4bd/E1s/UPOF3YD4A9noUFifIhymBkafmqA4YRNsvPByeGiSD8 # xVkHhX5qUT9NW5wKnivEDjO8mndBMRm5YEXQ6uT5ulUsZr3Ir8wPOCJITZ8ZqKwb # 6dbbXStf1aTIBzu53KaNhNpi9DQqKV5UeV7CiSuhuwWU0qmVg1RAZMg9X3oB80rE # WpWqH0rg9Z0Cn+3XL+oKSzbLD5SrrTV+Ohq+K8zT2rEk+hIXOE3shAPm2xfTT9Q2 # g65nBOf2UmNWeHlvn3XC2LtmIWq10/A78ogGgm4XwHx8TXIeA2KIKboyS8T37XAb # NwUllq9LRtfDVtDevpiTn6t7Oa7TC8zrxDJTT1rg/p+3D6MdfkonifwJJgVAwfuG # NF7R2iePKPQliWr1hi6W+ghzQMRFXgNBwUNOL39/BQguy5IqvNmSk6ovhl8IFocf # aXGh9U35DqgrsUvMa/7Fgf4uI2QNhERBGJrHfL0SPZ82sKb5CTrMw9URwg0DFnEF # 8v/zQ9xL4eF0uZn0OtaNlLXRCblDxcHSgecwix9Vip5toFIc1P8ar9FX98Zd/H5l # UD/a3ENtiwb6hnKhZ+45iM/NIFJeUK7A0944VnQzx00tA06wJLw= # =a4hl # -----END PGP SIGNATURE----- # gpg: Signature made Wed 01 Jul 2026 12:17:14 CEST # gpg: using RSA key 6AE902B6A7CA877D6D659296AF7C95130C538013 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 6AE9 02B6 A7CA 877D 6D65 9296 AF7C 9513 0C53 8013 * tag 'pull-riscv-to-apply-20260701' of https://github.com/alistair23/qemu: (39 commits) hw/riscv/riscv-iommu.c: always fault with SADE=0 and A=0 hw/riscv/atlantis: Add some i2c peripherals hw/riscv/atlantis: Integrate i2c controllers hw/i2c: Add DesignWare I2C Controller tests/functional/riscv64: Add tt-atlantis tests hw/riscv/atlantis: Ensure OpenSBI has a non-zero next_addr hw/riscv: Add Tenstorrent Atlantis machine target/riscv: tt-ascalon: Enable Zkr extension hw/riscv/aia: Configure stride for the M-mode IMSIC hw/riscv/aia: Provide number of irq sources hw/riscv/virt: Move AIA initialisation to helper file hw/riscv/boot: Account for discontiguous memory when loading firmware hw/riscv/boot: Describe discontiguous memory in boot_info target/riscv: Check PMP before updating PTE target/riscv: Report QEMU CPU archid as 42 hw/riscv/riscv-iommu.c: fix MSI MRIF interrupt-pending offset hw/riscv/riscv-iommu.c: set RISCV_IOMMU_FQ_HDR_PV appropriately hw/riscv/riscv-iommu: set cmd_ill IOFENCE.C rsvp bits are set target/riscv: Apply UXL WARL handling to vsstatus hw/riscv/riscv-iommu: check DC.TC reserved bits ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r--MAINTAINERS21
-rw-r--r--docs/system/riscv/tt_atlantis.rst41
-rw-r--r--docs/system/target-riscv.rst1
-rw-r--r--hw/i2c/Kconfig5
-rw-r--r--hw/i2c/designware_i2c.c745
-rw-r--r--hw/i2c/meson.build1
-rw-r--r--hw/intc/riscv_aplic.c4
-rw-r--r--hw/intc/riscv_imsic.c4
-rw-r--r--hw/riscv/Kconfig13
-rw-r--r--hw/riscv/aia.c94
-rw-r--r--hw/riscv/aia.h26
-rw-r--r--hw/riscv/boot.c34
-rw-r--r--hw/riscv/fdt-common.c30
-rw-r--r--hw/riscv/k230.c8
-rw-r--r--hw/riscv/meson.build2
-rw-r--r--hw/riscv/microchip_pfsoc.c8
-rw-r--r--hw/riscv/opentitan.c6
-rw-r--r--hw/riscv/riscv-iommu-bits.h4
-rw-r--r--hw/riscv/riscv-iommu-hpm.c4
-rw-r--r--hw/riscv/riscv-iommu.c73
-rw-r--r--hw/riscv/riscv-iommu.h26
-rw-r--r--hw/riscv/shakti_c.c6
-rw-r--r--hw/riscv/sifive_u.c48
-rw-r--r--hw/riscv/spike.c6
-rw-r--r--hw/riscv/tt_atlantis.c617
-rw-r--r--hw/riscv/virt-acpi-build.c29
-rw-r--r--hw/riscv/virt.c148
-rw-r--r--hw/riscv/xiangshan_kmh.c6
-rw-r--r--include/hw/i2c/designware_i2c.h56
-rw-r--r--include/hw/riscv/boot.h12
-rw-r--r--include/hw/riscv/fdt-common.h5
-rw-r--r--include/hw/riscv/sifive_u.h2
-rw-r--r--include/hw/riscv/tt_atlantis.h64
-rw-r--r--include/hw/riscv/virt.h2
-rw-r--r--target/riscv/cpu.c43
-rw-r--r--target/riscv/cpu.h39
-rw-r--r--target/riscv/cpu_helper.c8
-rw-r--r--target/riscv/csr.c48
-rw-r--r--target/riscv/debug.c46
-rw-r--r--target/riscv/debug.h1
-rw-r--r--target/riscv/kvm/kvm-cpu.c19
-rw-r--r--target/riscv/machine.c16
-rw-r--r--tests/functional/riscv64/meson.build1
-rwxr-xr-xtests/functional/riscv64/test_opensbi.py4
-rwxr-xr-xtests/functional/riscv64/test_tt_atlantis.py57
-rwxr-xr-xtests/functional/riscv64/test_tuxrun.py11
46 files changed, 2196 insertions, 248 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 08e04b6cbf..f0695d3d2a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1790,6 +1790,19 @@ F: hw/*/*sifive*.c
F: include/hw/*/*sifive*.h
F: tests/functional/riscv64/test_sifive_u.py
+Tenstorrent Machines
+M: Joel Stanley <joel@jms.id.au>
+M: Nicholas Piggin <npiggin@gmail.com>
+R: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>
+R: Michael Ellerman <mpe@kernel.org>
+R: Portia Stephens <portias@oss.tenstorrent.com>
+L: qemu-riscv@nongnu.org
+S: Supported
+F: docs/system/riscv/tt_*.rst
+F: hw/riscv/tt_*.c
+F: include/hw/riscv/tt_*.h
+F: tests/functional/riscv64/test_tt_*.py
+
AMD Microblaze-V Generic Board
M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
S: Maintained
@@ -2762,6 +2775,14 @@ S: Orphan
F: hw/gpio/pcf8574.c
F: include/hw/gpio/pcf8574.h
+DesignWare I2C
+M: Chris Rauer <crauer@google.com>
+R: Alano Song <alanosong@163.com>
+R: Joel Stanley <joel@jms.id.au>
+S: Maintained
+F: hw/i2c/designware_i2c.c
+F: include/hw/i2c/designware_i2c.h
+
Generic Loader
M: Alistair Francis <alistair@alistair23.me>
S: Maintained
diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst
new file mode 100644
index 0000000000..1f2880d617
--- /dev/null
+++ b/docs/system/riscv/tt_atlantis.rst
@@ -0,0 +1,41 @@
+Tenstorrent Atlantis (``tt-atlantis``)
+======================================
+
+The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
+and CoreLab Technology. It is based on the Atlantis SoC, which includes
+the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
+
+The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
+RISC-V CPU.
+
+tt-atlantis QEMU model features
+-------------------------------
+
+* 8-core Ascalon-X CPU Cluster
+* RISC-V compliant Advanced Interrupt Architecture
+* 16550A compatible UART
+
+Known limitations
+-----------------
+
+The QEMU tt-atlantis machine does not yet model every device on the
+real platform. Notably:
+
+* There is no PCI host bridge, so virtio-pci devices cannot be
+ attached. Boots that need block storage must use ``-initrd`` with
+ an initramfs.
+* The DesignWare UART is modelled with QEMU's ns16550-compatible
+ ``serial_mm`` device; DesignWare-specific registers beyond that
+ set return 0.
+
+Supported software
+------------------
+
+The Tenstorrent Ascalon CPUs avoid proprietary or non-standard
+extensions, so compatibility with existing software is generally
+good. The QEMU tt-atlantis machine works with upstream OpenSBI
+and Linux with default configurations.
+
+The development board hardware will require some implementation
+specific setup in firmware which is being developed and may
+become a requirement or option for the tt-atlantis machine.
diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index 896f14e78b..2639866a3e 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -72,6 +72,7 @@ undocumented; you can get a complete list by running
riscv/mips
riscv/shakti-c
riscv/sifive_u
+ riscv/tt_atlantis
riscv/virt
riscv/xiangshan-kunminghu
diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
index 596a7a3165..0766130b59 100644
--- a/hw/i2c/Kconfig
+++ b/hw/i2c/Kconfig
@@ -18,6 +18,11 @@ config ARM_SBCON_I2C
bool
select BITBANG_I2C
+config DESIGNWARE_I2C
+ bool
+ select REGISTER
+ select I2C
+
config ACPI_SMBUS
bool
select SMBUS
diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
new file mode 100644
index 0000000000..daa9714cb7
--- /dev/null
+++ b/hw/i2c/designware_i2c.c
@@ -0,0 +1,745 @@
+/*
+ * DesignWare I2C Module.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/i2c/designware_i2c.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#ifndef DESIGNWARE_I2C_ERR_DEBUG
+#define DESIGNWARE_I2C_ERR_DEBUG 0
+#endif
+
+REG32(DW_IC_CON, 0x00) /* I2C control */
+ FIELD(DW_IC_CON, STOP_DET_IF_MASTER_ACTIV, 10, 1)
+ FIELD(DW_IC_CON, RX_FIFO_FULL_HLD_CTRL, 9, 1)
+ FIELD(DW_IC_CON, TX_EMPTY_CTRL, 8, 1)
+ FIELD(DW_IC_CON, STOP_IF_ADDRESSED, 7, 1)
+ FIELD(DW_IC_CON, SLAVE_DISABLE, 6, 1)
+ FIELD(DW_IC_CON, IC_RESTART_EN, 5, 1)
+ FIELD(DW_IC_CON, 10BITADDR_MASTER, 4, 1)
+ FIELD(DW_IC_CON, 10BITADDR_SLAVE, 3, 1)
+ FIELD(DW_IC_CON, SPEED, 1, 2)
+ FIELD(DW_IC_CON, MASTER_MODE, 0, 1)
+REG32(DW_IC_TAR, 0x04) /* I2C target address */
+ FIELD(DW_IC_TAR, IC_10BITADDR_MASTER, 12, 1)
+ FIELD(DW_IC_TAR, SPECIAL, 11, 1)
+ FIELD(DW_IC_TAR, GC_OR_START, 10, 1)
+ FIELD(DW_IC_TAR, ADDRESS, 0, 10)
+REG32(DW_IC_SAR, 0x08) /* I2C slave address */
+REG32(DW_IC_DATA_CMD, 0x10)
+ FIELD(DW_IC_DATA_CMD, RESTART, 10, 1)
+ FIELD(DW_IC_DATA_CMD, STOP, 9, 1)
+ FIELD(DW_IC_DATA_CMD, CMD, 8, 1)
+ FIELD(DW_IC_DATA_CMD, DAT, 0, 8)
+REG32(DW_IC_SS_SCL_HCNT, 0x14) /* Standard speed i2c clock scl high count */
+REG32(DW_IC_SS_SCL_LCNT, 0x18) /* Standard speed i2c clock scl low count */
+REG32(DW_IC_FS_SCL_HCNT, 0x1c) /* Fast or fast plus i2c clock scl high count */
+REG32(DW_IC_FS_SCL_LCNT, 0x20) /* Fast or fast plus i2c clock scl low count */
+REG32(DW_IC_INTR_STAT, 0x2c)
+REG32(DW_IC_INTR_MASK, 0x30) /* I2C Interrupt Mask */
+REG32(DW_IC_RAW_INTR_STAT, 0x34) /* I2C raw interrupt status */
+ /* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */
+ SHARED_FIELD(DW_IC_INTR_RESTART_DET, 12, 1)
+ SHARED_FIELD(DW_IC_INTR_GEN_CALL, 11, 1)
+ SHARED_FIELD(DW_IC_INTR_START_DET, 10, 1)
+ SHARED_FIELD(DW_IC_INTR_STOP_DET, 9, 1)
+ SHARED_FIELD(DW_IC_INTR_ACTIVITY, 8, 1)
+ SHARED_FIELD(DW_IC_INTR_RX_DONE, 7, 1)
+ SHARED_FIELD(DW_IC_INTR_TX_ABRT, 6, 1)
+ SHARED_FIELD(DW_IC_INTR_RD_REQ, 5, 1)
+ SHARED_FIELD(DW_IC_INTR_TX_EMPTY, 4, 1) /* Hardware clear only. */
+ SHARED_FIELD(DW_IC_INTR_TX_OVER, 3, 1)
+ SHARED_FIELD(DW_IC_INTR_RX_FULL, 2, 1) /* Hardware clear only. */
+ SHARED_FIELD(DW_IC_INTR_RX_OVER, 1, 1)
+ SHARED_FIELD(DW_IC_INTR_RX_UNDER, 0, 1)
+
+#define DW_IC_INTR_ANY_MASK \
+ (DW_IC_INTR_RESTART_DET_MASK | \
+ DW_IC_INTR_GEN_CALL_MASK | \
+ DW_IC_INTR_START_DET_MASK | \
+ DW_IC_INTR_STOP_DET_MASK | \
+ DW_IC_INTR_ACTIVITY_MASK | \
+ DW_IC_INTR_RX_DONE_MASK | \
+ DW_IC_INTR_TX_ABRT_MASK | \
+ DW_IC_INTR_RD_REQ_MASK | \
+ DW_IC_INTR_TX_EMPTY_MASK | \
+ DW_IC_INTR_TX_OVER_MASK | \
+ DW_IC_INTR_RX_FULL_MASK | \
+ DW_IC_INTR_RX_OVER_MASK | \
+ DW_IC_INTR_RX_UNDER_MASK)
+
+#define DW_IC_INTR_ANY_SW_CLEAR_MASK \
+ (DW_IC_INTR_ANY_MASK & \
+ ~(DW_IC_INTR_TX_EMPTY_MASK | \
+ DW_IC_INTR_RX_FULL_MASK))
+
+REG32(DW_IC_RX_TL, 0x38) /* I2C receive FIFO threshold */
+REG32(DW_IC_TX_TL, 0x3c) /* I2C transmit FIFO threshold */
+REG32(DW_IC_CLR_INTR, 0x40)
+REG32(DW_IC_CLR_RX_UNDER, 0x44)
+REG32(DW_IC_CLR_RX_OVER, 0x48)
+REG32(DW_IC_CLR_TX_OVER, 0x4c)
+REG32(DW_IC_CLR_RD_REQ, 0x50)
+REG32(DW_IC_CLR_TX_ABRT, 0x54)
+REG32(DW_IC_CLR_RX_DONE, 0x58)
+REG32(DW_IC_CLR_ACTIVITY, 0x5c)
+REG32(DW_IC_CLR_STOP_DET, 0x60)
+REG32(DW_IC_CLR_START_DET, 0x64)
+REG32(DW_IC_CLR_GEN_CALL, 0x68)
+REG32(DW_IC_ENABLE, 0x6c) /* I2C enable */
+ FIELD(DW_IC_ENABLE, TX_CMD_BLOCK, 2, 1)
+ FIELD(DW_IC_ENABLE, ABORT, 1, 1)
+ FIELD(DW_IC_ENABLE, ENABLE, 0, 1)
+REG32(DW_IC_STATUS, 0x70) /* I2C status */
+ FIELD(DW_IC_STATUS, SLV_ACTIVITY, 6, 1)
+ FIELD(DW_IC_STATUS, MST_ACTIVITY, 5, 1)
+ FIELD(DW_IC_STATUS, RFF, 4, 1)
+ FIELD(DW_IC_STATUS, RFNE, 3, 1)
+ FIELD(DW_IC_STATUS, TFE, 2, 1)
+ FIELD(DW_IC_STATUS, TFNF, 1, 1)
+ FIELD(DW_IC_STATUS, ACTIVITY, 0, 1)
+REG32(DW_IC_TXFLR, 0x74) /* I2C transmit fifo level */
+REG32(DW_IC_RXFLR, 0x78) /* I2C receive fifo level */
+REG32(DW_IC_SDA_HOLD, 0x7c) /* I2C SDA hold time length */
+REG32(DW_IC_TX_ABRT_SOURCE, 0x80) /* The I2C transmit abort source */
+ FIELD(DW_IC_TX_ABRT_SOURCE, USER_ABRT, 16, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, SLVRD_INTX, 15, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, SLV_ARBLOST, 14, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, SLVFLUSH_TXFIFO, 13, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, ARB_LOST, 12, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, MASTER_DIS, 11, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, 10B_RD_NORSTRT, 10, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, SBYTE_NORSTRT, 9, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, HS_NORSTRT, 8, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, SBYTE_ACKDET, 7, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, HS_ACKDET, 6, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, GCALL_READ, 5, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, GCALL_NOACK, 4, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, TXDATA_NOACK, 3, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, 10ADDR2_NOACK, 2, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, 10ADDR1_NOACK, 1, 1)
+ FIELD(DW_IC_TX_ABRT_SOURCE, 7B_ADDR_NOACK, 0, 1)
+REG32(DW_IC_SLV_DATA_NACK_ONLY, 0x84)
+REG32(DW_IC_DMA_CR, 0x88)
+REG32(DW_IC_DMA_TDLR, 0x8c)
+REG32(DW_IC_DMA_RDLR, 0x90)
+REG32(DW_IC_SDA_SETUP, 0x94) /* I2C SDA setup */
+REG32(DW_IC_ACK_GENERAL_CALL, 0x98)
+REG32(DW_IC_ENABLE_STATUS, 0x9c) /* I2C enable status */
+ FIELD(DW_IC_ENABLE_STATUS, SLV_RX_DATA_LOST, 2, 1)
+ FIELD(DW_IC_ENABLE_STATUS, SLV_DISABLED_WHILE_BUSY, 1, 1)
+ FIELD(DW_IC_ENABLE_STATUS, IC_EN, 0, 1)
+REG32(DW_IC_FS_SPKLEN, 0xa0) /* I2C SS, FS or FM+ spike suppression limit */
+REG32(DW_IC_CLR_RESTART_DET, 0xa8)
+REG32(DW_IC_SMBUS_INTR_MASK, 0xcc) /* SMBus Interrupt Mask */
+REG32(DW_IC_COMP_PARAM_1, 0xf4) /* Component parameter */
+ FIELD(DW_IC_COMP_PARAM_1, TX_FIFO_SIZE, 16, 8)
+ FIELD(DW_IC_COMP_PARAM_1, RX_FIFO_SIZE, 8, 8)
+ FIELD(DW_IC_COMP_PARAM_1, HAS_ENCODED_PARAMS, 7, 1)
+ FIELD(DW_IC_COMP_PARAM_1, HAS_DMA, 6, 1)
+ FIELD(DW_IC_COMP_PARAM_1, INTR_IO, 5, 1)
+ FIELD(DW_IC_COMP_PARAM_1, HC_COUNT_VAL, 4, 1)
+ FIELD(DW_IC_COMP_PARAM_1, HIGH_SPEED_MODE, 2, 2)
+ FIELD(DW_IC_COMP_PARAM_1, APB_DATA_WIDTH_32, 0, 2)
+REG32(DW_IC_COMP_VERSION, 0xf8) /* I2C component version */
+REG32(DW_IC_COMP_TYPE, 0xfc) /* I2C component type */
+
+static void dw_i2c_update_irq(DesignWareI2CState *s)
+{
+ uint32_t intr = s->regs[R_DW_IC_RAW_INTR_STAT] & s->regs[R_DW_IC_INTR_MASK];
+
+ qemu_set_irq(s->irq, !!(intr & DW_IC_INTR_ANY_MASK));
+}
+
+static uint64_t dw_ic_data_cmd_reg_post_read(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ g_assert(value == 0);
+
+ if (s->status != DW_I2C_STATUS_RECEIVING) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Attempted to read from RX fifo when not in receive "
+ "state.\n", DEVICE(s)->canonical_path);
+ if (s->status != DW_I2C_STATUS_IDLE) {
+ SHARED_ARRAY_FIELD_DP32(s->regs, R_DW_IC_RAW_INTR_STAT,
+ DW_IC_INTR_RX_UNDER, 1);
+ dw_i2c_update_irq(s);
+ }
+ return 0;
+ }
+
+ g_assert(s->regs[R_DW_IC_RXFLR] == fifo8_num_used(&s->rx_fifo));
+
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ SHARED_ARRAY_FIELD_DP32(s->regs, R_DW_IC_RAW_INTR_STAT, DW_IC_INTR_RX_UNDER, 1);
+ dw_i2c_update_irq(s);
+ return 0;
+ }
+
+ s->regs[R_DW_IC_RXFLR]--;
+ if (s->regs[R_DW_IC_RXFLR] <= s->regs[R_DW_IC_RX_TL]) {
+ SHARED_ARRAY_FIELD_DP32(s->regs, R_DW_IC_RAW_INTR_STAT, DW_IC_INTR_RX_FULL, 0);
+ dw_i2c_update_irq(s);
+ }
+
+ return fifo8_pop(&s->rx_fifo);
+}
+
+static uint64_t dw_ic_clr_intr_reg_post_read(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ g_assert(value == 0);
+
+ switch (reg->access->addr) {
+ case A_DW_IC_CLR_INTR:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_ANY_SW_CLEAR_MASK;
+ break;
+ case A_DW_IC_CLR_RX_UNDER:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_UNDER_MASK;
+ break;
+ case A_DW_IC_CLR_RX_OVER:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_OVER_MASK;
+ break;
+ case A_DW_IC_CLR_TX_OVER:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_OVER_MASK;
+ break;
+ case A_DW_IC_CLR_RD_REQ:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RD_REQ_MASK;
+ break;
+ case A_DW_IC_CLR_TX_ABRT:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_ABRT_MASK;
+ break;
+ case A_DW_IC_CLR_RX_DONE:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_DONE_MASK;
+ break;
+ case A_DW_IC_CLR_ACTIVITY:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_ACTIVITY_MASK;
+ break;
+ case A_DW_IC_CLR_STOP_DET:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_STOP_DET_MASK;
+ break;
+ case A_DW_IC_CLR_START_DET:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_START_DET_MASK;
+ break;
+ case A_DW_IC_CLR_GEN_CALL:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_GEN_CALL_MASK;
+ break;
+ case A_DW_IC_CLR_RESTART_DET:
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RESTART_DET_MASK;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ dw_i2c_update_irq(s);
+
+ return 0;
+}
+
+static uint64_t dw_ic_intr_stat_reg_post_read(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ g_assert(value == 0);
+
+ return s->regs[R_DW_IC_RAW_INTR_STAT] & s->regs[R_DW_IC_INTR_MASK];
+}
+
+static uint64_t dw_ic_unsupported_reg_post_read(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - %s\n",
+ DEVICE(s)->canonical_path, reg->access->name);
+
+ return 0;
+}
+
+static uint64_t dw_ic_unsupported_reg_pre_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - %s\n",
+ DEVICE(s)->canonical_path, reg->access->name);
+
+ return 0;
+}
+
+static uint64_t dw_ic_con_reg_pre_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ if (s->regs[R_DW_IC_ENABLE] & R_DW_IC_ENABLE_ENABLE_MASK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid setting to ic_con %d when ic_enable[0]==1\n",
+ DEVICE(s)->canonical_path, (int)value);
+ return s->regs[R_DW_IC_CON]; /* keep old value */
+ }
+
+ return value;
+}
+
+static void dw_i2c_reset_to_idle(DesignWareI2CState *s)
+{
+ s->regs[R_DW_IC_ENABLE_STATUS] &= ~R_DW_IC_ENABLE_STATUS_IC_EN_MASK;
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_EMPTY_MASK;
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_FULL_MASK;
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_UNDER_MASK;
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_OVER_MASK;
+ s->regs[R_DW_IC_RXFLR] = 0;
+ fifo8_reset(&s->rx_fifo);
+ s->regs[R_DW_IC_STATUS] &= ~R_DW_IC_STATUS_ACTIVITY_MASK;
+ s->status = DW_I2C_STATUS_IDLE;
+ dw_i2c_update_irq(s);
+}
+
+static void dw_ic_tx_abort(DesignWareI2CState *s, uint32_t src)
+{
+ s->regs[R_DW_IC_TX_ABRT_SOURCE] |= src;
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_TX_ABRT_MASK;
+ dw_i2c_reset_to_idle(s);
+ dw_i2c_update_irq(s);
+}
+
+static void dw_ic_data_cmd_reg_post_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+ int recv = !!(value & R_DW_IC_DATA_CMD_CMD_MASK);
+
+ s->regs[R_DW_IC_DATA_CMD] = 0; /* Register has no storage */
+
+ if (!(s->regs[R_DW_IC_ENABLE] & R_DW_IC_ENABLE_ENABLE_MASK)) {
+ /*
+ * Controller is not enabled. The register_reset() path also lands
+ * here with value == 0, so silently ignore rather than reporting
+ * a spurious guest error.
+ */
+ return;
+ }
+
+ if (s->status == DW_I2C_STATUS_IDLE ||
+ s->regs[R_DW_IC_RAW_INTR_STAT] & DW_IC_INTR_TX_ABRT_MASK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Attempted to write to TX fifo when it is held in "
+ "reset\n", DEVICE(s)->canonical_path);
+ return;
+ }
+
+ /* Send the address if it hasn't been sent yet. */
+ if (s->status == DW_I2C_STATUS_SENDING_ADDRESS) {
+ int rv = i2c_start_transfer(s->bus,
+ ARRAY_FIELD_EX32(s->regs, DW_IC_TAR, ADDRESS), recv);
+ if (rv) {
+ dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_7B_ADDR_NOACK_MASK);
+ return;
+ }
+ s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING;
+ }
+
+ /* Send data */
+ if (!recv) {
+ int rv = i2c_send(s->bus, FIELD_EX32(value, DW_IC_DATA_CMD, DAT));
+ if (rv) {
+ i2c_end_transfer(s->bus);
+ dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_TXDATA_NOACK_MASK);
+ return;
+ }
+ dw_i2c_update_irq(s);
+ }
+
+ /* Restart command */
+ if (value & R_DW_IC_DATA_CMD_RESTART_MASK &&
+ s->regs[R_DW_IC_CON] & R_DW_IC_CON_IC_RESTART_EN_MASK) {
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RESTART_DET_MASK |
+ DW_IC_INTR_START_DET_MASK |
+ DW_IC_INTR_ACTIVITY_MASK;
+ s->regs[R_DW_IC_STATUS] |= R_DW_IC_STATUS_ACTIVITY_MASK;
+ dw_i2c_update_irq(s);
+
+ if (i2c_start_transfer(s->bus,
+ ARRAY_FIELD_EX32(s->regs, DW_IC_TAR, ADDRESS), recv)) {
+ dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_7B_ADDR_NOACK_MASK);
+ return;
+ }
+
+ s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING;
+ }
+
+ /* Receive data */
+ if (recv) {
+ g_assert(s->regs[R_DW_IC_RXFLR] == fifo8_num_used(&s->rx_fifo));
+
+ if (!fifo8_is_full(&s->rx_fifo)) {
+ fifo8_push(&s->rx_fifo, i2c_recv(s->bus));
+ s->regs[R_DW_IC_RXFLR]++;
+ } else {
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RX_OVER_MASK;
+ dw_i2c_update_irq(s);
+ }
+
+ if (s->regs[R_DW_IC_RXFLR] > s->regs[R_DW_IC_RX_TL]) {
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RX_FULL_MASK;
+ dw_i2c_update_irq(s);
+ }
+ if (value & R_DW_IC_DATA_CMD_STOP_MASK) {
+ i2c_nack(s->bus);
+ }
+ }
+
+ /* Stop command */
+ if (value & R_DW_IC_DATA_CMD_STOP_MASK) {
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_STOP_DET_MASK;
+ s->regs[R_DW_IC_STATUS] &= ~R_DW_IC_STATUS_ACTIVITY_MASK;
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_TX_EMPTY_MASK;
+ i2c_end_transfer(s->bus);
+ dw_i2c_update_irq(s);
+ }
+}
+
+static void dw_ic_intr_mask_reg_post_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ dw_i2c_update_irq(s);
+}
+
+static uint64_t dw_ic_enable_reg_pre_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ if (value & R_DW_IC_ENABLE_ENABLE_MASK &&
+ !(s->regs[R_DW_IC_CON] & R_DW_IC_CON_SLAVE_DISABLE_MASK)) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Designware I2C slave mode is not supported.\n",
+ DEVICE(s)->canonical_path);
+ return s->regs[R_DW_IC_ENABLE]; /* keep old value */
+ }
+
+ return value;
+}
+
+static void dw_ic_enable_reg_post_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ s->regs[R_DW_IC_ENABLE] = value & R_DW_IC_ENABLE_ENABLE_MASK;
+
+ if (value & R_DW_IC_ENABLE_ABORT_MASK || value & R_DW_IC_ENABLE_TX_CMD_BLOCK_MASK) {
+ dw_ic_tx_abort(s, R_DW_IC_TX_ABRT_SOURCE_USER_ABRT_MASK);
+ return;
+ }
+
+ if (value & R_DW_IC_ENABLE_ENABLE_MASK) {
+ s->regs[R_DW_IC_ENABLE_STATUS] |= R_DW_IC_ENABLE_STATUS_IC_EN_MASK;
+ s->regs[R_DW_IC_STATUS] |= R_DW_IC_STATUS_ACTIVITY_MASK;
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_ACTIVITY_MASK |
+ DW_IC_INTR_START_DET_MASK |
+ DW_IC_INTR_TX_EMPTY_MASK;
+ s->status = DW_I2C_STATUS_SENDING_ADDRESS;
+ dw_i2c_update_irq(s);
+ } else if ((value & R_DW_IC_ENABLE_ENABLE_MASK) == 0) {
+ dw_i2c_reset_to_idle(s);
+ }
+}
+
+static uint64_t dw_ic_rx_tl_reg_pre_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ /* Note that a value of 0 for ic_rx_tl indicates a threshold of 1. */
+ if (value > DESIGNWARE_I2C_RX_FIFO_SIZE - 1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid setting to ic_rx_tl %d\n",
+ DEVICE(s)->canonical_path, (int)value);
+ return DESIGNWARE_I2C_RX_FIFO_SIZE - 1;
+ }
+
+ return value;
+}
+
+static void dw_ic_rx_tl_reg_post_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ if (s->regs[R_DW_IC_RXFLR] > s->regs[R_DW_IC_RX_TL] &&
+ s->regs[R_DW_IC_ENABLE] & R_DW_IC_ENABLE_ENABLE_MASK) {
+ s->regs[R_DW_IC_RAW_INTR_STAT] |= DW_IC_INTR_RX_FULL_MASK;
+ } else {
+ s->regs[R_DW_IC_RAW_INTR_STAT] &= ~DW_IC_INTR_RX_FULL_MASK;
+ }
+ dw_i2c_update_irq(s);
+}
+
+static uint64_t dw_ic_tx_tl_reg_pre_write(RegisterInfo *reg, uint64_t value)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(reg->opaque);
+
+ /*
+ * Note that a value of 0 for ic_tx_tl indicates a threshold of 1.
+ * However, the tx threshold is not used in the model because commands are
+ * always sent out as soon as they are written.
+ */
+ if (value > DESIGNWARE_I2C_TX_FIFO_SIZE - 1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid setting to ic_tx_tl %d\n",
+ DEVICE(s)->canonical_path, (int)value);
+ return DESIGNWARE_I2C_TX_FIFO_SIZE - 1;
+ }
+
+ return value;
+}
+
+static const RegisterAccessInfo designware_i2c_regs_info[] = {
+ { .name = "DW_IC_CON", .addr = A_DW_IC_CON,
+ .reset = 0x7d,
+ .unimp = 0xfffffc00,
+ .pre_write = dw_ic_con_reg_pre_write,
+ },{ .name = "DW_IC_TAR", .addr = A_DW_IC_TAR,
+ .reset = 0x1055,
+ .unimp = 0xfffff000,
+ },{ .name = "DW_IC_SAR", .addr = A_DW_IC_SAR,
+ .reset = 0x55,
+ .unimp = 0xfffffc00,
+ .post_read = dw_ic_unsupported_reg_post_read,
+ .pre_write = dw_ic_unsupported_reg_pre_write,
+ },{ .name = "DW_IC_DATA_CMD", .addr = A_DW_IC_DATA_CMD,
+ .post_read = dw_ic_data_cmd_reg_post_read,
+ .post_write = dw_ic_data_cmd_reg_post_write,
+ },{ .name = "DW_IC_SS_SCL_HCNT", .addr = A_DW_IC_SS_SCL_HCNT,
+ .reset = 0x190,
+ .unimp = 0xffff0000,
+ },{ .name = "DW_IC_SS_SCL_LCNT", .addr = A_DW_IC_SS_SCL_LCNT,
+ .reset = 0x1d6,
+ .unimp = 0xffff0000,
+ },{ .name = "DW_IC_FS_SCL_HCNT", .addr = A_DW_IC_FS_SCL_HCNT,
+ .reset = 0x3c,
+ .unimp = 0xffff0000,
+ },{ .name = "DW_IC_FS_SCL_LCNT", .addr = A_DW_IC_FS_SCL_LCNT,
+ .reset = 0x82,
+ .unimp = 0xffff0000,
+ },{ .name = "DW_IC_INTR_STAT", .addr = A_DW_IC_INTR_STAT,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_intr_stat_reg_post_read,
+ },{ .name = "DW_IC_INTR_MASK", .addr = A_DW_IC_INTR_MASK,
+ .reset = 0x8ff,
+ .unimp = 0xffff8000,
+ .post_write = dw_ic_intr_mask_reg_post_write,
+ },{ .name = "DW_IC_RAW_INTR_STAT", .addr = A_DW_IC_RAW_INTR_STAT,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_RX_TL", .addr = A_DW_IC_RX_TL,
+ .pre_write = dw_ic_rx_tl_reg_pre_write,
+ .post_write = dw_ic_rx_tl_reg_post_write,
+ },{ .name = "DW_IC_TX_TL", .addr = A_DW_IC_TX_TL,
+ .pre_write = dw_ic_tx_tl_reg_pre_write,
+ },{ .name = "DW_IC_CLR_INTR", .addr = A_DW_IC_CLR_INTR,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_RX_UNDER", .addr = A_DW_IC_CLR_RX_UNDER,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_RX_OVER", .addr = A_DW_IC_CLR_RX_OVER,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_TX_OVER", .addr = A_DW_IC_CLR_TX_OVER,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_RD_REQ", .addr = A_DW_IC_CLR_RD_REQ,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_TX_ABRT", .addr = A_DW_IC_CLR_TX_ABRT,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_RX_DONE", .addr = A_DW_IC_CLR_RX_DONE,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_ACTIVITY", .addr = A_DW_IC_CLR_ACTIVITY,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_STOP_DET", .addr = A_DW_IC_CLR_STOP_DET,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_START_DET", .addr = A_DW_IC_CLR_START_DET,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_CLR_GEN_CALL", .addr = A_DW_IC_CLR_GEN_CALL,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_ENABLE", .addr = A_DW_IC_ENABLE,
+ .unimp = 0xfffffff8,
+ .pre_write = dw_ic_enable_reg_pre_write,
+ .post_write = dw_ic_enable_reg_post_write,
+ },{ .name = "DW_IC_STATUS", .addr = A_DW_IC_STATUS,
+ .reset = 0x6,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_TXFLR", .addr = A_DW_IC_TXFLR,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_RXFLR", .addr = A_DW_IC_RXFLR,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_SDA_HOLD", .addr = A_DW_IC_SDA_HOLD,
+ .reset = 0x1,
+ .unimp = 0xff000000,
+ },{ .name = "DW_IC_TX_ABRT_SOURCE", .addr = A_DW_IC_TX_ABRT_SOURCE,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_SLV_DATA_NACK_ONLY", .addr = A_DW_IC_SLV_DATA_NACK_ONLY,
+ .post_read = dw_ic_unsupported_reg_post_read,
+ .pre_write = dw_ic_unsupported_reg_pre_write,
+ },{ .name = "DW_IC_DMA_CR", .addr = A_DW_IC_DMA_CR,
+ .post_read = dw_ic_unsupported_reg_post_read,
+ .pre_write = dw_ic_unsupported_reg_pre_write,
+ },{ .name = "DW_IC_DMA_TDLR", .addr = A_DW_IC_DMA_TDLR,
+ .post_read = dw_ic_unsupported_reg_post_read,
+ .pre_write = dw_ic_unsupported_reg_pre_write,
+ },{ .name = "DW_IC_DMA_RDLR", .addr = A_DW_IC_DMA_RDLR,
+ .post_read = dw_ic_unsupported_reg_post_read,
+ .pre_write = dw_ic_unsupported_reg_pre_write,
+ },{ .name = "DW_IC_SDA_SETUP", .addr = A_DW_IC_SDA_SETUP,
+ .reset = 0x64,
+ .unimp = 0xffffff00,
+ },{ .name = "DW_IC_ACK_GENERAL_CALL", .addr = A_DW_IC_ACK_GENERAL_CALL,
+ .post_read = dw_ic_unsupported_reg_post_read,
+ .pre_write = dw_ic_unsupported_reg_pre_write,
+ },{ .name = "DW_IC_ENABLE_STATUS", .addr = A_DW_IC_ENABLE_STATUS,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_FS_SPKLEN", .addr = A_DW_IC_FS_SPKLEN,
+ .reset = 0x2,
+ .ro = 0xffffff00,
+ },{ .name = "DW_IC_CLR_RESTART_DET", .addr = A_DW_IC_CLR_RESTART_DET,
+ .ro = 0xffffffff,
+ .post_read = dw_ic_clr_intr_reg_post_read,
+ },{ .name = "DW_IC_SMBUS_INTR_MASK", .addr = A_DW_IC_SMBUS_INTR_MASK,
+ /* No SMBus interrupts are implemented, Linux updates the mask */
+ .reset = 0x7ff,
+ .unimp = 0xfffff800,
+ },{ .name = "DW_IC_COMP_PARAM_1", .addr = A_DW_IC_COMP_PARAM_1,
+ .reset = /* HAS_DMA and HC_COUNT_VAL are disabled */
+ ((2 << R_DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32_SHIFT) |
+ R_DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE_MASK |
+ R_DW_IC_COMP_PARAM_1_INTR_IO_MASK |
+ R_DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS_MASK |
+ ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1)
+ << R_DW_IC_COMP_PARAM_1_RX_FIFO_SIZE_SHIFT) |
+ ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1)
+ << R_DW_IC_COMP_PARAM_1_TX_FIFO_SIZE_SHIFT)),
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_COMP_VERSION", .addr = A_DW_IC_COMP_VERSION,
+ .reset = 0x3132302a,
+ .ro = 0xffffffff,
+ },{ .name = "DW_IC_COMP_TYPE", .addr = A_DW_IC_COMP_TYPE,
+ .reset = 0x44570140,
+ .ro = 0xffffffff,
+ }
+};
+
+static const MemoryRegionOps designware_i2c_ops = {
+ .read = register_read_memory,
+ .write = register_write_memory,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void designware_i2c_enter_reset(Object *obj, ResetType type)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->regs); ++i) {
+ register_reset(&s->regs_info[i]);
+ }
+
+ fifo8_reset(&s->rx_fifo);
+
+ s->status = DW_I2C_STATUS_IDLE;
+}
+
+static void designware_i2c_hold_reset(Object *obj, ResetType type)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+
+ qemu_irq_lower(s->irq);
+}
+
+static const VMStateDescription vmstate_designware_i2c = {
+ .name = TYPE_DESIGNWARE_I2C,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, DesignWareI2CState, DESIGNWARE_I2C_R_MAX),
+ VMSTATE_FIFO8(rx_fifo, DesignWareI2CState),
+ VMSTATE_UINT32(status, DesignWareI2CState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void designware_i2c_instance_init(Object *obj)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ RegisterInfoArray *reg_array;
+
+ fifo8_create(&s->rx_fifo, DESIGNWARE_I2C_RX_FIFO_SIZE);
+
+ s->bus = i2c_init_bus(DEVICE(s), "i2c-bus");
+
+ memory_region_init(&s->iomem, obj, TYPE_DESIGNWARE_I2C, 4 * KiB);
+ reg_array = register_init_block32(DEVICE(obj), designware_i2c_regs_info,
+ ARRAY_SIZE(designware_i2c_regs_info),
+ s->regs_info, s->regs,
+ &designware_i2c_ops,
+ DESIGNWARE_I2C_ERR_DEBUG,
+ DESIGNWARE_I2C_R_MAX * 4);
+ memory_region_add_subregion(&s->iomem, 0, &reg_array->mem);
+
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void designware_i2c_finalize(Object *obj)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+
+ fifo8_destroy(&s->rx_fifo);
+}
+
+static void designware_i2c_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "Designware I2C";
+ dc->vmsd = &vmstate_designware_i2c;
+ rc->phases.enter = designware_i2c_enter_reset;
+ rc->phases.hold = designware_i2c_hold_reset;
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo designware_i2c_types[] = {
+ {
+ .name = TYPE_DESIGNWARE_I2C,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(DesignWareI2CState),
+ .class_init = designware_i2c_class_init,
+ .instance_init = designware_i2c_instance_init,
+ .instance_finalize = designware_i2c_finalize,
+ },
+};
+DEFINE_TYPES(designware_i2c_types);
diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
index c459adcb59..88aea35662 100644
--- a/hw/i2c/meson.build
+++ b/hw/i2c/meson.build
@@ -11,6 +11,7 @@ i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c'))
i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c'))
i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c'))
i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c'))
+i2c_ss.add(when: 'CONFIG_DESIGNWARE_I2C', if_true: files('designware_i2c.c'))
i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c'))
i2c_ss.add(when: 'CONFIG_ARM_SBCON_I2C', if_true: files('arm_sbcon_i2c.c'))
i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c
index c2c67c29e6..84606e9f3d 100644
--- a/hw/intc/riscv_aplic.c
+++ b/hw/intc/riscv_aplic.c
@@ -910,6 +910,10 @@ static void riscv_aplic_reset_enter(Object *obj, ResetType type)
RISCVAPLICState *aplic = RISCV_APLIC(obj);
int i;
+ if (!riscv_use_emulated_aplic(aplic->msimode)) {
+ return;
+ }
+
aplic->domaincfg = 0;
memset(aplic->sourcecfg, 0, sizeof(uint32_t) * aplic->num_irqs);
memset(aplic->target, 0, sizeof(uint32_t) * aplic->num_irqs);
diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c
index 1c86ecdb2c..7e5b5349ba 100644
--- a/hw/intc/riscv_imsic.c
+++ b/hw/intc/riscv_imsic.c
@@ -347,6 +347,10 @@ static void riscv_imsic_reset_enter(Object *obj, ResetType type)
RISCVIMSICState *imsic = RISCV_IMSIC(obj);
int i;
+ if (kvm_irqchip_in_kernel()) {
+ return;
+ }
+
memset(imsic->eidelivery, 0, sizeof(uint32_t) * imsic->num_pages);
memset(imsic->eithreshold, 0, sizeof(uint32_t) * imsic->num_pages);
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 54e41a6afc..de37c08cae 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -120,6 +120,19 @@ config SPIKE
select RISCV_ACLINT
select SIFIVE_PLIC
+config TENSTORRENT
+ bool
+ default y
+ depends on RISCV64
+ select RISCV_ACLINT
+ select RISCV_APLIC
+ select RISCV_IMSIC
+ select SERIAL_MM
+ select DEVICE_TREE
+ select DESIGNWARE_I2C
+ select DS1338
+ select TMP105
+
config XIANGSHAN_KUNMINGHU
bool
default y
diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c
new file mode 100644
index 0000000000..ed89160029
--- /dev/null
+++ b/hw/riscv/aia.c
@@ -0,0 +1,94 @@
+/*
+ * QEMU RISC-V Advanced Interrupt Architecture (AIA)
+ *
+ * Copyright (C) 2019 Western Digital Corporation or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/kvm.h"
+#include "hw/intc/riscv_aplic.h"
+#include "hw/intc/riscv_imsic.h"
+
+#include "aia.h"
+
+uint32_t imsic_num_bits(uint32_t count)
+{
+ uint32_t ret = 0;
+
+ while (BIT(ret) < count) {
+ ret++;
+ }
+
+ return ret;
+}
+
+DeviceState *riscv_create_aia(bool msimode, int aia_guests,
+ uint32_t m_imsic_stride,
+ uint16_t num_sources,
+ const MemMapEntry *aplic_m,
+ const MemMapEntry *aplic_s,
+ const MemMapEntry *imsic_m,
+ const MemMapEntry *imsic_s,
+ int socket, int base_hartid, int hart_count,
+ uint32_t num_msis, uint32_t num_prio_bits)
+{
+ int i;
+ hwaddr addr = 0;
+ uint32_t guest_bits;
+ DeviceState *aplic_s_dev = NULL;
+ DeviceState *aplic_m_dev = NULL;
+
+ /* The RISC-V Advanced Interrupt Architecture, Chapter 1.2. Limits */
+ g_assert(num_sources <= 1023);
+
+ if (msimode) {
+ if (!kvm_enabled()) {
+ /* Per-socket M-level IMSICs */
+ addr = imsic_m->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT);
+ for (i = 0; i < hart_count; i++) {
+ riscv_imsic_create(addr + i * m_imsic_stride,
+ base_hartid + i, true, 1,
+ num_msis);
+ }
+ }
+
+ /* Per-socket S-level IMSICs */
+ guest_bits = imsic_num_bits(aia_guests + 1);
+ addr = imsic_s->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT);
+ for (i = 0; i < hart_count; i++) {
+ riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits),
+ base_hartid + i, false, 1 + aia_guests,
+ num_msis);
+ }
+ }
+
+ if (!kvm_enabled()) {
+ /* Per-socket M-level APLIC */
+ aplic_m_dev = riscv_aplic_create(aplic_m->base +
+ socket * aplic_m->size,
+ aplic_m->size,
+ (msimode) ? 0 : base_hartid,
+ (msimode) ? 0 : hart_count,
+ num_sources,
+ num_prio_bits,
+ msimode, true, NULL);
+ }
+
+ /* Per-socket S-level APLIC */
+ aplic_s_dev = riscv_aplic_create(aplic_s->base +
+ socket * aplic_s->size,
+ aplic_s->size,
+ (msimode) ? 0 : base_hartid,
+ (msimode) ? 0 : hart_count,
+ num_sources,
+ num_prio_bits,
+ msimode, false, aplic_m_dev);
+
+ if (kvm_enabled() && msimode) {
+ riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s_dev), addr);
+ }
+
+ return kvm_enabled() ? aplic_s_dev : aplic_m_dev;
+}
diff --git a/hw/riscv/aia.h b/hw/riscv/aia.h
new file mode 100644
index 0000000000..565f91accc
--- /dev/null
+++ b/hw/riscv/aia.h
@@ -0,0 +1,26 @@
+/*
+ * QEMU RISC-V Advanced Interrupt Architecture (AIA)
+ *
+ * Copyright (C) 2019 Western Digital Corporation or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_RISCV_AIA_H
+#define HW_RISCV_AIA_H
+
+#include "exec/hwaddr.h"
+
+uint32_t imsic_num_bits(uint32_t count);
+
+DeviceState *riscv_create_aia(bool msimode, int aia_guests,
+ uint32_t m_imsic_stride,
+ uint16_t num_sources,
+ const MemMapEntry *aplic_m,
+ const MemMapEntry *aplic_s,
+ const MemMapEntry *imsic_m,
+ const MemMapEntry *imsic_s,
+ int socket, int base_hartid, int hart_count,
+ uint32_t num_msis, uint32_t num_prio_bits);
+
+#endif
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 7c9cd61468..5e2dfa091a 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -70,11 +70,27 @@ char *riscv_plic_hart_config_string(int hart_count)
void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts)
{
+ info->ram_low_start = 0;
+ info->ram_low_size = 0;
info->kernel_size = 0;
info->initrd_size = 0;
info->is_32bit = riscv_is_32bit(harts);
}
+/*
+ * This can be used instead of riscv_boot_info_init() if the machine has
+ * discontiguous physical memory. The low memory range specified will be
+ * used to place firmware images.
+ */
+void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
+ RISCVHartArrayState *harts,
+ hwaddr low_start, hwaddr low_size)
+{
+ riscv_boot_info_init(info, harts);
+ info->ram_low_start = low_start;
+ info->ram_low_size = low_size;
+}
+
vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
hwaddr firmware_end_addr) {
if (info->is_32bit) {
@@ -135,6 +151,7 @@ char *riscv_find_firmware(const char *firmware_filename,
}
hwaddr riscv_find_and_load_firmware(MachineState *machine,
+ RISCVBootInfo *info,
const char *default_machine_firmware,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb)
@@ -147,7 +164,8 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
if (firmware_filename) {
/* If not "none" load the firmware */
- firmware_end_addr = riscv_load_firmware(firmware_filename,
+ firmware_end_addr = riscv_load_firmware(machine, info,
+ firmware_filename,
firmware_load_addr, sym_cb);
g_free(firmware_filename);
}
@@ -155,10 +173,13 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
return firmware_end_addr;
}
-hwaddr riscv_load_firmware(const char *firmware_filename,
+hwaddr riscv_load_firmware(MachineState *machine,
+ const RISCVBootInfo *info,
+ const char *firmware_filename,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb)
{
+ uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
uint64_t firmware_entry, firmware_end;
ssize_t firmware_size;
@@ -187,7 +208,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
firmware_size = load_image_targphys_as(firmware_filename,
*firmware_load_addr,
- current_machine->ram_size, NULL,
+ mem_size, NULL,
NULL);
if (firmware_size > 0) {
@@ -202,7 +223,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info)
{
const char *filename = machine->initrd_filename;
- uint64_t mem_size = machine->ram_size;
+ uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
void *fdt = machine->fdt;
hwaddr start, end;
ssize_t size;
@@ -248,6 +269,7 @@ void riscv_load_kernel(MachineState *machine,
bool load_initrd,
symbol_fn_t sym_cb)
{
+ uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
const char *kernel_filename = machine->kernel_filename;
ssize_t kernel_size;
void *fdt = machine->fdt;
@@ -279,7 +301,7 @@ void riscv_load_kernel(MachineState *machine,
}
kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr,
- current_machine->ram_size, NULL, NULL);
+ mem_size, NULL, NULL);
if (kernel_size > 0) {
info->kernel_size = kernel_size;
info->image_low_addr = kernel_start_addr;
@@ -375,7 +397,7 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
if (dtb_start_limit && (dtb_start < dtb_start_limit)) {
- error_report("No enough memory to place DTB after kernel/initrd");
+ error_report("Not enough memory to place DTB after kernel/initrd");
exit(1);
}
diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c
index b27ff13bca..e0e31af09b 100644
--- a/hw/riscv/fdt-common.c
+++ b/hw/riscv/fdt-common.c
@@ -200,3 +200,33 @@ create_fdt_socket_cpu_sifive(void *fdt, char *clust_name,
socket_id, socket_hartid_base,
phandle, intc_phandles, false, false);
}
+
+void create_fdt_plic(void *fdt, hwaddr addr, uint64_t size,
+ uint32_t plic_phandle, uint32_t int_cells,
+ uint32_t addr_cells, uint32_t *plic_cells,
+ uint32_t cells_size, uint32_t ndev_sources,
+ bool numa_enabled, int socket_id)
+{
+ g_autofree char *nodename = NULL;
+ static const char * const plic_compat[2] = {
+ "sifive,plic-1.0.0", "riscv,plic0"
+ };
+
+ nodename = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIx, addr);
+
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", int_cells);
+ qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", addr_cells);
+ qemu_fdt_setprop_string_array(fdt, nodename, "compatible",
+ (char **)&plic_compat, ARRAY_SIZE(plic_compat));
+ qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+ plic_cells, cells_size);
+ qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
+ 2, addr, 2, size);
+ qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", ndev_sources);
+ if (numa_enabled) {
+ qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", socket_id);
+ }
+ qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle);
+}
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
index 502281c52c..656f28190c 100644
--- a/hw/riscv/k230.c
+++ b/hw/riscv/k230.c
@@ -424,7 +424,8 @@ static void k230_direct_boot(K230MachineState *s, MachineState *machine)
riscv_load_fdt(K230_DIRECT_DTB_ADDR, machine->fdt);
- firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
&start_addr, NULL);
if (firmware_end_addr > K230_DIRECT_KERNEL_ADDR) {
error_report("K230 firmware overlaps kernel address 0x%x",
@@ -442,13 +443,16 @@ static void k230_firmware_boot(K230MachineState *s, MachineState *machine)
{
const char *firmware_name = riscv_default_firmware_name(&s->soc.c908_cpu);
hwaddr start_addr = memmap[K230_DEV_DDRC].base;
+ RISCVBootInfo boot_info = {0};
if (machine->dtb || (machine->kernel_cmdline && *machine->kernel_cmdline)) {
error_report("K230 firmware boot does not support -dtb or -append");
exit(EXIT_FAILURE);
}
- riscv_find_and_load_firmware(machine, firmware_name, &start_addr, NULL);
+ riscv_boot_info_init(&boot_info, &s->soc.c908_cpu);
+ riscv_find_and_load_firmware(machine, &boot_info, firmware_name,
+ &start_addr, NULL);
riscv_setup_rom_reset_vec(machine, &s->soc.c908_cpu, start_addr,
memmap[K230_DEV_BOOTROM].base,
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index b70a054579..0d82ceacc4 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -1,4 +1,5 @@
riscv_ss = ss.source_set()
+riscv_ss.add(files('aia.c'))
riscv_ss.add(files('boot.c'))
riscv_ss.add(files('fdt-common.c'))
riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c'))
@@ -10,6 +11,7 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c'))
riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c'))
riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c'))
+riscv_ss.add(when: 'CONFIG_TENSTORRENT', if_true: files('tt_atlantis.c'))
riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
index 5e48a29708..4017129c83 100644
--- a/hw/riscv/microchip_pfsoc.c
+++ b/hw/riscv/microchip_pfsoc.c
@@ -619,18 +619,22 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
firmware_load_addr = RESET_VECTOR;
}
+ riscv_boot_info_init_discontig_mem(&boot_info, &s->soc.u_cpus,
+ memmap[MICROCHIP_PFSOC_DRAM_LO].base,
+ mem_low_size);
+
/* Load the firmware if necessary */
firmware_end_addr = firmware_load_addr;
if (firmware_name) {
char *filename = riscv_find_firmware(firmware_name, NULL);
if (filename) {
- firmware_end_addr = riscv_load_firmware(filename,
+ firmware_end_addr = riscv_load_firmware(machine, &boot_info,
+ filename,
&firmware_load_addr, NULL);
g_free(filename);
}
}
- riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index c8b2f028f2..5b2f33d5ac 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -100,12 +100,14 @@ static void opentitan_machine_init(MachineState *machine)
memory_region_add_subregion(sys_mem,
memmap[IBEX_DEV_RAM].base, machine->ram);
+ riscv_boot_info_init(&boot_info, &s->soc.cpus);
+
if (machine->firmware) {
hwaddr firmware_load_addr = memmap[IBEX_DEV_RAM].base;
- riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL);
+ riscv_load_firmware(machine, &boot_info, machine->firmware,
+ &firmware_load_addr, NULL);
}
- riscv_boot_info_init(&boot_info, &s->soc.cpus);
if (machine->kernel_filename) {
riscv_load_kernel(machine, &boot_info,
memmap[IBEX_DEV_RAM].base,
diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h
index a938fd3eb4..cc0b1c3ccd 100644
--- a/hw/riscv/riscv-iommu-bits.h
+++ b/hw/riscv/riscv-iommu-bits.h
@@ -308,6 +308,9 @@ struct riscv_iommu_dc {
#define RISCV_IOMMU_DC_IOHGATP_GSCID GENMASK_ULL(59, 44)
#define RISCV_IOMMU_DC_IOHGATP_MODE RISCV_IOMMU_ATP_MODE_FIELD
+#define RISCV_IOMMU_DC_TC_RESERVED (GENMASK_ULL(23, 12) \
+ | GENMASK_ULL(63, 32))
+
enum riscv_iommu_dc_iohgatp_modes {
RISCV_IOMMU_DC_IOHGATP_MODE_BARE = 0,
RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4 = 8,
@@ -344,6 +347,7 @@ struct riscv_iommu_command {
#define RISCV_IOMMU_CMD_IOFENCE_OPCODE 2
#define RISCV_IOMMU_CMD_IOFENCE_FUNC_C 0
#define RISCV_IOMMU_CMD_IOFENCE_AV BIT_ULL(10)
+#define RISCV_IOMMU_CMD_IOFENCE_RESERVED GENMASK_ULL(31, 14)
#define RISCV_IOMMU_CMD_IOFENCE_DATA GENMASK_ULL(63, 32)
#define RISCV_IOMMU_CMD_IODIR_OPCODE 3
diff --git a/hw/riscv/riscv-iommu-hpm.c b/hw/riscv/riscv-iommu-hpm.c
index 5bf80a8db9..9bfef6db45 100644
--- a/hw/riscv/riscv-iommu-hpm.c
+++ b/hw/riscv/riscv-iommu-hpm.c
@@ -60,8 +60,8 @@ static void hpm_incr_ctr(RISCVIOMMUState *s, uint32_t ctr_idx)
const uint32_t off = ctr_idx << 3;
uint64_t cntr_val;
- cntr_val = ldq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]);
- stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1);
+ cntr_val = ldq_le_p(&s->regs[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]);
+ stq_le_p(&s->regs[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1);
trace_riscv_iommu_hpm_incr_ctr(cntr_val);
diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
index a500cb8440..974042d017 100644
--- a/hw/riscv/riscv-iommu.c
+++ b/hw/riscv/riscv-iommu.c
@@ -480,7 +480,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
break; /* Read access check failed */
} else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) {
break; /* Write access check failed */
- } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) {
+ } else if (!ade && !(pte & PTE_A)) {
break; /* Access bit not set */
} else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) {
break; /* Dirty bit not set */
@@ -580,7 +580,7 @@ static void riscv_iommu_report_fault(RISCVIOMMUState *s,
ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause);
ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type);
ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid);
- ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true);
+ ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, pv);
if (pv) {
ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id);
@@ -686,7 +686,26 @@ static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s,
/* MRIF pending bit address */
addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9;
- addr = addr | ((data & 0x7c0) >> 3);
+ /*
+ * AIA spec section "Format of a memory-resident interrupt file":
+ * address offset 0x000 contains interrupt-pending bits for
+ * identities 1-63, offfset 0x010 for identities 64-127, and
+ * so it goes up to 0x1F0 for identities 1984-2047.
+ *
+ * Hence each batch of identities advances offset by 16 (0x010)
+ * for every interrupt-pending bits. This means that doing
+ * (data & 0x7c0) will filter out the first 6 bits, then
+ * a >> 2 will turn the result in the 0x10 steps we need.
+ *
+ * E.g:
+ *
+ * - (1-63 & 0x7c0) = 0, 0 >> 2 = 0, offset 0x000
+ * - (64-127 & 0x7c0) = 64, 64 >> 2 = 16, offset 0x010
+ * - (128-191 & 0x7c0) = 128, 128 >> 2 = 32, offset 0x020
+ *
+ * and so on.
+ */
+ addr = addr | ((data & 0x7c0) >> 2);
trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid),
PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid),
@@ -752,6 +771,10 @@ static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s,
uint32_t fsc_mode, msi_mode;
uint64_t gatp;
+ if (ctx->tc & RISCV_IOMMU_DC_TC_RESERVED) {
+ return false;
+ }
+
if (!(s->cap & RISCV_IOMMU_CAP_ATS) &&
(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS ||
ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI ||
@@ -1876,6 +1899,10 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s)
switch (cmd_opcode) {
case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C,
RISCV_IOMMU_CMD_IOFENCE_OPCODE):
+ if (cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_RESERVED) {
+ goto cmd_ill;
+ }
+
res = riscv_iommu_iofence(s,
cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2,
get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA));
@@ -2034,8 +2061,8 @@ static void riscv_iommu_process_cq_control(RISCVIOMMUState *s)
s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1;
s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN));
stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask);
- stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0);
- stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0);
+ stl_le_p(&s->regs[RISCV_IOMMU_REG_CQH], 0);
+ stl_le_p(&s->regs[RISCV_IOMMU_REG_CQT], 0);
ctrl_set = RISCV_IOMMU_CQCSR_CQON;
ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF |
RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO |
@@ -2075,8 +2102,8 @@ static void riscv_iommu_process_fq_control(RISCVIOMMUState *s)
s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1;
s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN));
stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask);
- stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0);
- stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0);
+ stl_le_p(&s->regs[RISCV_IOMMU_REG_FQH], 0);
+ stl_le_p(&s->regs[RISCV_IOMMU_REG_FQT], 0);
ctrl_set = RISCV_IOMMU_FQCSR_FQON;
ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF |
RISCV_IOMMU_FQCSR_FQOF;
@@ -2105,8 +2132,8 @@ static void riscv_iommu_process_pq_control(RISCVIOMMUState *s)
s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1;
s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN));
stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask);
- stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0);
- stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0);
+ stl_le_p(&s->regs[RISCV_IOMMU_REG_PQH], 0);
+ stl_le_p(&s->regs[RISCV_IOMMU_REG_PQT], 0);
ctrl_set = RISCV_IOMMU_PQCSR_PQON;
ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF |
RISCV_IOMMU_PQCSR_PQOF;
@@ -2276,9 +2303,9 @@ static void riscv_iommu_write_reg_val(RISCVIOMMUState *s,
{
uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size);
uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size);
- uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size);
+ uint64_t curr_val = ldn_le_p(&s->regs[reg_addr], size);
- stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc));
+ stn_le_p(dest, size, ((curr_val & ro) | (data & ~ro)) & ~(data & wc));
}
static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
@@ -2378,12 +2405,12 @@ static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr,
* is set IOMMU behavior of additional writes to the register
* is UNSPECIFIED.
*/
- riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data);
+ riscv_iommu_write_reg_val(s, &s->regs[addr], addr, size, data);
/* Busy flag update, MSB 4-byte register. */
if (busy) {
- uint32_t rw = ldl_le_p(&s->regs_rw[regb]);
- stl_le_p(&s->regs_rw[regb], rw | busy);
+ uint32_t rw = ldl_le_p(&s->regs[regb]);
+ stl_le_p(&s->regs[regb], rw | busy);
}
/* Process HPM writes and update any internal state if needed. */
@@ -2428,13 +2455,13 @@ static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr,
* it's not dependent over the timer callback and is computed
* from cycle overflow.
*/
- val = ldq_le_p(&s->regs_rw[addr]);
+ val = ldq_le_p(&s->regs[addr]);
val |= (riscv_iommu_hpmcycle_read(s) & RISCV_IOMMU_IOHPMCYCLES_OVF)
? RISCV_IOMMU_IOCOUNTOVF_CY
: 0;
ptr = (uint8_t *)&val + (addr & 3);
} else {
- ptr = &s->regs_rw[addr];
+ ptr = &s->regs[addr];
}
val = ldn_le_p(ptr, size);
@@ -2529,7 +2556,7 @@ static void riscv_iommu_instance_init(Object *obj)
s->cap |= RISCV_IOMMU_CAP_PD8;
/* register storage */
- s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
+ s->regs = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
@@ -2554,7 +2581,7 @@ static void riscv_iommu_instance_finalize(Object *obj)
{
RISCVIOMMUState *s = RISCV_IOMMU(obj);
- g_free(s->regs_rw);
+ g_free(s->regs);
g_free(s->regs_ro);
g_free(s->regs_wc);
@@ -2608,10 +2635,12 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp)
"riscv-iommu-regs", RISCV_IOMMU_REG_SIZE);
/* Set power-on register state */
- stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap);
- stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0);
+ stq_le_p(&s->regs[RISCV_IOMMU_REG_CAP], s->cap);
+
+ stq_le_p(&s->regs[RISCV_IOMMU_REG_FCTL], 0);
stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL],
- ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI));
+ ~(RISCV_IOMMU_FCTL_GXL | RISCV_IOMMU_FCTL_WSI));
+
stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP],
~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE));
stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB],
@@ -2634,7 +2663,7 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp)
RISCV_IOMMU_PQCSR_BUSY);
stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0);
stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0);
- stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp);
+ stq_le_p(&s->regs[RISCV_IOMMU_REG_DDTP], s->ddtp);
/* If debug registers enabled. */
if (s->cap & RISCV_IOMMU_CAP_DBG) {
stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0);
diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h
index 2a9f6fccd5..a778e86fb7 100644
--- a/hw/riscv/riscv-iommu.h
+++ b/hw/riscv/riscv-iommu.h
@@ -76,9 +76,13 @@ struct RISCVIOMMUState {
/* MMIO Hardware Interface */
MemoryRegion regs_mr;
- uint8_t *regs_rw; /* register state (user write) */
+ uint8_t *regs; /* current register state */
uint8_t *regs_wc; /* write-1-to-clear mask */
- uint8_t *regs_ro; /* read-only mask */
+ /*
+ * read-only mask. NOTE: bits not present in this RO
+ * mask are assumed to be read and write.
+ */
+ uint8_t *regs_ro;
QLIST_ENTRY(RISCVIOMMUState) iommus;
QLIST_HEAD(, RISCVIOMMUSpace) spaces;
@@ -120,39 +124,39 @@ struct RISCVIOMMUContext {
static inline uint32_t riscv_iommu_reg_mod32(RISCVIOMMUState *s,
unsigned idx, uint32_t set, uint32_t clr)
{
- uint32_t val = ldl_le_p(s->regs_rw + idx);
- stl_le_p(s->regs_rw + idx, (val & ~clr) | set);
+ uint32_t val = ldl_le_p(s->regs + idx);
+ stl_le_p(s->regs + idx, (val & ~clr) | set);
return val;
}
static inline void riscv_iommu_reg_set32(RISCVIOMMUState *s, unsigned idx,
uint32_t set)
{
- stl_le_p(s->regs_rw + idx, set);
+ stl_le_p(s->regs + idx, set);
}
static inline uint32_t riscv_iommu_reg_get32(RISCVIOMMUState *s, unsigned idx)
{
- return ldl_le_p(s->regs_rw + idx);
+ return ldl_le_p(s->regs + idx);
}
static inline uint64_t riscv_iommu_reg_mod64(RISCVIOMMUState *s, unsigned idx,
uint64_t set, uint64_t clr)
{
- uint64_t val = ldq_le_p(s->regs_rw + idx);
- stq_le_p(s->regs_rw + idx, (val & ~clr) | set);
+ uint64_t val = ldq_le_p(s->regs + idx);
+ stq_le_p(s->regs + idx, (val & ~clr) | set);
return val;
}
static inline void riscv_iommu_reg_set64(RISCVIOMMUState *s, unsigned idx,
uint64_t set)
{
- stq_le_p(s->regs_rw + idx, set);
+ stq_le_p(s->regs + idx, set);
}
static inline uint64_t riscv_iommu_reg_get64(RISCVIOMMUState *s,
- unsigned idx)
+ unsigned idx)
{
- return ldq_le_p(s->regs_rw + idx);
+ return ldq_le_p(s->regs + idx);
}
#endif
diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c
index b1823a3125..835b1f879b 100644
--- a/hw/riscv/shakti_c.c
+++ b/hw/riscv/shakti_c.c
@@ -46,6 +46,7 @@ static void shakti_c_machine_state_init(MachineState *mstate)
{
ShaktiCMachineState *sms = RISCV_SHAKTI_MACHINE(mstate);
MemoryRegion *system_memory = get_system_memory();
+ RISCVBootInfo boot_info;
hwaddr firmware_load_addr = shakti_c_memmap[SHAKTI_C_RAM].base;
/* Initialize SoC */
@@ -58,8 +59,11 @@ static void shakti_c_machine_state_init(MachineState *mstate)
shakti_c_memmap[SHAKTI_C_RAM].base,
mstate->ram);
+ riscv_boot_info_init(&boot_info, &sms->soc.cpus);
+
if (mstate->firmware) {
- riscv_load_firmware(mstate->firmware, &firmware_load_addr, NULL);
+ riscv_load_firmware(mstate, &boot_info, mstate->firmware,
+ &firmware_load_addr, NULL);
}
/* ROM reset vector */
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 0c6e4204cb..99423ef472 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -100,14 +100,11 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
MachineState *ms = MACHINE(s);
void *fdt;
int cpu;
- uint32_t *cells;
+ uint32_t *cells, cells_length;
char *nodename;
uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1;
uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle;
static const char * const ethclk_names[2] = { "pclk", "hclk" };
- static const char * const plic_compat[2] = {
- "sifive,plic-1.0.0", "riscv,plic0"
- };
g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus);
g_autofree char *clust_name = NULL;
@@ -197,41 +194,28 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
g_free(nodename);
plic_phandle = phandle++;
- cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2);
+ cells_length = ms->smp.cpus * 4 - 2;
+ cells = g_new0(uint32_t, cells_length);
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
- nodename =
- g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
- uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
/* cpu 0 is the management hart that does not have S-mode */
if (cpu == 0) {
- cells[0] = cpu_to_be32(intc_phandle);
+ cells[0] = cpu_to_be32(intc_phandles[cpu]);
cells[1] = cpu_to_be32(IRQ_M_EXT);
} else {
- cells[cpu * 4 - 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 - 2] = cpu_to_be32(intc_phandles[cpu]);
cells[cpu * 4 - 1] = cpu_to_be32(IRQ_M_EXT);
- cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_S_EXT);
}
- g_free(nodename);
}
- nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
- (long)memmap[SIFIVE_U_DEV_PLIC].base);
- qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
- qemu_fdt_setprop_string_array(fdt, nodename, "compatible",
- (char **)&plic_compat, ARRAY_SIZE(plic_compat));
- qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
- cells, (ms->smp.cpus * 4 - 2) * sizeof(uint32_t));
- qemu_fdt_setprop_cells(fdt, nodename, "reg",
- 0x0, memmap[SIFIVE_U_DEV_PLIC].base,
- 0x0, memmap[SIFIVE_U_DEV_PLIC].size);
- qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev",
- SIFIVE_U_PLIC_NUM_SOURCES - 1);
- qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle);
- plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
+
+ create_fdt_plic(fdt, memmap[SIFIVE_U_DEV_PLIC].base,
+ memmap[SIFIVE_U_DEV_PLIC].size,
+ plic_phandle, SIFIVE_U_PLIC_INT_CELLS,
+ SIFIVE_U_PLIC_ADDR_CELLS, cells,
+ cells_length * sizeof(uint32_t),
+ SIFIVE_U_PLIC_NUM_SOURCES - 1, false, 0);
g_free(cells);
- g_free(nodename);
gpio_phandle = phandle++;
nodename = g_strdup_printf("/soc/gpio@%lx",
@@ -540,11 +524,13 @@ static void sifive_u_machine_init(MachineState *machine)
break;
}
+ riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
+
firmware_name = riscv_default_firmware_name(&s->soc.u_cpus);
- firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
&start_addr, NULL);
- riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index 4e60724c14..9fde0faf39 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -205,9 +205,12 @@ static void spike_board_init(MachineState *machine)
}
}
+ riscv_boot_info_init(&boot_info, &s->soc[0]);
+
/* Load firmware */
if (firmware_name) {
- firmware_end_addr = riscv_load_firmware(firmware_name,
+ firmware_end_addr = riscv_load_firmware(machine, &boot_info,
+ firmware_name,
&firmware_load_addr,
htif_symbol_callback);
g_free(firmware_name);
@@ -217,7 +220,6 @@ static void spike_board_init(MachineState *machine)
create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base);
/* Load kernel */
- riscv_boot_info_init(&boot_info, &s->soc[0]);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
new file mode 100644
index 0000000000..beaf64e0c1
--- /dev/null
+++ b/hw/riscv/tt_atlantis.c
@@ -0,0 +1,617 @@
+/*
+ * Tenstorrent Atlantis RISC-V System on Chip
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qemu/guest-random.h"
+#include "qemu/units.h"
+
+#include "hw/core/boards.h"
+#include "hw/core/loader.h"
+#include "hw/core/sysbus.h"
+
+#include "target/riscv/cpu.h"
+#include "target/riscv/pmu.h"
+
+#include "hw/riscv/boot.h"
+#include "hw/riscv/fdt-common.h"
+#include "hw/riscv/machines-qom.h"
+#include "hw/riscv/riscv_hart.h"
+
+#include "hw/char/serial-mm.h"
+#include "hw/intc/riscv_aclint.h"
+#include "hw/misc/unimp.h"
+
+#include "system/system.h"
+#include "system/device_tree.h"
+
+#include "hw/riscv/tt_atlantis.h"
+
+#include "aia.h"
+
+#define TT_IRQCHIP_NUM_MSIS 255
+#define TT_IRQCHIP_NUM_SOURCES 128
+#define TT_IRQCHIP_NUM_PRIO_BITS 3
+#define TT_IRQCHIP_GUESTS 63 /* aia_guests, gives guest_index_bits=6 */
+#define TT_IRQCHIP_MIMSIC_STRIDE 0x40000
+
+#define TT_ACLINT_MTIME_SIZE 0x8050
+#define TT_ACLINT_MTIME 0x0
+#define TT_ACLINT_MTIMECMP 0x8000
+#define TT_ACLINT_TIMEBASE_FREQ 1000000000
+
+static const MemMapEntry tt_atlantis_memmap[] = {
+ /* Keep sorted with :'<,'>!sort -g -k 4 */
+ [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 },
+ [TT_ATL_BOOTROM] = { 0x80000000, 0x2000 },
+ [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 },
+ [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 },
+ [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 },
+ [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
+ [TT_ATL_I2C0] = { 0xd4040000, 0x10000 },
+ [TT_ATL_I2C1] = { 0xd4050000, 0x10000 },
+ [TT_ATL_I2C2] = { 0xd4060000, 0x10000 },
+ [TT_ATL_I2C3] = { 0xd4070000, 0x10000 },
+ [TT_ATL_I2C4] = { 0xd4080000, 0x10000 },
+ [TT_ATL_UART1] = { 0xd4110000, 0x10000 },
+ [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
+ [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
+};
+
+static I2CBus *i2c_get_bus(TTAtlantisState *s, unsigned busnr)
+{
+ assert(busnr < TT_ATL_NUM_I2C);
+
+ return s->i2c[busnr].bus;
+}
+
+static uint32_t fdt_phandle = 1;
+static uint32_t next_phandle(void)
+{
+ return fdt_phandle++;
+}
+
+static void create_fdt_memory(TTAtlantisState *s)
+{
+ void *fdt = MACHINE(s)->fdt;
+ hwaddr size_lo = MACHINE(s)->ram_size;
+ hwaddr size_hi = 0;
+
+ if (size_lo > s->memmap[TT_ATL_DDR_LO].size) {
+ size_lo = s->memmap[TT_ATL_DDR_LO].size;
+ size_hi = MACHINE(s)->ram_size - size_lo;
+ }
+
+ create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_LO].base, size_lo,
+ 0, false);
+ if (size_hi) {
+ /*
+ * The first part of the HI address is aliased at the LO address
+ * so do not include that as usable memory. Is there any way
+ * (or good reason) to describe that aliasing 2GB with DT?
+ */
+ create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_HI].base + size_lo,
+ size_hi, 0, false);
+ }
+}
+
+static void create_fdt_aclint(TTAtlantisState *s, uint32_t *intc_phandles)
+{
+ void *fdt = MACHINE(s)->fdt;
+ g_autofree char *name = NULL;
+ g_autofree uint32_t *aclint_mtimer_cells = NULL;
+ uint32_t aclint_cells_size;
+ hwaddr addr;
+
+ aclint_mtimer_cells = g_new0(uint32_t, s->soc.num_harts * 2);
+
+ for (int cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
+ }
+ aclint_cells_size = s->soc.num_harts * sizeof(uint32_t) * 2;
+
+ addr = s->memmap[TT_ATL_ACLINT].base;
+
+ name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aclint-mtimer");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg",
+ 2, addr + TT_ACLINT_MTIME,
+ 2, 0x1000,
+ 2, addr + TT_ACLINT_MTIMECMP,
+ 2, 0x1000);
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
+ aclint_mtimer_cells, aclint_cells_size);
+}
+
+static void create_fdt_one_imsic(void *fdt, const MemMapEntry *mem, int cpus,
+ uint32_t *intc_phandles, uint32_t msi_phandle,
+ int irq_line, uint32_t imsic_guest_bits)
+{
+ g_autofree char *name = NULL;
+ g_autofree uint32_t *imsic_cells = g_new0(uint32_t, cpus * 2);
+
+ for (int cpu = 0; cpu < cpus; cpu++) {
+ imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ imsic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
+ }
+
+ name = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,imsics");
+
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 0);
+ qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, name, "msi-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
+ imsic_cells, sizeof(uint32_t) * cpus * 2);
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "riscv,num-ids", TT_IRQCHIP_NUM_MSIS);
+
+ if (imsic_guest_bits) {
+ qemu_fdt_setprop_cell(fdt, name, "riscv,guest-index-bits",
+ imsic_guest_bits);
+ }
+ qemu_fdt_setprop_cell(fdt, name, "phandle", msi_phandle);
+}
+
+static void create_fdt_one_aplic(void *fdt,
+ const MemMapEntry *mem,
+ uint32_t msi_phandle,
+ uint32_t *intc_phandles,
+ uint32_t aplic_phandle,
+ uint32_t aplic_child_phandle,
+ int irq_line, int num_harts)
+{
+ g_autofree char *name =
+ g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
+ g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2);
+
+ for (int cpu = 0; cpu < num_harts; cpu++) {
+ aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aplic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
+ }
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aplic");
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", 0);
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 2);
+ qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
+
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
+ aplic_cells, num_harts * sizeof(uint32_t) * 2);
+ qemu_fdt_setprop_cell(fdt, name, "msi-parent", msi_phandle);
+
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "riscv,num-sources",
+ TT_IRQCHIP_NUM_SOURCES);
+
+ if (aplic_child_phandle) {
+ qemu_fdt_setprop_cell(fdt, name, "riscv,children",
+ aplic_child_phandle);
+ qemu_fdt_setprop_cells(fdt, name, "riscv,delegation",
+ aplic_child_phandle, 1, TT_IRQCHIP_NUM_SOURCES);
+ }
+
+ qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle);
+}
+
+static void create_fdt_pmu(TTAtlantisState *s)
+{
+ char pmu_name[] = "/pmu";
+ void *fdt = MACHINE(s)->fdt;
+ RISCVCPU *hart = &s->soc.harts[0];
+
+ qemu_fdt_add_subnode(fdt, pmu_name);
+ qemu_fdt_setprop_string(fdt, pmu_name, "compatible", "riscv,pmu");
+ riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name);
+}
+
+static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap,
+ uint32_t aplic_s_phandle,
+ uint32_t imsic_s_phandle)
+{
+ MachineState *ms = MACHINE(s);
+ void *fdt = MACHINE(s)->fdt;
+ g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus);
+
+ fdt_create_cpu_socket_subnode(fdt, TT_ACLINT_TIMEBASE_FREQ);
+
+ create_fdt_socket_cpus(fdt, s->soc.harts, 0, s->soc.num_harts,
+ s->soc.hartid_base, &fdt_phandle, intc_phandles,
+ false, false);
+
+ create_fdt_memory(s);
+
+ create_fdt_aclint(s, intc_phandles);
+
+ uint32_t imsic_guest_bits = imsic_num_bits(TT_IRQCHIP_GUESTS + 1);
+
+ /* M-level IMSIC node */
+ uint32_t msi_m_phandle = next_phandle();
+ create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], ms->smp.cpus,
+ intc_phandles, msi_m_phandle,
+ IRQ_M_EXT, imsic_guest_bits);
+
+ /* S-level IMSIC node */
+ create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], ms->smp.cpus,
+ intc_phandles, imsic_s_phandle,
+ IRQ_S_EXT, imsic_guest_bits);
+
+ uint32_t aplic_m_phandle = next_phandle();
+
+ /* M-level APLIC node */
+ create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC],
+ msi_m_phandle, intc_phandles,
+ aplic_m_phandle, aplic_s_phandle,
+ IRQ_M_EXT, s->soc.num_harts);
+
+ /* S-level APLIC node */
+ create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC],
+ imsic_s_phandle, intc_phandles,
+ aplic_s_phandle, 0,
+ IRQ_S_EXT, s->soc.num_harts);
+}
+
+static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq,
+ int irqchip_phandle)
+{
+ g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX,
+ mem->base);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2);
+ qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4);
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400);
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle);
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4);
+
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name);
+ qemu_fdt_setprop_string(fdt, "/aliases", "serial0", name);
+}
+
+static void create_fdt_rng(void *fdt)
+{
+ uint8_t rng_seed[32];
+
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
+}
+
+static void create_fdt_clk(void *fdt, const char *clock_name,
+ uint32_t freq, uint32_t phandle)
+{
+ g_autofree char *name = g_strdup_printf("/clocks/%s", clock_name);
+
+ qemu_fdt_add_path(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "fixed-clock");
+ qemu_fdt_setprop_string(fdt, name, "clock-output-names", clock_name);
+ qemu_fdt_setprop_cell(fdt, name, "#clock-cells", 0);
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", freq);
+ qemu_fdt_setprop_cell(fdt, name, "phandle", phandle);
+}
+
+static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq,
+ uint32_t irqchip_phandle, uint32_t clk_phandle)
+{
+ g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX, mem->base);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "snps,designware-i2c");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle);
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4);
+ qemu_fdt_setprop_cell(fdt, name, "clocks", clk_phandle);
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 100000);
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", 1);
+ qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0);
+}
+
+static void create_fdt_i2c_device(TTAtlantisState *s, int bus,
+ const char *compat, int addr)
+{
+ void *fdt = MACHINE(s)->fdt;
+ hwaddr base = s->memmap[TT_ATL_I2C0 + bus].base;
+ g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX"/sensor@%x",
+ base, addr);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", compat);
+ qemu_fdt_setprop_cell(fdt, name, "reg", addr);
+}
+
+static void finalize_fdt(TTAtlantisState *s)
+{
+ uint32_t aplic_s_phandle = next_phandle();
+ uint32_t imsic_s_phandle = next_phandle();
+ uint32_t periph_clk_phandle = next_phandle();
+ void *fdt = MACHINE(s)->fdt;
+
+ create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
+
+ /*
+ * We want to do this, but the Linux aplic driver was broken before v6.16
+ *
+ * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
+ * aplic_s_phandle);
+ */
+
+ create_fdt_uart(fdt, &s->memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ,
+ aplic_s_phandle);
+
+ create_fdt_clk(fdt, "periph-clk", 100000000, periph_clk_phandle);
+
+ for (int i = 0; i < TT_ATL_NUM_I2C; i++) {
+ create_fdt_i2c(fdt,
+ &s->memmap[TT_ATL_I2C0 + i],
+ TT_ATL_I2C0_IRQ + i,
+ aplic_s_phandle, periph_clk_phandle);
+ }
+
+ create_fdt_i2c_device(s, 0, "dallas,ds1338", 0x6f);
+ create_fdt_i2c_device(s, 4, "ti,tmp105", 0x48);
+}
+
+static void create_fdt(TTAtlantisState *s)
+{
+ MachineState *ms = MACHINE(s);
+
+ ms->fdt = create_board_device_tree("Tenstorrent Atlantis RISC-V Machine",
+ "tenstorrent,atlantis", &s->fdt_size);
+
+ qemu_fdt_add_subnode(ms->fdt, "/chosen");
+
+ create_fdt_rng(ms->fdt);
+
+ qemu_fdt_add_subnode(ms->fdt, "/aliases");
+
+ create_fdt_pmu(s);
+}
+
+static void load_fdt(TTAtlantisState *s)
+{
+ MachineState *ms = MACHINE(s);
+ char **node_path;
+ Error *err = NULL;
+
+ ms->fdt = load_device_tree(ms->dtb, &s->fdt_size);
+ if (!ms->fdt) {
+ error_report("load_device_tree() failed");
+ exit(1);
+ }
+
+ qemu_fdt_add_path(ms->fdt, "/chosen");
+
+ /* Clear memory nodes and update with the specified RAM size */
+ node_path = qemu_fdt_node_unit_path(ms->fdt, "memory", &err);
+ if (err) {
+ warn_report_err(err);
+ } else {
+ for (int i = 0; node_path[i]; i++) {
+ warn_report("Replacing device tree %s with the requested RAM size",
+ node_path[i]);
+ qemu_fdt_nop_node(ms->fdt, node_path[i]);
+ }
+ g_strfreev(node_path);
+ }
+
+ create_fdt_memory(s);
+}
+
+static void tt_atlantis_machine_done(Notifier *notifier, void *data)
+{
+ TTAtlantisState *s = container_of(notifier, TTAtlantisState, machine_done);
+ MachineState *machine = MACHINE(s);
+ hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base;
+ hwaddr mem_size;
+ target_ulong firmware_end_addr, kernel_start_addr;
+ const char *firmware_name = riscv_default_firmware_name(&s->soc);
+ uint64_t fdt_load_addr;
+ uint64_t kernel_entry;
+ RISCVBootInfo boot_info;
+
+ /*
+ * A user provided dtb must include everything, including
+ * dynamic sysbus devices. Our FDT needs to be finalized.
+ */
+ if (machine->dtb == NULL) {
+ finalize_fdt(s);
+ }
+
+ mem_size = machine->ram_size;
+ if (mem_size > s->memmap[TT_ATL_DDR_LO].size) {
+ mem_size = s->memmap[TT_ATL_DDR_LO].size;
+ }
+ riscv_boot_info_init_discontig_mem(&boot_info, &s->soc,
+ s->memmap[TT_ATL_DDR_LO].base,
+ mem_size);
+
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
+ &start_addr, NULL);
+
+ kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
+ firmware_end_addr);
+ if (machine->kernel_filename) {
+ riscv_load_kernel(machine, &boot_info, kernel_start_addr,
+ true, NULL);
+ kernel_entry = boot_info.image_low_addr;
+ } else {
+ /* If we aren't loading a payload, OpenSBI thinks we are trying to boot
+ * address 0, which fails `sbi_domain_check_addr()` as that is where
+ * OpenSBI is running. Instead point OpenSBI to the end of the region
+ * where it was loaded, which avoids the early hang, allowing the
+ * system to proceed with the OpenSBI boot output.
+ */
+ kernel_entry = kernel_start_addr;
+ }
+
+ fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base,
+ s->memmap[TT_ATL_DDR_LO].size,
+ machine, &boot_info);
+ riscv_load_fdt(fdt_load_addr, machine->fdt);
+
+ /* load the reset vector */
+ riscv_setup_rom_reset_vec(machine, &s->soc, start_addr,
+ s->memmap[TT_ATL_BOOTROM].base,
+ s->memmap[TT_ATL_BOOTROM].size,
+ kernel_entry,
+ fdt_load_addr);
+}
+
+static void tt_atlantis_machine_init(MachineState *machine)
+{
+ TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine);
+
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
+ MemoryRegion *bootrom = g_new(MemoryRegion, 1);
+ ram_addr_t lo_ram_size;
+ int hart_count = machine->smp.cpus;
+
+ s->memmap = tt_atlantis_memmap;
+
+ object_initialize_child(OBJECT(machine), "soc", &s->soc,
+ TYPE_RISCV_HART_ARRAY);
+ object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), "hartid-base", 0,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), "num-harts", hart_count,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), "resetvec",
+ s->memmap[TT_ATL_BOOTROM].base,
+ &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
+
+ s->irqchip = riscv_create_aia(true, TT_IRQCHIP_GUESTS,
+ TT_IRQCHIP_MIMSIC_STRIDE,
+ TT_IRQCHIP_NUM_SOURCES,
+ &s->memmap[TT_ATL_MAPLIC],
+ &s->memmap[TT_ATL_SAPLIC],
+ &s->memmap[TT_ATL_MIMSIC],
+ &s->memmap[TT_ATL_SIMSIC],
+ 0, 0, hart_count,
+ TT_IRQCHIP_NUM_MSIS,
+ TT_IRQCHIP_NUM_PRIO_BITS);
+
+ riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base,
+ TT_ACLINT_MTIME_SIZE,
+ 0, hart_count,
+ TT_ACLINT_MTIMECMP,
+ TT_ACLINT_MTIME,
+ TT_ACLINT_TIMEBASE_FREQ, true);
+
+ /*
+ * DDR
+ *
+ * The high address is where RAM lives. It is always present and may be
+ * up to 64GB. The low address is an alias of the first 2GB of that RAM.
+ */
+ if (machine->ram_size > s->memmap[TT_ATL_DDR_HI].size) {
+ char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size);
+ error_report("RAM size is too large, maximum is %s", sz);
+ g_free(sz);
+ exit(EXIT_FAILURE);
+ }
+
+ memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", machine->ram,
+ 0, machine->ram_size);
+ memory_region_add_subregion(system_memory,
+ s->memmap[TT_ATL_DDR_HI].base, ram_hi);
+
+ lo_ram_size = MIN(machine->ram_size, s->memmap[TT_ATL_DDR_LO].size);
+ memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", machine->ram,
+ 0, lo_ram_size);
+ memory_region_add_subregion(system_memory,
+ s->memmap[TT_ATL_DDR_LO].base, ram_lo);
+
+ /* Boot ROM */
+ memory_region_init_rom(bootrom, NULL, "tt-atlantis.bootrom",
+ s->memmap[TT_ATL_BOOTROM].size, &error_fatal);
+ memory_region_add_subregion(system_memory, s->memmap[TT_ATL_BOOTROM].base,
+ bootrom);
+
+ /* UART1, the soc console (UART0 is for the boot microcontroller) */
+ serial_mm_init(system_memory, s->memmap[TT_ATL_UART1].base, 2,
+ qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ),
+ 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+ /*
+ * Atlantis contains a DesignWare uart while the QEMU machine
+ * uses the serial_mm model with the base ns16550 register set.
+ * Linux's dw driver writes outside of serial_mm's 0x20 sized
+ * mapping and faults.
+ *
+ * Create an unimplemented device region so writes don't fault
+ * and reads return zero, which keeps Linux happy.
+ */
+ create_unimplemented_device("tt-atlantis.uart0",
+ s->memmap[TT_ATL_UART1].base,
+ s->memmap[TT_ATL_UART1].size);
+
+ /* I2C */
+ for (int i = 0; i < TT_ATL_NUM_I2C; i++) {
+ SysBusDevice *sbd;
+
+ object_initialize_child(OBJECT(s), "i2c[*]", &s->i2c[i],
+ TYPE_DESIGNWARE_I2C);
+ sbd = SYS_BUS_DEVICE(&s->i2c[i]);
+ sysbus_realize(sbd, &error_fatal);
+ memory_region_add_subregion(system_memory,
+ s->memmap[TT_ATL_I2C0 + i].base,
+ sysbus_mmio_get_region(sbd, 0));
+ sysbus_connect_irq(sbd, 0,
+ qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i));
+ }
+
+ /* I2C peripherals: qemu specific */
+ i2c_slave_create_simple(i2c_get_bus(s, 0), "ds1338", 0x6f);
+ i2c_slave_create_simple(i2c_get_bus(s, 4), "tmp105", 0x48);
+
+ /* Load or create device tree */
+ if (machine->dtb) {
+ load_fdt(s);
+ } else {
+ create_fdt(s);
+ }
+
+ s->machine_done.notify = tt_atlantis_machine_done;
+ qemu_add_machine_init_done_notifier(&s->machine_done);
+}
+
+static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Tenstorrent Atlantis RISC-V SoC (Experimental)";
+ mc->init = tt_atlantis_machine_init;
+ mc->max_cpus = 8;
+ mc->default_cpus = 8;
+ mc->default_ram_size = 4 * GiB;
+ mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
+ mc->block_default_type = IF_VIRTIO;
+ mc->no_cdrom = 1;
+ mc->default_ram_id = "tt_atlantis.ram";
+}
+
+static const TypeInfo tt_atlantis_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("tt-atlantis"),
+ .parent = TYPE_MACHINE,
+ .class_init = tt_atlantis_machine_class_init,
+ .instance_size = sizeof(TTAtlantisState),
+ .interfaces = riscv64_machine_interfaces,
+ },
+};
+
+DEFINE_TYPES(tt_atlantis_types)
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index 413d47d70e..59c454f4f9 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -42,6 +42,8 @@
#include "system/kvm.h"
#include "system/reset.h"
+#include "aia.h"
+
#define ACPI_BUILD_TABLE_SIZE 0x20000
#define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index))
@@ -100,6 +102,8 @@ static void riscv_acpi_madt_add_rintc(uint32_t uid,
build_append_int_noprefix(entry,
ACPI_BUILD_INTC_ID(
arch_ids->cpus[uid].props.node_id,
+ kvm_enabled() ?
+ local_cpu_id :
2 * local_cpu_id + 1),
4);
} else {
@@ -144,6 +148,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s)
}
static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count,
+ uint16_t num_sources,
uint64_t mmio_base, uint64_t mmio_size,
const char *hid)
{
@@ -151,9 +156,12 @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count,
uint32_t gsi_base;
uint8_t socket;
+ /* The RISC-V Advanced Interrupt Architecture, Chapter 1.2. Limits */
+ g_assert(num_sources <= 1023);
+
for (socket = 0; socket < socket_count; socket++) {
plic_aplic_addr = mmio_base + mmio_size * socket;
- gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
+ gsi_base = num_sources * socket;
Aml *dev = aml_device("IC%.02X", socket);
aml_append(dev, aml_name_decl("_HID", aml_string("%s", hid)));
aml_append(dev, aml_name_decl("_UID", aml_int(socket)));
@@ -472,10 +480,13 @@ static void build_dsdt(GArray *table_data,
socket_count = riscv_socket_count(ms);
if (s->aia_type == VIRT_AIA_TYPE_NONE) {
- acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_PLIC].base,
- memmap[VIRT_PLIC].size, "RSCV0001");
+ acpi_dsdt_add_plic_aplic(scope, socket_count, s->num_sources,
+ memmap[VIRT_PLIC].base,
+ memmap[VIRT_PLIC].size,
+ "RSCV0001");
} else {
- acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_APLIC_S].base,
+ acpi_dsdt_add_plic_aplic(scope, socket_count, s->num_sources,
+ memmap[VIRT_APLIC_S].base,
memmap[VIRT_APLIC_S].size, "RSCV0002");
}
@@ -492,15 +503,15 @@ static void build_dsdt(GArray *table_data,
} else if (socket_count == 2) {
virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
memmap[VIRT_VIRTIO].size,
- VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0,
+ VIRTIO_IRQ + s->num_sources, 0,
VIRTIO_COUNT);
- acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources);
} else {
virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
memmap[VIRT_VIRTIO].size,
- VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0,
+ VIRTIO_IRQ + s->num_sources, 0,
VIRTIO_COUNT);
- acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES * 2);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources * 2);
}
aml_append(dsdt, scope);
@@ -579,7 +590,7 @@ static void build_madt(GArray *table_data,
for (socket = 0; socket < riscv_socket_count(ms); socket++) {
aplic_addr = s->memmap[VIRT_APLIC_S].base +
s->memmap[VIRT_APLIC_S].size * socket;
- gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
+ gsi_base = s->num_sources * socket;
build_append_int_noprefix(table_data, 0x1A, 1); /* Type */
build_append_int_noprefix(table_data, 36, 1); /* Length */
build_append_int_noprefix(table_data, 1, 1); /* Version */
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 33775a61fd..b68067cfdd 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -61,6 +61,8 @@
#include "hw/virtio/virtio-iommu.h"
#include "hw/uefi/var-service-api.h"
+#include "aia.h"
+
/* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */
static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type)
{
@@ -326,39 +328,25 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
int cpu;
g_autofree char *plic_name = NULL;
g_autofree uint32_t *plic_cells;
- unsigned long plic_addr;
MachineState *ms = MACHINE(s);
- static const char * const plic_compat[2] = {
- "sifive,plic-1.0.0", "riscv,plic0"
- };
+ unsigned long plic_addr = s->memmap[VIRT_PLIC].base +
+ (s->memmap[VIRT_PLIC].size * socket);
+ bool numa_enabled = riscv_numa_enabled(MACHINE(s));
+ uint32_t cells_length;
- plic_phandles[socket] = (*phandle)++;
- plic_addr = s->memmap[VIRT_PLIC].base +
- (s->memmap[VIRT_PLIC].size * socket);
- plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr);
- qemu_fdt_add_subnode(ms->fdt, plic_name);
- qemu_fdt_setprop_cell(ms->fdt, plic_name,
- "#interrupt-cells", FDT_PLIC_INT_CELLS);
- qemu_fdt_setprop_cell(ms->fdt, plic_name,
- "#address-cells", FDT_PLIC_ADDR_CELLS);
- qemu_fdt_setprop_string_array(ms->fdt, plic_name, "compatible",
- (char **)&plic_compat,
- ARRAY_SIZE(plic_compat));
- qemu_fdt_setprop(ms->fdt, plic_name, "interrupt-controller", NULL, 0);
+ plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
if (kvm_enabled()) {
- plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
+ cells_length = s->soc[socket].num_harts * 2;
+ plic_cells = g_new0(uint32_t, cells_length);
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
plic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT);
}
-
- qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended",
- plic_cells,
- s->soc[socket].num_harts * sizeof(uint32_t) * 2);
} else {
- plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
+ cells_length = s->soc[socket].num_harts * 4;
+ plic_cells = g_new0(uint32_t, cells_length);
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
@@ -366,19 +354,16 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
}
-
- qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended",
- plic_cells,
- s->soc[socket].num_harts * sizeof(uint32_t) * 4);
}
- qemu_fdt_setprop_sized_cells(ms->fdt, plic_name, "reg",
- 2, plic_addr, 2, s->memmap[VIRT_PLIC].size);
- qemu_fdt_setprop_cell(ms->fdt, plic_name, "riscv,ndev",
- VIRT_IRQCHIP_NUM_SOURCES - 1);
- riscv_socket_fdt_write_id(ms, plic_name, socket);
- qemu_fdt_setprop_cell(ms->fdt, plic_name, "phandle",
- plic_phandles[socket]);
+ plic_phandles[socket] = (*phandle)++;
+
+ create_fdt_plic(ms->fdt, plic_addr, s->memmap[VIRT_PLIC].size,
+ plic_phandles[socket], FDT_PLIC_INT_CELLS,
+ FDT_PLIC_ADDR_CELLS, plic_cells,
+ cells_length * sizeof(uint32_t),
+ VIRT_IRQCHIP_NUM_SOURCES - 1,
+ numa_enabled, socket);
if (!socket) {
platform_bus_add_all_fdt_nodes(ms->fdt, plic_name,
@@ -388,17 +373,6 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
}
}
-uint32_t imsic_num_bits(uint32_t count)
-{
- uint32_t ret = 0;
-
- while (BIT(ret) < count) {
- ret++;
- }
-
- return ret;
-}
-
static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr,
uint32_t *intc_phandles, uint32_t msi_phandle,
bool m_mode, uint32_t imsic_guest_bits)
@@ -1163,68 +1137,6 @@ static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket,
memmap[VIRT_PLIC].size);
}
-static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
- const MemMapEntry *memmap, int socket,
- int base_hartid, int hart_count)
-{
- int i;
- hwaddr addr = 0;
- uint32_t guest_bits;
- DeviceState *aplic_s = NULL;
- DeviceState *aplic_m = NULL;
- bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
-
- if (msimode) {
- if (!kvm_enabled()) {
- /* Per-socket M-level IMSICs */
- addr = memmap[VIRT_IMSIC_M].base +
- socket * VIRT_IMSIC_GROUP_MAX_SIZE;
- for (i = 0; i < hart_count; i++) {
- riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0),
- base_hartid + i, true, 1,
- VIRT_IRQCHIP_NUM_MSIS);
- }
- }
-
- /* Per-socket S-level IMSICs */
- guest_bits = imsic_num_bits(aia_guests + 1);
- addr = memmap[VIRT_IMSIC_S].base + socket * VIRT_IMSIC_GROUP_MAX_SIZE;
- for (i = 0; i < hart_count; i++) {
- riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits),
- base_hartid + i, false, 1 + aia_guests,
- VIRT_IRQCHIP_NUM_MSIS);
- }
- }
-
- if (!kvm_enabled()) {
- /* Per-socket M-level APLIC */
- aplic_m = riscv_aplic_create(memmap[VIRT_APLIC_M].base +
- socket * memmap[VIRT_APLIC_M].size,
- memmap[VIRT_APLIC_M].size,
- (msimode) ? 0 : base_hartid,
- (msimode) ? 0 : hart_count,
- VIRT_IRQCHIP_NUM_SOURCES,
- VIRT_IRQCHIP_NUM_PRIO_BITS,
- msimode, true, NULL);
- }
-
- /* Per-socket S-level APLIC */
- aplic_s = riscv_aplic_create(memmap[VIRT_APLIC_S].base +
- socket * memmap[VIRT_APLIC_S].size,
- memmap[VIRT_APLIC_S].size,
- (msimode) ? 0 : base_hartid,
- (msimode) ? 0 : hart_count,
- VIRT_IRQCHIP_NUM_SOURCES,
- VIRT_IRQCHIP_NUM_PRIO_BITS,
- msimode, false, aplic_m);
-
- if (kvm_enabled() && msimode) {
- riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s), addr);
- }
-
- return kvm_enabled() ? aplic_s : aplic_m;
-}
-
static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip)
{
DeviceState *dev;
@@ -1327,7 +1239,10 @@ static void virt_machine_done(Notifier *notifier, void *data)
}
}
- firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
+ riscv_boot_info_init(&boot_info, &s->soc[0]);
+
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
&start_addr, NULL);
pflash_blk0 = pflash_cfi01_get_blk(s->flash[0]);
@@ -1350,8 +1265,6 @@ static void virt_machine_done(Notifier *notifier, void *data)
}
}
- riscv_boot_info_init(&boot_info, &s->soc[0]);
-
if (machine->kernel_filename && !kernel_entry) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
@@ -1486,9 +1399,17 @@ static void virt_machine_init(MachineState *machine)
s->irqchip[i] = virt_create_plic(s->memmap, i,
base_hartid, hart_count);
} else {
- s->irqchip[i] = virt_create_aia(s->aia_type, s->aia_guests,
- s->memmap, i, base_hartid,
- hart_count);
+ s->irqchip[i] = riscv_create_aia(s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC,
+ s->aia_guests,
+ IMSIC_HART_SIZE(0),
+ s->num_sources,
+ &s->memmap[VIRT_APLIC_M],
+ &s->memmap[VIRT_APLIC_S],
+ &s->memmap[VIRT_IMSIC_M],
+ &s->memmap[VIRT_IMSIC_S],
+ i, base_hartid, hart_count,
+ VIRT_IRQCHIP_NUM_MSIS,
+ VIRT_IRQCHIP_NUM_PRIO_BITS);
}
/* Try to use different IRQCHIP instance based device type */
@@ -1638,6 +1559,7 @@ static void virt_machine_instance_init(Object *obj)
s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
s->acpi = ON_OFF_AUTO_AUTO;
s->iommu_sys = ON_OFF_AUTO_AUTO;
+ s->num_sources = VIRT_IRQCHIP_NUM_SOURCES;
}
static char *virt_get_aia_guests(Object *obj, Error **errp)
diff --git a/hw/riscv/xiangshan_kmh.c b/hw/riscv/xiangshan_kmh.c
index 76417ba7ab..384624d69a 100644
--- a/hw/riscv/xiangshan_kmh.c
+++ b/hw/riscv/xiangshan_kmh.c
@@ -167,6 +167,7 @@ static void xiangshan_kmh_machine_init(MachineState *machine)
const MemMapEntry *memmap = xiangshan_kmh_memmap;
MemoryRegion *system_memory = get_system_memory();
hwaddr start_addr = memmap[XIANGSHAN_KMH_DRAM].base;
+ RISCVBootInfo boot_info;
/* Initialize SoC */
object_initialize_child(OBJECT(machine), "soc", &s->soc,
@@ -178,13 +179,16 @@ static void xiangshan_kmh_machine_init(MachineState *machine)
memmap[XIANGSHAN_KMH_DRAM].base,
machine->ram);
+ riscv_boot_info_init(&boot_info, &s->soc.cpus);
+
/* ROM reset vector */
riscv_setup_rom_reset_vec(machine, &s->soc.cpus,
start_addr,
memmap[XIANGSHAN_KMH_ROM].base,
memmap[XIANGSHAN_KMH_ROM].size, 0, 0);
if (machine->firmware) {
- riscv_load_firmware(machine->firmware, &start_addr, NULL);
+ riscv_load_firmware(machine, &boot_info, machine->firmware,
+ &start_addr, NULL);
}
/* Note: dtb has been integrated into firmware(OpenSBI) when compiling */
diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
new file mode 100644
index 0000000000..4d5ff5d973
--- /dev/null
+++ b/include/hw/i2c/designware_i2c.h
@@ -0,0 +1,56 @@
+/*
+ * DesignWare I2C Module.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef DESIGNWARE_I2C_H
+#define DESIGNWARE_I2C_H
+
+#include "qemu/fifo8.h"
+#include "hw/i2c/i2c.h"
+#include "hw/core/irq.h"
+#include "hw/core/register.h"
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define DESIGNWARE_I2C_R_MAX (0x100 / 4)
+
+#define DESIGNWARE_I2C_RX_FIFO_SIZE 16
+#define DESIGNWARE_I2C_TX_FIFO_SIZE 16
+
+typedef enum DesignWareI2CStatus {
+ DW_I2C_STATUS_IDLE,
+ DW_I2C_STATUS_SENDING_ADDRESS,
+ DW_I2C_STATUS_SENDING,
+ DW_I2C_STATUS_RECEIVING,
+} DesignWareI2CStatus;
+
+#define TYPE_DESIGNWARE_I2C "designware-i2c"
+OBJECT_DECLARE_SIMPLE_TYPE(DesignWareI2CState, DESIGNWARE_I2C)
+
+/*
+ * struct DesignWareI2CState - DesignWare I2C device state.
+ * @bus: The underlying I2C Bus
+ * @irq: Interrupt line fired on transaction events.
+ * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
+ */
+struct DesignWareI2CState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+
+ I2CBus *bus;
+ qemu_irq irq;
+
+ uint32_t regs[DESIGNWARE_I2C_R_MAX];
+ RegisterInfo regs_info[DESIGNWARE_I2C_R_MAX];
+
+ /* fifo8_num_used(rx_fifo) should always equal DW_IC_RXFLR */
+ Fifo8 rx_fifo;
+
+ DesignWareI2CStatus status;
+};
+
+#endif /* DESIGNWARE_I2C_H */
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index f00b3ca122..4e7bd9a225 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -28,6 +28,10 @@
#define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin"
typedef struct RISCVBootInfo {
+ /* First contiguous RAM region. If size is zero then assume entire RAM */
+ hwaddr ram_low_start;
+ hwaddr ram_low_size;
+
ssize_t kernel_size;
hwaddr image_low_addr;
hwaddr image_high_addr;
@@ -43,16 +47,22 @@ bool riscv_is_32bit(RISCVHartArrayState *harts);
char *riscv_plic_hart_config_string(int hart_count);
void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts);
+void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
+ RISCVHartArrayState *harts,
+ hwaddr low_start, hwaddr low_size);
vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
hwaddr firmware_end_addr);
hwaddr riscv_find_and_load_firmware(MachineState *machine,
+ RISCVBootInfo *info,
const char *default_machine_firmware,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb);
const char *riscv_default_firmware_name(RISCVHartArrayState *harts);
char *riscv_find_firmware(const char *firmware_filename,
const char *default_machine_firmware);
-hwaddr riscv_load_firmware(const char *firmware_filename,
+hwaddr riscv_load_firmware(MachineState *machine,
+ const RISCVBootInfo *info,
+ const char *firmware_filename,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb);
void riscv_load_kernel(MachineState *machine,
diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h
index 2d6b9a5d03..017278b611 100644
--- a/include/hw/riscv/fdt-common.h
+++ b/include/hw/riscv/fdt-common.h
@@ -30,4 +30,9 @@ void create_fdt_socket_cpu_sifive(void *fdt, char *clust_name,
int cpu_id, int socket_id,
int socket_hartid_base, uint32_t *phandle,
uint32_t *intc_phandles);
+void create_fdt_plic(void *fdt, hwaddr addr, uint64_t size,
+ uint32_t plic_phandle, uint32_t int_cells,
+ uint32_t addr_cells, uint32_t *plic_cells,
+ uint32_t cells_size, uint32_t ndev_sources,
+ bool numa_enabled, int socket);
#endif
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
index e4c9860d50..aed966a62d 100644
--- a/include/hw/riscv/sifive_u.h
+++ b/include/hw/riscv/sifive_u.h
@@ -156,6 +156,8 @@ enum {
#define SIFIVE_U_MANAGEMENT_CPU_COUNT 1
#define SIFIVE_U_COMPUTE_CPU_COUNT 4
+#define SIFIVE_U_PLIC_ADDR_CELLS 0
+#define SIFIVE_U_PLIC_INT_CELLS 1
#define SIFIVE_U_PLIC_NUM_SOURCES 54
#define SIFIVE_U_PLIC_NUM_PRIORITIES 7
#define SIFIVE_U_PLIC_PRIORITY_BASE 0x00
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
new file mode 100644
index 0000000000..7f7d4a5a59
--- /dev/null
+++ b/include/hw/riscv/tt_atlantis.h
@@ -0,0 +1,64 @@
+/*
+ * Tenstorrent Atlantis RISC-V System on Chip
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
+ */
+
+#ifndef HW_RISCV_TT_ATLANTIS_H
+#define HW_RISCV_TT_ATLANTIS_H
+
+#include "hw/core/boards.h"
+#include "hw/core/sysbus.h"
+#include "hw/i2c/designware_i2c.h"
+#include "hw/intc/riscv_imsic.h"
+#include "hw/riscv/riscv_hart.h"
+
+#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
+OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
+
+#define TT_ATL_NUM_I2C 5
+
+struct TTAtlantisState {
+ /*< private >*/
+ MachineState parent;
+
+ /*< public >*/
+ Notifier machine_done;
+ const MemMapEntry *memmap;
+
+ RISCVHartArrayState soc;
+ DeviceState *irqchip;
+ DesignWareI2CState i2c[TT_ATL_NUM_I2C];
+
+ int fdt_size;
+};
+
+enum {
+ TT_ATL_I2C0_IRQ = 33,
+ TT_ATL_I2C1_IRQ = 34,
+ TT_ATL_I2C2_IRQ = 35,
+ TT_ATL_I2C3_IRQ = 36,
+ TT_ATL_I2C4_IRQ = 37,
+ TT_ATL_UART1_IRQ = 39,
+};
+
+enum {
+ TT_ATL_ACLINT,
+ TT_ATL_BOOTROM,
+ TT_ATL_DDR_LO,
+ TT_ATL_DDR_HI,
+ TT_ATL_I2C0,
+ TT_ATL_I2C1,
+ TT_ATL_I2C2,
+ TT_ATL_I2C3,
+ TT_ATL_I2C4,
+ TT_ATL_MAPLIC,
+ TT_ATL_MIMSIC,
+ TT_ATL_SAPLIC,
+ TT_ATL_SIMSIC,
+ TT_ATL_UART1,
+};
+
+#endif
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index 18a2a323a3..36a2def410 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -64,6 +64,7 @@ struct RISCVVirtState {
struct GPEXHost *gpex_host;
OnOffAuto iommu_sys;
uint16_t pci_iommu_bdf;
+ uint16_t num_sources;
};
enum {
@@ -135,7 +136,6 @@ enum {
bool virt_is_acpi_enabled(RISCVVirtState *s);
bool virt_is_iommu_sys_enabled(RISCVVirtState *s);
void virt_acpi_setup(RISCVVirtState *vms);
-uint32_t imsic_num_bits(uint32_t count);
/*
* The virt machine physical address space used by some of the devices
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index fa497e5e8a..4d2eb281bf 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -47,6 +47,13 @@
static const char riscv_single_letter_exts[] = "IEMAFDQCBPVH";
const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV,
RVC, RVS, RVU, RVH, RVG, RVB, 0};
+#define RISCV_CPU_MVENDORID 0
+#define RISCV_CPU_MIMPID 0
+/*
+ * marchid allocated for qemu:
+ * https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md
+ */
+#define RISCV_CPU_MARCHID 42
/*
* From vector_helper.c
@@ -418,7 +425,7 @@ int riscv_cpu_max_xlen(RISCVCPUClass *mcc)
#ifndef CONFIG_USER_ONLY
static uint8_t satp_mode_from_str(const char *satp_mode_str)
{
- if (!strncmp(satp_mode_str, "mbare", 5)) {
+ if (!strncmp(satp_mode_str, "svbare", 6)) {
return VM_1_10_MBARE;
}
@@ -1009,6 +1016,19 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
mcc->parent_realize(dev, errp);
}
+static void riscv_cpu_unrealize(DeviceState *dev)
+{
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+#ifndef CONFIG_USER_ONLY
+ RISCVCPU *cpu = RISCV_CPU(dev);
+
+ if (cpu->cfg.debug) {
+ riscv_trigger_unrealize(&cpu->env);
+ }
+#endif
+ mcc->parent_unrealize(dev);
+}
+
bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu)
{
if (tcg_enabled()) {
@@ -1050,6 +1070,9 @@ void riscv_add_satp_mode_properties(Object *obj)
{
RISCVCPU *cpu = RISCV_CPU(obj);
+ object_property_add(obj, "svbare", "bool", cpu_riscv_get_satp,
+ cpu_riscv_set_satp, NULL, &cpu->satp_modes);
+
if (cpu->env.misa_mxl == MXL_RV32) {
object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp,
cpu_riscv_set_satp, NULL, &cpu->satp_modes);
@@ -1151,6 +1174,11 @@ static void riscv_cpu_init(Object *obj)
IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi,
"riscv.cpu.rnmi", RNMI_MAX);
+
+ if (mcc->def->num_triggers) {
+ env->num_triggers = mcc->def->num_triggers;
+ }
+
#endif /* CONFIG_USER_ONLY */
cpu->user_options = g_hash_table_new(g_str_hash, g_str_equal);
@@ -1183,6 +1211,10 @@ static void riscv_cpu_init(Object *obj)
mcc->def->profile->enabled = true;
}
+ cpu->cfg.mvendorid = RISCV_CPU_MVENDORID;
+ cpu->cfg.marchid = RISCV_CPU_MARCHID;
+ cpu->cfg.mimpid = RISCV_CPU_MIMPID;
+
env->misa_ext_mask = env->misa_ext = mcc->def->misa_ext;
riscv_cpu_cfg_merge(&cpu->cfg, &mcc->def->cfg);
@@ -2603,6 +2635,8 @@ static const Property riscv_cpu_properties[] = {
DEFAULT_RNMI_IRQVEC),
DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec,
DEFAULT_RNMI_EXCPVEC),
+ DEFINE_PROP_UINT32("num-triggers", RISCVCPU, env.num_triggers,
+ RV_DEFAULT_NUM_TRIGGERS),
#endif
DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
@@ -2664,6 +2698,8 @@ static void riscv_cpu_common_class_init(ObjectClass *c, const void *data)
device_class_set_parent_realize(dc, riscv_cpu_realize,
&mcc->parent_realize);
+ device_class_set_parent_unrealize(dc, riscv_cpu_unrealize,
+ &mcc->parent_unrealize);
resettable_class_set_parent_phases(rc, NULL, riscv_cpu_reset_hold, NULL,
&mcc->parent_phases);
@@ -2746,6 +2782,10 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data)
!valid_vm_1_10_32[mcc->def->cfg.max_satp_mode]) {
mcc->def->cfg.max_satp_mode = VM_1_10_SV32;
}
+
+ if (def->num_triggers) {
+ mcc->def->num_triggers = def->num_triggers;
+ }
#endif
}
if (def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) {
@@ -3211,6 +3251,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
.cfg.ext_zba = true,
.cfg.ext_zbb = true,
.cfg.ext_zbs = true,
+ .cfg.ext_zkr = true,
.cfg.ext_zkt = true,
.cfg.ext_zvbb = true,
.cfg.ext_zvbc = true,
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 7582874c35..bdd28d329b 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -189,7 +189,22 @@ extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[];
#define RV_VLEN_MAX 1024
#define RV_MAX_MHPMEVENTS 32
#define RV_MAX_MHPMCOUNTERS 32
-#define RV_MAX_TRIGGERS 2
+
+/*
+ * The Debug 1.0 spec allows a humongous amount of triggers. Section
+ * "Enumeration" says: "The above algorithm reads back tselect so that
+ * implementations which have 2^n triggers only need to implement n
+ * bits of tselect.". tselect can have up to XLEN bits, so the max
+ * theoretical RV_MAX_TRIGGERS value is 2^XLEN.
+ *
+ * Allowing 2^XLEN triggers per hart is silly so we'll set a max to a
+ * modest 1024 triggers, which is way more than what we see current
+ * hardware use (most chips uses 2-4 triggers per hart, RISC-V Server
+ * Ref requires at least 11). With a 1024 max per hart we'll be set
+ * for a long time ... hopefully.
+ */
+#define RV_MAX_TRIGGERS 1024
+#define RV_DEFAULT_NUM_TRIGGERS 2
FIELD(VTYPE, VLMUL, 0, 3)
FIELD(VTYPE, VSEW, 3, 3)
@@ -467,12 +482,18 @@ struct CPUArchState {
/* trigger module */
uint16_t mcontext;
uint8_t trigger_cur;
- uint64_t tdata1[RV_MAX_TRIGGERS];
- uint64_t tdata2[RV_MAX_TRIGGERS];
- uint64_t tdata3[RV_MAX_TRIGGERS];
- struct CPUBreakpoint *cpu_breakpoint[RV_MAX_TRIGGERS];
- struct CPUWatchpoint *cpu_watchpoint[RV_MAX_TRIGGERS];
- QEMUTimer *itrigger_timer[RV_MAX_TRIGGERS];
+ /*
+ * num_triggers is the length of tdata1, tdata2, tdata3,
+ * cpu_breakpoint, cpu_watchpoint and itrigger_timer
+ * arrays.
+ */
+ uint32_t num_triggers;
+ uint64_t *tdata1;
+ uint64_t *tdata2;
+ uint64_t *tdata3;
+ struct CPUBreakpoint **cpu_breakpoint;
+ struct CPUWatchpoint **cpu_watchpoint;
+ QEMUTimer **itrigger_timer;
int64_t last_icount;
bool itrigger_enabled;
@@ -571,11 +592,14 @@ typedef struct RISCVCPUDef {
RISCVCPUConfig cfg;
bool bare;
const RISCVCSR *custom_csrs;
+ /* This is just a setter for env->num_triggers. */
+ uint32_t num_triggers;
} RISCVCPUDef;
/**
* RISCVCPUClass:
* @parent_realize: The parent class' realize handler.
+ * @parent_unrealize: The parent class' unrealize handler.
* @parent_phases: The parent class' reset phase handlers.
*
* A RISCV CPU model.
@@ -584,6 +608,7 @@ struct RISCVCPUClass {
CPUClass parent_class;
DeviceRealize parent_realize;
+ DeviceUnrealize parent_unrealize;
ResettablePhases parent_phases;
RISCVCPUDef *def;
};
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 59edcdd370..2db07f5dfb 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -1655,10 +1655,18 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
/* Page table updates need to be atomic with MTTCG enabled */
if (updated_pte != pte && !is_debug) {
+ int pmp_prot, pmp_ret;
+
if (!adue) {
return TRANSLATE_FAIL;
}
+ pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr,
+ sxlen_bytes, MMU_DATA_STORE, PRV_S);
+ if (pmp_ret != TRANSLATE_SUCCESS) {
+ return TRANSLATE_PMP_FAIL;
+ }
+
/*
* - if accessed or dirty bits need updating, and the PTE is
* in RAM, then we do so atomically with a compare and swap.
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index dd9726fcf4..7168a4dc12 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -949,7 +949,16 @@ static RISCVException read_vtype(CPURISCVState *env, int csrno,
target_ulong *val)
{
uint64_t vill;
- switch (env->xl) {
+ int xl = env->xl;
+ /*
+ * TCG plugins can read registers before env->xl is initialized.
+ * Fall back to the CPU's maximum XLEN in that early-init case.
+ */
+ if (xl == 0) {
+ xl = riscv_cpu_mxl(env);
+ }
+
+ switch (xl) {
case MXL_RV32:
vill = (uint32_t)env->vill << 31;
break;
@@ -2012,6 +2021,20 @@ static target_ulong legalize_mpp(CPURISCVState *env, target_ulong old_mpp,
return val;
}
+static uint64_t riscv_write_uxl(CPURISCVState *env, uint64_t val,
+ uint64_t field)
+{
+ RISCVMXL xl = riscv_cpu_mxl(env);
+ uint64_t uxl = get_field(val, field);
+
+ if (uxl == MXL_RV128) {
+ uxl = xl == MXL_RV128 ? MXL_RV64 : xl;
+ val = set_field(val, field, uxl);
+ }
+
+ return val;
+}
+
static RISCVException write_mstatus(CPURISCVState *env, int csrno,
target_ulong val, uintptr_t ra)
{
@@ -2058,17 +2081,8 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
if (xl != MXL_RV32 || env->debugger) {
if ((val & MSTATUS64_UXL) != 0) {
- uint64_t uxl = val & MSTATUS64_UXL >> 32;
mask |= MSTATUS64_UXL;
-
- /*
- * uxl = 3 is reserved so write the current xl instead.
- * In case xl = MXL_RV128 (3) write MXL_RV64.
- */
- if (uxl == 3) {
- uxl = xl == MXL_RV128 ? MXL_RV64 : xl;
- val = deposit64(val, 32, 2, uxl);
- }
+ val = riscv_write_uxl(env, val, MSTATUS64_UXL);
}
}
@@ -2733,7 +2747,9 @@ static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno,
AIA_MAKE_IREG(isel, priv, virt, vgein,
riscv_cpu_mxl_bits(env)),
&wide_val, new_val, wr_mask);
- *val = wide_val;
+ if (val) {
+ *val = wide_val;
+ }
}
} else {
isel_reserved = true;
@@ -3009,7 +3025,9 @@ static RISCVException rmw_xtopei(CPURISCVState *env, int csrno,
AIA_MAKE_IREG(ISELECT_IMSIC_TOPEI, priv, virt, vgein,
riscv_cpu_mxl_bits(env)),
&wide_val, new_val, wr_mask);
- *val = wide_val;
+ if (val) {
+ *val = wide_val;
+ }
done:
if (ret) {
@@ -5226,6 +5244,8 @@ static RISCVException write_vsstatus(CPURISCVState *env, int csrno,
uint64_t mask = (target_ulong)-1;
if ((val & VSSTATUS64_UXL) == 0) {
mask &= ~VSSTATUS64_UXL;
+ } else {
+ val = riscv_write_uxl(env, val, VSSTATUS64_UXL);
}
if ((env->henvcfg & HENVCFG_DTE)) {
if ((val & SSTATUS_SDT) != 0) {
@@ -5437,7 +5457,7 @@ static RISCVException read_tdata(CPURISCVState *env, int csrno,
target_ulong *val)
{
/* return 0 in tdata1 to end the trigger enumeration */
- if (env->trigger_cur >= RV_MAX_TRIGGERS && csrno == CSR_TDATA1) {
+ if (env->trigger_cur >= env->num_triggers && csrno == CSR_TDATA1) {
*val = 0;
return RISCV_EXCP_NONE;
}
diff --git a/target/riscv/debug.c b/target/riscv/debug.c
index 30d39ee5cd..ba5bc6ae13 100644
--- a/target/riscv/debug.c
+++ b/target/riscv/debug.c
@@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "cpu.h"
#include "target/riscv/debug.h"
#include "trace.h"
@@ -172,7 +173,7 @@ target_ulong tselect_csr_read(CPURISCVState *env)
void tselect_csr_write(CPURISCVState *env, target_ulong val)
{
- if (val < RV_MAX_TRIGGERS) {
+ if (val < env->num_triggers) {
env->trigger_cur = val;
}
}
@@ -701,7 +702,7 @@ static bool check_itrigger_priv(CPURISCVState *env, int index)
bool riscv_itrigger_enabled(CPURISCVState *env)
{
int count;
- for (int i = 0; i < RV_MAX_TRIGGERS; i++) {
+ for (int i = 0; i < env->num_triggers; i++) {
if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) {
continue;
}
@@ -721,7 +722,7 @@ bool riscv_itrigger_enabled(CPURISCVState *env)
void helper_itrigger_match(CPURISCVState *env)
{
int count;
- for (int i = 0; i < RV_MAX_TRIGGERS; i++) {
+ for (int i = 0; i < env->num_triggers; i++) {
if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) {
continue;
}
@@ -750,7 +751,7 @@ static void riscv_itrigger_update_count(CPURISCVState *env)
int64_t last_icount = env->last_icount, current_icount;
current_icount = env->last_icount = icount_get_raw();
- for (int i = 0; i < RV_MAX_TRIGGERS; i++) {
+ for (int i = 0; i < env->num_triggers; i++) {
if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) {
continue;
}
@@ -950,7 +951,7 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs)
int i;
QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
- for (i = 0; i < RV_MAX_TRIGGERS; i++) {
+ for (i = 0; i < env->num_triggers; i++) {
trigger_type = get_trigger_type(env, i);
if (!trigger_common_match(env, trigger_type, i)) {
@@ -996,7 +997,7 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
int flags;
int i;
- for (i = 0; i < RV_MAX_TRIGGERS; i++) {
+ for (i = 0; i < env->num_triggers; i++) {
trigger_type = get_trigger_type(env, i);
if (!trigger_common_match(env, trigger_type, i)) {
@@ -1049,19 +1050,48 @@ void riscv_trigger_realize(CPURISCVState *env)
{
int i;
- for (i = 0; i < RV_MAX_TRIGGERS; i++) {
+ if (env->num_triggers > RV_MAX_TRIGGERS) {
+ error_report(
+ "Invalid configuration: 'num-triggers' must be less than %u",
+ RV_MAX_TRIGGERS);
+ exit(1);
+ }
+
+ env->tdata1 = g_new0(uint64_t, env->num_triggers);
+ env->tdata2 = g_new0(uint64_t, env->num_triggers);
+ env->tdata3 = g_new0(uint64_t, env->num_triggers);
+ env->cpu_breakpoint = g_new0(struct CPUBreakpoint *, env->num_triggers);
+ env->cpu_watchpoint = g_new0(struct CPUWatchpoint *, env->num_triggers);
+ env->itrigger_timer = g_new0(QEMUTimer *, env->num_triggers);
+
+ for (i = 0; i < env->num_triggers; i++) {
env->itrigger_timer[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL,
riscv_itrigger_timer_cb, env);
}
}
+void riscv_trigger_unrealize(CPURISCVState *env)
+{
+ g_free(env->tdata1);
+ g_free(env->tdata2);
+ g_free(env->tdata3);
+
+ g_free(env->cpu_breakpoint);
+ g_free(env->cpu_watchpoint);
+
+ for (int i = 0; i < env->num_triggers; i++) {
+ timer_del(env->itrigger_timer[i]);
+ }
+ g_free(env->itrigger_timer);
+}
+
void riscv_trigger_reset_hold(CPURISCVState *env)
{
target_ulong tdata1 = build_tdata1(env, TRIGGER_TYPE_AD_MATCH, 0, 0);
int i;
/* init to type 2 triggers */
- for (i = 0; i < RV_MAX_TRIGGERS; i++) {
+ for (i = 0; i < env->num_triggers; i++) {
/*
* type = TRIGGER_TYPE_AD_MATCH
* dmode = 0 (both debug and M-mode can write tdata)
diff --git a/target/riscv/debug.h b/target/riscv/debug.h
index 55a3ac72e6..a25d099b37 100644
--- a/target/riscv/debug.h
+++ b/target/riscv/debug.h
@@ -148,6 +148,7 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs);
bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp);
void riscv_trigger_realize(CPURISCVState *env);
+void riscv_trigger_unrealize(CPURISCVState *env);
void riscv_trigger_reset_hold(CPURISCVState *env);
bool riscv_itrigger_enabled(CPURISCVState *env);
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index f0de5c3071..39d48a9db4 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -1395,6 +1395,17 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
return ret;
}
+ /*
+ * For RUNTIME_STATE, KVM already has the correct FP and Vector state
+ * from the preceding KVM_RUN exit. QEMU never modifies these registers
+ * during exit handling, so re-syncing is unnecessary. This saves ~68
+ * KVM_SET_ONE_REG ioctls per vCPU exit. See also s390x which uses
+ * the same pattern.
+ */
+ if (KVM_PUT_RUNTIME_STATE == level) {
+ return ret;
+ }
+
ret = kvm_riscv_put_regs_fp(cs);
if (ret) {
return ret;
@@ -1407,11 +1418,9 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
if (KVM_PUT_RESET_STATE == level) {
RISCVCPU *cpu = RISCV_CPU(cs);
- if (cs->cpu_index == 0) {
- ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE);
- } else {
- ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED);
- }
+ int state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE
+ : KVM_MP_STATE_STOPPED;
+ ret = kvm_riscv_sync_mpstate_to_kvm(cpu, state);
if (ret) {
return ret;
}
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index 6e70b145a5..ba96ceceef 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -240,16 +240,22 @@ static int debug_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_debug = {
.name = "cpu/debug",
- .version_id = 3,
- .minimum_version_id = 3,
+ .version_id = 4,
+ .minimum_version_id = 4,
.needed = debug_needed,
.post_load = debug_post_load,
.fields = (const VMStateField[]) {
VMSTATE_UINT16(env.mcontext, RISCVCPU),
VMSTATE_UINT8(env.trigger_cur, RISCVCPU),
- VMSTATE_UINT64_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS),
- VMSTATE_UINT64_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS),
- VMSTATE_UINT64_ARRAY(env.tdata3, RISCVCPU, RV_MAX_TRIGGERS),
+ VMSTATE_VARRAY_UINT32(env.tdata1, RISCVCPU,
+ env.num_triggers, 0,
+ vmstate_info_uint64, uint64_t),
+ VMSTATE_VARRAY_UINT32(env.tdata2, RISCVCPU,
+ env.num_triggers, 0,
+ vmstate_info_uint64, uint64_t),
+ VMSTATE_VARRAY_UINT32(env.tdata3, RISCVCPU,
+ env.num_triggers, 0,
+ vmstate_info_uint64, uint64_t),
VMSTATE_END_OF_LIST()
}
};
diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build
index 5871211e89..c9d4c090fb 100644
--- a/tests/functional/riscv64/meson.build
+++ b/tests/functional/riscv64/meson.build
@@ -14,5 +14,6 @@ tests_riscv64_system_thorough = [
'endianness',
'boston',
'sifive_u',
+ 'tt_atlantis',
'tuxrun',
]
diff --git a/tests/functional/riscv64/test_opensbi.py b/tests/functional/riscv64/test_opensbi.py
index d077e40f42..0f8beb7e7a 100755
--- a/tests/functional/riscv64/test_opensbi.py
+++ b/tests/functional/riscv64/test_opensbi.py
@@ -28,6 +28,10 @@ class RiscvOpenSBI(QemuSystemTest):
self.set_machine('sifive_u')
self.boot_opensbi()
+ def test_riscv_tt_atlantis(self):
+ self.set_machine('tt-atlantis')
+ self.boot_opensbi()
+
def test_riscv_virt(self):
self.set_machine('virt')
self.boot_opensbi()
diff --git a/tests/functional/riscv64/test_tt_atlantis.py b/tests/functional/riscv64/test_tt_atlantis.py
new file mode 100755
index 0000000000..48abd5cd27
--- /dev/null
+++ b/tests/functional/riscv64/test_tt_atlantis.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots a Linux kernel on a Tenstorrent Atlantis machine
+# and checks the console
+#
+# Copyright (c) Linaro Ltd.
+# Copyright 2026 Tenstorrent
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import Asset, LinuxKernelTest
+
+
+class TTAtlantis(LinuxKernelTest):
+
+ ASSET_KERNEL = Asset(
+ 'https://storage.tuxboot.com/kernels/6.11.9/riscv64/Image',
+ '174f8bb87f08961e54fa3fcd954a8e31f4645f6d6af4dd43983d5e9841490fb0')
+ ASSET_ROOTFS = Asset(
+ ('https://github.com/groeck/linux-build-test/raw/'
+ '9819da19e6eef291686fdd7b029ea00e764dc62f/rootfs/riscv64/'
+ 'rootfs.ext2.gz'),
+ 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b')
+
+ def do_test_riscv64_tt_atlantis(self, connect_disk):
+ self.set_machine('tt-atlantis')
+ kernel_path = self.ASSET_KERNEL.fetch()
+ rootfs_path = self.uncompress(self.ASSET_ROOTFS)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon=sbi '
+
+ if connect_disk:
+ kernel_command_line += 'root=/dev/vda panic=-1 noreboot rootwait '
+ self.vm.add_args('-device',
+ 'virtio-blk,drive=drive0,serial=0x1234,bus=pcie.0')
+ self.vm.add_args('-drive',
+ f'file={rootfs_path},if=none,id=drive0,format=raw')
+ pattern = 'Boot successful.'
+ else:
+ kernel_command_line += 'panic=0 noreboot '
+ pattern = 'Cannot open root device'
+
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+
+ self.vm.launch()
+ self.wait_for_console_pattern(pattern)
+
+ def test_riscv64_tt_atlantis(self):
+ # tt-atlantis machine has no PCI host yet, so no disk
+ self.do_test_riscv64_tt_atlantis(False)
+
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
diff --git a/tests/functional/riscv64/test_tuxrun.py b/tests/functional/riscv64/test_tuxrun.py
index 0d8de36204..6203a45060 100755
--- a/tests/functional/riscv64/test_tuxrun.py
+++ b/tests/functional/riscv64/test_tuxrun.py
@@ -17,8 +17,8 @@ from qemu_test.tuxruntest import TuxRunBaselineTest
class TuxRunRiscV64Test(TuxRunBaselineTest):
ASSET_RISCV64_KERNEL = Asset(
- 'https://storage.tuxboot.com/buildroot/20241119/riscv64/Image',
- '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba')
+ 'https://storage.tuxboot.com/kernels/6.11.9/riscv64/Image',
+ '174f8bb87f08961e54fa3fcd954a8e31f4645f6d6af4dd43983d5e9841490fb0')
ASSET_RISCV64_ROOTFS = Asset(
'https://storage.tuxboot.com/buildroot/20241119/riscv64/rootfs.ext4.zst',
'aa4736a9872651dfc0d95e709465eedf1134fd19d42b8cb305bfd776f9801004')
@@ -41,6 +41,13 @@ class TuxRunRiscV64Test(TuxRunBaselineTest):
self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL,
rootfs_asset=self.ASSET_RISCV64_ROOTFS)
+ def test_riscv64_aia(self):
+ self.set_machine('virt')
+ self.vm.set_machine('virt,aia=aplic-imsic')
+ self.cpu='max'
+ self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL,
+ rootfs_asset=self.ASSET_RISCV64_ROOTFS)
+
def test_riscv64_rv32(self):
self.set_machine('virt')
self.cpu='rv32'